Merge branch 'next'

Conflicts:
	include/data.h
	src/config.c
	src/handlers.c
	src/layout.c
This commit is contained in:
Michael Stapelberg
2009-11-09 22:55:24 +01:00
73 changed files with 25616 additions and 526 deletions

92
src/cfgparse.l Normal file
View File

@ -0,0 +1,92 @@
%option nounput
%option noinput
%{
/*
* vim:ts=8:expandtab
*
*/
#include <stdio.h>
#include <string.h>
#include "cfgparse.tab.h"
#include <xcb/xcb.h>
#include "data.h"
#include "config.h"
%}
%Start BIND_COND
%Start BINDSYM_COND
%Start BIND_AWS_COND
%Start BINDSYM_AWS_COND
%Start BIND_A2WS_COND
%Start ASSIGN_COND
%Start COLOR_COND
%Start SCREEN_COND
%Start SCREEN_AWS_COND
%%
<BIND_A2WS_COND>[^\n]+ { BEGIN(INITIAL); yylval.string = strdup(yytext); return STR; }
^[ \t]*#[^\n]* { return TOKCOMMENT; }
<COLOR_COND>[0-9a-fA-F]+ { yylval.string = strdup(yytext); return HEX; }
[0-9]+ { yylval.number = atoi(yytext); return NUMBER; }
mode { return TOKMODE; }
bind { BEGIN(BIND_COND); return TOKBIND; }
bindsym { BEGIN(BINDSYM_COND); return TOKBINDSYM; }
floating_modifier { return TOKFLOATING_MODIFIER; }
workspace { BEGIN(INITIAL); return TOKWORKSPACE; }
screen { BEGIN(SCREEN_COND); return TOKSCREEN; }
terminal { BEGIN(BIND_AWS_COND); return TOKTERMINAL; }
font { BEGIN(BIND_AWS_COND); return TOKFONT; }
assign { BEGIN(ASSIGN_COND); return TOKASSIGN; }
set[^\n]* { return TOKCOMMENT; }
ipc-socket { BEGIN(BIND_AWS_COND); return TOKIPCSOCKET; }
ipc_socket { BEGIN(BIND_AWS_COND); return TOKIPCSOCKET; }
new_container { return TOKNEWCONTAINER; }
new_window { return TOKNEWWINDOW; }
default { yylval.number = MODE_DEFAULT; return TOKCONTAINERMODE; }
stacking { yylval.number = MODE_STACK; return TOKCONTAINERMODE; }
tabbed { yylval.number = MODE_TABBED; return TOKCONTAINERMODE; }
stack-limit { return TOKSTACKLIMIT; }
cols { yylval.number = STACK_LIMIT_COLS; return TOKSTACKLIMIT; }
rows { yylval.number = STACK_LIMIT_ROWS; return TOKSTACKLIMIT; }
exec { BEGIN(BIND_AWS_COND); return TOKEXEC; }
client.focused { BEGIN(COLOR_COND); yylval.color = &config.client.focused; return TOKCOLOR; }
client.focused_inactive { BEGIN(COLOR_COND); yylval.color = &config.client.focused_inactive; return TOKCOLOR; }
client.unfocused { BEGIN(COLOR_COND); yylval.color = &config.client.unfocused; return TOKCOLOR; }
client.urgent { BEGIN(COLOR_COND); yylval.color = &config.client.urgent; return TOKCOLOR; }
bar.focused { BEGIN(COLOR_COND); yylval.color = &config.bar.focused; return TOKCOLOR; }
bar.unfocused { BEGIN(COLOR_COND); yylval.color = &config.bar.unfocused; return TOKCOLOR; }
bar.urgent { BEGIN(COLOR_COND); yylval.color = &config.bar.urgent; return TOKCOLOR; }
Mod1 { yylval.number = BIND_MOD1; return MODIFIER; }
Mod2 { yylval.number = BIND_MOD2; return MODIFIER; }
Mod3 { yylval.number = BIND_MOD3; return MODIFIER; }
Mod4 { yylval.number = BIND_MOD4; return MODIFIER; }
Mod5 { yylval.number = BIND_MOD5; return MODIFIER; }
Mode_switch { yylval.number = BIND_MODE_SWITCH; return MODIFIER; }
control { return TOKCONTROL; }
shift { return TOKSHIFT; }
→ { return TOKARROW; }
\n /* ignore end of line */;
<SCREEN_AWS_COND>x { return (int)yytext[0]; }
<BIND_COND>[ \t]+ { BEGIN(BIND_AWS_COND); return WHITESPACE; }
<BINDSYM_COND>[ \t]+ { BEGIN(BINDSYM_AWS_COND); return WHITESPACE; }
<BIND_AWS_COND>[ \t]+ { BEGIN(BIND_A2WS_COND); return WHITESPACE; }
<BINDSYM_AWS_COND>[ \t]+ { BEGIN(BIND_A2WS_COND); return WHITESPACE; }
<SCREEN_COND>[ \t]+ { BEGIN(SCREEN_AWS_COND); return WHITESPACE; }
<SCREEN_AWS_COND>[ \t]+ { BEGIN(BIND_A2WS_COND); return WHITESPACE; }
[ \t]+ { return WHITESPACE; }
\"[^\"]+\" {
/* if ASSIGN_COND then */
BEGIN(INITIAL);
/* yylval will be the string, but without quotes */
char *copy = strdup(yytext+1);
copy[strlen(copy)-1] = '\0';
yylval.string = copy;
return QUOTEDSTRING;
}
<ASSIGN_COND>[^ \t]+ { BEGIN(INITIAL); yylval.string = strdup(yytext); return STR_NG; }
<BINDSYM_AWS_COND>[a-zA-Z0-9]+ { yylval.string = strdup(yytext); return WORD; }
[a-zA-Z]+ { yylval.string = strdup(yytext); return WORD; }
. { return (int)yytext[0]; }
%%

537
src/cfgparse.y Normal file
View File

@ -0,0 +1,537 @@
%{
/*
* vim:ts=8:expandtab
*
*/
#include <stdio.h>
#include <string.h>
#include <xcb/xcb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include "data.h"
#include "config.h"
#include "i3.h"
#include "util.h"
#include "queue.h"
#include "table.h"
#include "workspace.h"
#include "xcb.h"
typedef struct yy_buffer_state *YY_BUFFER_STATE;
extern int yylex(void);
extern int yyparse(void);
extern FILE *yyin;
YY_BUFFER_STATE yy_scan_string(const char *);
static struct bindings_head *current_bindings;
int yydebug = 1;
void yyerror(const char *str) {
fprintf(stderr,"error: %s\n",str);
}
int yywrap() {
return 1;
}
void parse_file(const char *f) {
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
int fd, ret, read_bytes = 0;
struct stat stbuf;
char *buf;
FILE *fstr;
char buffer[1026], key[512], value[512];
if ((fd = open(f, O_RDONLY)) == -1)
die("Could not open configuration file: %s\n", strerror(errno));
if (fstat(fd, &stbuf) == -1)
die("Could not fstat file: %s\n", strerror(errno));
buf = smalloc(stbuf.st_size * sizeof(char));
while (read_bytes < stbuf.st_size) {
if ((ret = read(fd, buf + read_bytes, (stbuf.st_size - read_bytes))) < 0)
die("Could not read(): %s\n", strerror(errno));
read_bytes += ret;
}
if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
die("Could not lseek: %s\n", strerror(errno));
if ((fstr = fdopen(fd, "r")) == NULL)
die("Could not fdopen: %s\n", strerror(errno));
while (!feof(fstr)) {
if (fgets(buffer, 1024, fstr) == NULL) {
if (feof(fstr))
break;
die("Could not read configuration file\n");
}
/* sscanf implicitly strips whitespace. Also, we skip comments and empty lines. */
if (sscanf(buffer, "%s %[^\n]", key, value) < 1 ||
key[0] == '#' || strlen(key) < 3)
continue;
if (strcasecmp(key, "set") == 0) {
if (value[0] != '$')
die("Malformed variable assignment, name has to start with $\n");
/* get key/value for this variable */
char *v_key = value, *v_value;
if ((v_value = strstr(value, " ")) == NULL)
die("Malformed variable assignment, need a value\n");
*(v_value++) = '\0';
struct Variable *new = scalloc(sizeof(struct Variable));
new->key = sstrdup(v_key);
new->value = sstrdup(v_value);
SLIST_INSERT_HEAD(&variables, new, variables);
LOG("Got new variable %s = %s\n", v_key, v_value);
continue;
}
}
/* For every custom variable, see how often it occurs in the file and
* how much extra bytes it requires when replaced. */
struct Variable *current, *nearest;
int extra_bytes = 0;
SLIST_FOREACH(current, &variables, variables) {
int extra = (strlen(current->value) - strlen(current->key));
char *next;
for (next = buf;
(next = strcasestr(buf + (next - buf), current->key)) != NULL;
next += strlen(current->key))
extra_bytes += extra;
}
/* Then, allocate a new buffer and copy the file over to the new one,
* but replace occurences of our variables */
char *walk = buf, *destwalk;
char *new = smalloc((stbuf.st_size + extra_bytes + 1) * sizeof(char));
destwalk = new;
while (walk < (buf + stbuf.st_size)) {
/* Find the next variable */
SLIST_FOREACH(current, &variables, variables)
current->next_match = strcasestr(walk, current->key);
nearest = NULL;
int distance = stbuf.st_size;
SLIST_FOREACH(current, &variables, variables) {
if (current->next_match == NULL)
continue;
if ((current->next_match - walk) < distance) {
distance = (current->next_match - walk);
nearest = current;
}
}
if (nearest == NULL) {
/* If there are no more variables, we just copy the rest */
strncpy(destwalk, walk, (buf + stbuf.st_size) - walk);
destwalk += (buf + stbuf.st_size) - walk;
*destwalk = '\0';
break;
} else {
/* Copy until the next variable, then copy its value */
strncpy(destwalk, walk, distance);
strncpy(destwalk + distance, nearest->value, strlen(nearest->value));
walk += distance + strlen(nearest->key);
destwalk += distance + strlen(nearest->value);
}
}
yy_scan_string(new);
if (yyparse() != 0) {
fprintf(stderr, "Could not parse configfile\n");
exit(1);
}
free(new);
free(buf);
}
%}
%expect 1
%union {
int number;
char *string;
struct Colortriple *color;
struct Assignment *assignment;
struct Binding *binding;
}
%token <number>NUMBER
%token <string>WORD
%token <string>STR
%token <string>STR_NG
%token <string>HEX
%token TOKBIND
%token TOKTERMINAL
%token TOKCOMMENT
%token TOKFONT
%token TOKBINDSYM
%token MODIFIER
%token TOKCONTROL
%token TOKSHIFT
%token WHITESPACE
%token TOKFLOATING_MODIFIER
%token QUOTEDSTRING
%token TOKWORKSPACE
%token TOKSCREEN
%token TOKASSIGN
%token TOKSET
%token TOKIPCSOCKET
%token TOKEXEC
%token TOKCOLOR
%token TOKARROW
%token TOKMODE
%token TOKNEWCONTAINER
%token TOKNEWWINDOW
%token TOKCONTAINERMODE
%token TOKSTACKLIMIT
%%
lines: /* empty */
| lines WHITESPACE line
| lines line
;
line:
bindline
| mode
| floating_modifier
| new_container
| new_window
| workspace
| assign
| ipcsocket
| exec
| color
| terminal
| font
| comment
;
comment:
TOKCOMMENT
;
command:
STR
;
bindline:
binding
{
TAILQ_INSERT_TAIL(bindings, $<binding>1, bindings);
}
;
binding:
TOKBIND WHITESPACE bind { $<binding>$ = $<binding>3; }
| TOKBINDSYM WHITESPACE bindsym { $<binding>$ = $<binding>3; }
;
bind:
binding_modifiers NUMBER WHITESPACE command
{
printf("\tFound binding mod%d with key %d and command %s\n", $<number>1, $2, $<string>4);
Binding *new = scalloc(sizeof(Binding));
new->keycode = $<number>2;
new->mods = $<number>1;
new->command = sstrdup($<string>4);
$<binding>$ = new;
}
;
bindsym:
binding_modifiers word_or_number WHITESPACE command
{
printf("\tFound symbolic mod%d with key %s and command %s\n", $<number>1, $<string>2, $<string>4);
Binding *new = scalloc(sizeof(Binding));
new->symbol = sstrdup($<string>2);
new->mods = $<number>1;
new->command = sstrdup($<string>4);
$<binding>$ = new;
}
;
word_or_number:
WORD
| NUMBER
{
asprintf(&$<string>$, "%d", $1);
}
;
mode:
TOKMODE WHITESPACE QUOTEDSTRING WHITESPACE '{' modelines '}'
{
if (strcasecmp($<string>3, "default") == 0) {
printf("You cannot use the name \"default\" for your mode\n");
exit(1);
}
printf("\t now in mode %s\n", $<string>3);
printf("\t current bindings = %p\n", current_bindings);
Binding *binding;
TAILQ_FOREACH(binding, current_bindings, bindings) {
printf("got binding on mods %d, keycode %d, symbol %s, command %s\n",
binding->mods, binding->keycode, binding->symbol, binding->command);
}
struct Mode *mode = scalloc(sizeof(struct Mode));
mode->name = strdup($<string>3);
mode->bindings = current_bindings;
current_bindings = NULL;
SLIST_INSERT_HEAD(&modes, mode, modes);
}
;
modelines:
/* empty */
| modelines modeline
;
modeline:
WHITESPACE
| comment
| binding
{
if (current_bindings == NULL) {
current_bindings = scalloc(sizeof(struct bindings_head));
TAILQ_INIT(current_bindings);
}
TAILQ_INSERT_TAIL(current_bindings, $<binding>1, bindings);
}
;
floating_modifier:
TOKFLOATING_MODIFIER WHITESPACE binding_modifiers
{
LOG("floating modifier = %d\n", $<number>3);
config.floating_modifier = $<number>3;
}
;
new_container:
TOKNEWCONTAINER WHITESPACE TOKCONTAINERMODE
{
LOG("new containers will be in mode %d\n", $<number>3);
config.container_mode = $<number>3;
/* We also need to change the layout of the already existing
* workspaces here. Workspaces may exist at this point because
* of the other directives which are modifying workspaces
* (setting the preferred screen or name). While the workspace
* objects are already created, they have never been used.
* Thus, the user very likely awaits the default container mode
* to trigger in this case, regardless of where it is inside
* his configuration file. */
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->table == NULL)
continue;
switch_layout_mode(global_conn,
ws->table[0][0],
config.container_mode);
}
}
| TOKNEWCONTAINER WHITESPACE TOKSTACKLIMIT WHITESPACE TOKSTACKLIMIT WHITESPACE NUMBER
{
LOG("stack-limit %d with val %d\n", $<number>5, $<number>7);
config.container_stack_limit = $<number>5;
config.container_stack_limit_value = $<number>7;
/* See the comment above */
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->table == NULL)
continue;
Container *con = ws->table[0][0];
con->stack_limit = config.container_stack_limit;
con->stack_limit_value = config.container_stack_limit_value;
}
}
;
new_window:
TOKNEWWINDOW WHITESPACE WORD
{
LOG("new windows should start in mode %s\n", $<string>3);
config.default_border = strdup($<string>3);
}
;
workspace:
TOKWORKSPACE WHITESPACE NUMBER WHITESPACE TOKSCREEN WHITESPACE screen optional_workspace_name
{
int ws_num = $<number>3;
if (ws_num < 1) {
LOG("Invalid workspace assignment, workspace number %d out of range\n", ws_num);
} else {
Workspace *ws = workspace_get(ws_num - 1);
ws->preferred_screen = sstrdup($<string>7);
if ($<string>8 != NULL)
workspace_set_name(ws, $<string>8);
}
}
| TOKWORKSPACE WHITESPACE NUMBER WHITESPACE workspace_name
{
int ws_num = $<number>3;
if (ws_num < 1) {
LOG("Invalid workspace assignment, workspace number %d out of range\n", ws_num);
} else {
if ($<string>5 != NULL)
workspace_set_name(workspace_get(ws_num - 1), $<string>5);
}
}
;
optional_workspace_name:
/* empty */ { $<string>$ = NULL; }
| WHITESPACE workspace_name { $<string>$ = $<string>2; }
;
workspace_name:
QUOTEDSTRING { $<string>$ = $<string>1; }
| STR { $<string>$ = $<string>1; }
;
screen:
NUMBER { asprintf(&$<string>$, "%d", $<number>1); }
| NUMBER 'x' { asprintf(&$<string>$, "%d", $<number>1); }
| NUMBER 'x' NUMBER { asprintf(&$<string>$, "%dx%d", $<number>1, $<number>3); }
| 'x' NUMBER { asprintf(&$<string>$, "x%d", $<number>2); }
;
assign:
TOKASSIGN WHITESPACE window_class WHITESPACE optional_arrow assign_target
{
printf("assignment of %s\n", $<string>3);
struct Assignment *new = $<assignment>6;
printf(" to %d\n", new->workspace);
printf(" floating = %d\n", new->floating);
new->windowclass_title = strdup($<string>3);
TAILQ_INSERT_TAIL(&assignments, new, assignments);
}
;
assign_target:
NUMBER
{
struct Assignment *new = scalloc(sizeof(struct Assignment));
new->workspace = $<number>1;
new->floating = ASSIGN_FLOATING_NO;
$<assignment>$ = new;
}
| '~'
{
struct Assignment *new = scalloc(sizeof(struct Assignment));
new->floating = ASSIGN_FLOATING_ONLY;
$<assignment>$ = new;
}
| '~' NUMBER
{
struct Assignment *new = scalloc(sizeof(struct Assignment));
new->workspace = $<number>2;
new->floating = ASSIGN_FLOATING;
$<assignment>$ = new;
}
;
window_class:
QUOTEDSTRING
| STR_NG
;
optional_arrow:
/* NULL */
| TOKARROW WHITESPACE
;
ipcsocket:
TOKIPCSOCKET WHITESPACE STR
{
config.ipc_socket_path = sstrdup($<string>3);
}
;
exec:
TOKEXEC WHITESPACE STR
{
struct Autostart *new = smalloc(sizeof(struct Autostart));
new->command = sstrdup($<string>3);
TAILQ_INSERT_TAIL(&autostarts, new, autostarts);
}
;
terminal:
TOKTERMINAL WHITESPACE STR
{
config.terminal = sstrdup($<string>3);
printf("terminal %s\n", config.terminal);
}
;
font:
TOKFONT WHITESPACE STR
{
config.font = sstrdup($<string>3);
printf("font %s\n", config.font);
}
;
color:
TOKCOLOR WHITESPACE colorpixel WHITESPACE colorpixel WHITESPACE colorpixel
{
struct Colortriple *dest = $<color>1;
dest->border = $<number>3;
dest->background = $<number>5;
dest->text = $<number>7;
}
;
colorpixel:
'#' HEX
{
char *hex;
if (asprintf(&hex, "#%s", $<string>2) == -1)
die("asprintf()");
$<number>$ = get_colorpixel(global_conn, hex);
free(hex);
}
;
binding_modifiers:
/* NULL */ { $<number>$ = 0; }
| binding_modifier
| binding_modifiers '+' binding_modifier { $<number>$ = $<number>1 | $<number>3; }
| binding_modifiers '+' { $<number>$ = $<number>1; }
;
binding_modifier:
MODIFIER { $<number>$ = $<number>1; }
| TOKCONTROL { $<number>$ = BIND_CONTROL; }
| TOKSHIFT { $<number>$ = BIND_SHIFT; }
;

385
src/click.c Normal file
View File

@ -0,0 +1,385 @@
/*
* vim:ts=8:expandtab
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
* src/click.c: Contains the handlers for button press (mouse click) events
* because they are quite large.
*
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <math.h>
#include <xcb/xcb.h>
#include <xcb/xcb_atom.h>
#include <xcb/xcb_icccm.h>
#include <X11/XKBlib.h>
#include "i3.h"
#include "queue.h"
#include "table.h"
#include "config.h"
#include "util.h"
#include "xcb.h"
#include "client.h"
#include "workspace.h"
#include "commands.h"
#include "floating.h"
#include "resize.h"
static struct Stack_Window *get_stack_window(xcb_window_t window_id) {
struct Stack_Window *current;
SLIST_FOREACH(current, &stack_wins, stack_windows) {
if (current->window != window_id)
continue;
return current;
}
return NULL;
}
/*
* Checks if the button press was on a stack window, handles focus setting and returns true
* if so, or false otherwise.
*
*/
static bool button_press_stackwin(xcb_connection_t *conn, xcb_button_press_event_t *event) {
struct Stack_Window *stack_win;
/* If we find a corresponding stack window, we can handle the event */
if ((stack_win = get_stack_window(event->event)) == NULL)
return false;
/* A stack window was clicked, we check if it was button4 or button5
which are scroll up / scroll down. */
if (event->detail == XCB_BUTTON_INDEX_4 || event->detail == XCB_BUTTON_INDEX_5) {
direction_t direction = (event->detail == XCB_BUTTON_INDEX_4 ? D_UP : D_DOWN);
focus_window_in_container(conn, CUR_CELL, direction);
return true;
}
/* It was no scrolling, so we calculate the destination client by
dividing the Y position of the event through the height of a window
decoration and then set the focus to this client. */
i3Font *font = load_font(conn, config.font);
int decoration_height = (font->height + 2 + 2);
int destination = (event->event_y / decoration_height),
c = 0,
num_clients = 0;
Client *client;
Container *container = stack_win->container;
CIRCLEQ_FOREACH(client, &(container->clients), clients)
num_clients++;
/* If we dont have any clients in this container, we cannot do
* anything useful anyways. */
if (num_clients == 0)
return true;
if (container->mode == MODE_TABBED)
destination = (event->event_x / (container->width / num_clients));
else if (container->mode == MODE_STACK &&
container->stack_limit != STACK_LIMIT_NONE) {
if (container->stack_limit == STACK_LIMIT_COLS) {
int wrap = ceil((float)num_clients / container->stack_limit_value);
int clicked_column = (event->event_x / (stack_win->rect.width / container->stack_limit_value));
int clicked_row = (event->event_y / decoration_height);
LOG("clicked on column %d, row %d\n", clicked_column, clicked_row);
destination = (wrap * clicked_column) + clicked_row;
} else {
int width = (stack_win->rect.width / ceil((float)num_clients / container->stack_limit_value));
int clicked_column = (event->event_x / width);
int clicked_row = (event->event_y / decoration_height);
LOG("clicked on column %d, row %d\n", clicked_column, clicked_row);
destination = (container->stack_limit_value * clicked_column) + clicked_row;
}
}
LOG("Click on stack_win for client %d\n", destination);
CIRCLEQ_FOREACH(client, &(stack_win->container->clients), clients)
if (c++ == destination) {
set_focus(conn, client, true);
return true;
}
return true;
}
/*
* Checks if the button press was on a bar, switches to the workspace and returns true
* if so, or false otherwise.
*
*/
static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *event) {
i3Screen *screen;
TAILQ_FOREACH(screen, virtual_screens, screens) {
if (screen->bar != event->event)
continue;
LOG("Click on a bar\n");
/* Check if the button was one of button4 or button5 (scroll up / scroll down) */
if (event->detail == XCB_BUTTON_INDEX_4 || event->detail == XCB_BUTTON_INDEX_5) {
Workspace *ws = c_ws;
if (event->detail == XCB_BUTTON_INDEX_5) {
while ((ws = TAILQ_NEXT(ws, workspaces)) != TAILQ_END(workspaces_head)) {
if (ws->screen == screen) {
workspace_show(conn, ws->num + 1);
return true;
}
}
} else {
while ((ws = TAILQ_PREV(ws, workspaces_head, workspaces)) != TAILQ_END(workspaces)) {
if (ws->screen == screen) {
workspace_show(conn, ws->num + 1);
return true;
}
}
}
return true;
}
int drawn = 0;
/* Because workspaces can be on different screens, we need to loop
through all of them and decide to count it based on its ->screen */
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen != screen)
continue;
LOG("Checking if click was on workspace %d with drawn = %d, tw = %d\n",
ws->num, drawn, ws->text_width);
if (event->event_x > (drawn + 1) &&
event->event_x <= (drawn + 1 + ws->text_width + 5 + 5)) {
workspace_show(conn, ws->num + 1);
return true;
}
drawn += ws->text_width + 5 + 5 + 2;
}
return true;
}
return false;
}
int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_event_t *event) {
LOG("Button %d pressed\n", event->state);
/* This was either a focus for a clients parent (= titlebar)… */
Client *client = table_get(&by_child, event->event);
bool border_click = false;
if (client == NULL) {
client = table_get(&by_parent, event->event);
border_click = true;
}
/* See if this was a click with the configured modifier. If so, we need
* to move around the client if it was floating. if not, we just process
* as usual. */
if (config.floating_modifier != 0 &&
(event->state & config.floating_modifier) != 0) {
if (client == NULL) {
LOG("Not handling, floating_modifier was pressed and no client found\n");
return 1;
}
if (client->fullscreen) {
LOG("Not handling, client is in fullscreen mode\n");
return 1;
}
if (client_is_floating(client)) {
LOG("button %d pressed\n", event->detail);
if (event->detail == 1) {
LOG("left mouse button, dragging\n");
floating_drag_window(conn, client, event);
} else if (event->detail == 3) {
LOG("right mouse button\n");
floating_resize_window(conn, client, event);
}
return 1;
} else {
/* The client is in tiling layout. We can still
* initiate a resize with the right mouse button,
* by chosing the border which is the most near one
* to the position of the mouse pointer */
if (event->detail == 3) {
int to_right = client->rect.width - event->event_x,
to_left = event->event_x,
to_top = event->event_y,
to_bottom = client->rect.height - event->event_y;
resize_orientation_t orientation = O_VERTICAL;
Container *con = client->container;
Workspace *ws = con->workspace;
int first = 0, second = 0;
LOG("click was %d px to the right, %d px to the left, %d px to top, %d px to bottom\n",
to_right, to_left, to_top, to_bottom);
if (to_right < to_left &&
to_right < to_top &&
to_right < to_bottom) {
/* …right border */
first = con->col + (con->colspan - 1);
LOG("column %d\n", first);
if (!cell_exists(first, con->row) ||
(first == (ws->cols-1)))
return 1;
second = first + 1;
} else if (to_left < to_right &&
to_left < to_top &&
to_left < to_bottom) {
/* …left border */
if (con->col == 0)
return 1;
first = con->col - 1;
second = con->col;
} else if (to_top < to_right &&
to_top < to_left &&
to_top < to_bottom) {
/* This was a press on the top border */
if (con->row == 0)
return 1;
first = con->row - 1;
second = con->row;
orientation = O_HORIZONTAL;
} else if (to_bottom < to_right &&
to_bottom < to_left &&
to_bottom < to_top) {
/* …bottom border */
first = con->row + (con->rowspan - 1);
if (!cell_exists(con->col, first) ||
(first == (ws->rows-1)))
return 1;
second = first + 1;
orientation = O_HORIZONTAL;
}
return resize_graphical_handler(conn, ws, first, second, orientation, event);
}
}
}
if (client == NULL) {
/* The client was neither on a clients titlebar nor on a client itself, maybe on a stack_window? */
if (button_press_stackwin(conn, event))
return 1;
/* Or on a bar? */
if (button_press_bar(conn, event))
return 1;
LOG("Could not handle this button press\n");
return 1;
}
/* Set focus in any case */
set_focus(conn, client, true);
/* Lets see if this was on the borders (= resize). If not, were done */
LOG("press button on x=%d, y=%d\n", event->event_x, event->event_y);
resize_orientation_t orientation = O_VERTICAL;
Container *con = client->container;
int first, second;
if (client->dock) {
LOG("dock. done.\n");
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
xcb_flush(conn);
return 1;
}
LOG("event->event_x = %d, client->rect.width = %d\n", event->event_x, client->rect.width);
/* Some clients (xfontsel for example) seem to pass clicks on their
* window to the parent window, thus we receive an event here which in
* reality is a border_click. Check for the position and fix state. */
if (border_click &&
event->event_x >= client->child_rect.x &&
event->event_x <= (client->child_rect.x + client->child_rect.width) &&
event->event_y >= client->child_rect.y &&
event->event_y <= (client->child_rect.y + client->child_rect.height)) {
LOG("Fixing border_click = false because of click in child\n");
border_click = false;
}
if (!border_click) {
LOG("client. done.\n");
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
/* Floating clients should be raised on click */
if (client_is_floating(client))
xcb_raise_window(conn, client->frame);
xcb_flush(conn);
return 1;
}
/* Dont handle events inside the titlebar, only borders are interesting */
i3Font *font = load_font(conn, config.font);
if (event->event_y >= 2 && event->event_y <= (font->height + 2 + 2)) {
LOG("click on titlebar\n");
/* Floating clients can be dragged by grabbing their titlebar */
if (client_is_floating(client)) {
/* Firstly, we raise it. Maybe the user just wanted to raise it without grabbing */
xcb_raise_window(conn, client->frame);
xcb_flush(conn);
floating_drag_window(conn, client, event);
}
return 1;
}
if (client_is_floating(client))
return floating_border_click(conn, client, event);
Workspace *ws = con->workspace;
if (event->event_y < 2) {
/* This was a press on the top border */
if (con->row == 0)
return 1;
first = con->row - 1;
second = con->row;
orientation = O_HORIZONTAL;
} else if (event->event_y >= (client->rect.height - 2)) {
/* …bottom border */
first = con->row + (con->rowspan - 1);
if (!cell_exists(con->col, first) ||
(first == (ws->rows-1)))
return 1;
second = first + 1;
orientation = O_HORIZONTAL;
} else if (event->event_x <= 2) {
/* …left border */
if (con->col == 0)
return 1;
first = con->col - 1;
second = con->col;
} else if (event->event_x > 2) {
/* …right border */
first = con->col + (con->colspan - 1);
LOG("column %d\n", first);
if (!cell_exists(first, con->row) ||
(first == (ws->cols-1)))
return 1;
second = first + 1;
}
return resize_graphical_handler(conn, ws, first, second, orientation, event);
}

View File

@ -24,6 +24,8 @@
#include "queue.h"
#include "layout.h"
#include "client.h"
#include "table.h"
#include "workspace.h"
/*
* Removes the given client from the container, either because it will be inserted into another
@ -38,7 +40,9 @@ void client_remove_from_container(xcb_connection_t *conn, Client *client, Contai
/* If the container will be empty now and is in stacking mode, we need to
unmap the stack_win */
if (CIRCLEQ_EMPTY(&(container->clients)) && container->mode == MODE_STACK) {
if (CIRCLEQ_EMPTY(&(container->clients)) &&
(container->mode == MODE_STACK ||
container->mode == MODE_TABBED)) {
LOG("Unmapping stack window\n");
struct Stack_Window *stack_win = &(container->stack_win);
stack_win->rect.height = 0;
@ -233,8 +237,9 @@ void client_toggle_fullscreen(xcb_connection_t *conn, Client *client) {
*/
void client_set_below_floating(xcb_connection_t *conn, Client *client) {
/* Ensure that it is below all floating clients */
Client *first_floating = TAILQ_FIRST(&(client->workspace->floating_clients));
if (first_floating != TAILQ_END(&(client->workspace->floating_clients))) {
Workspace *ws = client->workspace;
Client *first_floating = TAILQ_FIRST(&(ws->floating_clients));
if (first_floating != TAILQ_END(&(ws->floating_clients))) {
LOG("Setting below floating\n");
uint32_t values[] = { first_floating->frame, XCB_STACK_MODE_BELOW };
xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
@ -253,30 +258,41 @@ bool client_is_floating(Client *client) {
/*
* Change the border type for the given client to normal (n), 1px border (p) or
* completely borderless (b).
* completely borderless (b) without actually re-rendering the layout. Useful
* for calling it when initializing a new client.
*
*/
void client_change_border(xcb_connection_t *conn, Client *client, char border_type) {
bool client_init_border(xcb_connection_t *conn, Client *client, char border_type) {
switch (border_type) {
case 'n':
LOG("Changing to normal border\n");
client->titlebar_position = TITLEBAR_TOP;
client->borderless = false;
break;
return true;
case 'p':
LOG("Changing to 1px border\n");
client->titlebar_position = TITLEBAR_OFF;
client->borderless = false;
break;
return true;
case 'b':
LOG("Changing to borderless\n");
client->titlebar_position = TITLEBAR_OFF;
client->borderless = true;
break;
return true;
default:
LOG("Unknown border mode\n");
return;
return false;
}
}
/*
* Change the border type for the given client to normal (n), 1px border (p) or
* completely borderless (b).
*
*/
void client_change_border(xcb_connection_t *conn, Client *client, char border_type) {
if (!client_init_border(conn, client, border_type))
return;
/* Ensure that the childs position inside our window gets updated */
client->force_reconfigure = true;
@ -314,3 +330,31 @@ void client_map(xcb_connection_t *conn, Client *client) {
xcb_map_window(conn, client->frame);
}
/*
* Set the given mark for this client. Used for jumping to the client
* afterwards (like m<mark> and '<mark> in vim).
*
*/
void client_mark(xcb_connection_t *conn, Client *client, const char *mark) {
if (client->mark != NULL)
free(client->mark);
client->mark = sstrdup(mark);
/* Make sure no other client has this mark set */
Client *current;
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces)
SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) {
if (current == client ||
current->mark == NULL ||
strcmp(current->mark, mark) != 0)
continue;
free(current->mark);
current->mark = NULL;
/* We can break here since there can only be one other
* client with this mark. */
break;
}
}

View File

@ -29,6 +29,7 @@
#include "config.h"
#include "workspace.h"
#include "commands.h"
#include "resize.h"
bool focus_window_in_container(xcb_connection_t *conn, Container *container, direction_t direction) {
/* If this container is empty, were done */
@ -58,6 +59,24 @@ bool focus_window_in_container(xcb_connection_t *conn, Container *container, dir
typedef enum { THING_WINDOW, THING_CONTAINER, THING_SCREEN } thing_t;
static void jump_to_mark(xcb_connection_t *conn, const char *mark) {
Client *current;
LOG("Jumping to \"%s\"\n", mark);
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces)
SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) {
if (current->mark == NULL || strcmp(current->mark, mark) != 0)
continue;
workspace_show(conn, current->workspace->num + 1);
set_focus(conn, current, true);
return;
}
LOG("No window with this mark found\n");
}
static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t thing) {
LOG("focusing direction %d\n", direction);
@ -113,7 +132,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);
workspace_show(conn, target->current_workspace + 1);
workspace_show(conn, target->current_workspace->num + 1);
return;
}
@ -148,7 +167,7 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t
/* No screen found? Then wrap */
screen = get_screen_most((direction == D_UP ? D_DOWN : D_UP), container->workspace->screen);
}
t_ws = &(workspaces[screen->current_workspace]);
t_ws = screen->current_workspace;
new_row = (direction == D_UP ? (t_ws->rows - 1) : 0);
}
@ -190,7 +209,7 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t
LOG("Wrapping screen around horizontally\n");
screen = get_screen_most((direction == D_LEFT ? D_RIGHT : D_LEFT), container->workspace->screen);
}
t_ws = &(workspaces[screen->current_workspace]);
t_ws = screen->current_workspace;
new_col = (direction == D_LEFT ? (t_ws->cols - 1) : 0);
}
@ -506,7 +525,7 @@ static void snap_current_container(xcb_connection_t *conn, direction_t direction
static void move_floating_window_to_workspace(xcb_connection_t *conn, Client *client, int workspace) {
/* t_ws (to workspace) is just a container pointer to the workspace were switching to */
Workspace *t_ws = &(workspaces[workspace-1]),
Workspace *t_ws = workspace_get(workspace-1),
*old_ws = client->workspace;
LOG("moving floating\n");
@ -561,7 +580,7 @@ static void move_current_window_to_workspace(xcb_connection_t *conn, int workspa
assert(container != NULL);
/* t_ws (to workspace) is just a container pointer to the workspace were switching to */
Workspace *t_ws = &(workspaces[workspace-1]);
Workspace *t_ws = workspace_get(workspace-1);
Client *current_client = container->currently_focused;
if (current_client == NULL) {
@ -767,31 +786,75 @@ static char **append_argument(char **original, char *argument) {
*
*/
static void next_previous_workspace(xcb_connection_t *conn, int direction) {
Workspace *t_ws;
int i;
Workspace *ws = c_ws;
if (direction == 'n') {
/* If we are on the last workspace, we cannot go any further */
if (c_ws->num == 9)
return;
while ((ws = TAILQ_NEXT(ws, workspaces)) != TAILQ_END(workspaces_head)) {
if (ws->screen == NULL)
continue;
for (i = c_ws->num + 1; i <= 9; i++) {
t_ws = &(workspaces[i]);
if (t_ws->screen != NULL)
break;
workspace_show(conn, ws->num + 1);
return;
}
} else if (direction == 'p') {
if (c_ws->num == 0)
while ((ws = TAILQ_PREV(ws, workspaces_head, workspaces)) != TAILQ_END(workspaces)) {
if (ws->screen == NULL)
continue;
workspace_show(conn, ws->num + 1);
return;
for (i = c_ws->num - 1; i >= 0 ; i--) {
t_ws = &(workspaces[i]);
if (t_ws->screen != NULL)
break;
}
}
}
if (t_ws->screen != NULL)
workspace_show(conn, i+1);
static void parse_resize_command(xcb_connection_t *conn, Client *last_focused, const char *command) {
int first, second;
resize_orientation_t orientation = O_VERTICAL;
Container *con = last_focused->container;
Workspace *ws = con->workspace;
if (STARTS_WITH(command, "left")) {
if (con->col == 0)
return;
first = con->col - 1;
second = con->col;
command += strlen("left");
} else if (STARTS_WITH(command, "right")) {
first = con->col + (con->colspan - 1);
LOG("column %d\n", first);
if (!cell_exists(first, con->row) ||
(first == (ws->cols-1)))
return;
second = first + 1;
command += strlen("right");
} else if (STARTS_WITH(command, "top")) {
if (con->row == 0)
return;
first = con->row - 1;
second = con->row;
orientation = O_HORIZONTAL;
command += strlen("top");
} else if (STARTS_WITH(command, "bottom")) {
first = con->row + (con->rowspan - 1);
if (!cell_exists(con->col, first) ||
(first == (ws->rows-1)))
return;
second = first + 1;
orientation = O_HORIZONTAL;
command += strlen("bottom");
} else {
LOG("Syntax: resize <left|right|top|bottom> [+|-]<pixels>\n");
return;
}
int pixels = atoi(command);
if (pixels == 0)
return;
resize_container(conn, ws, first, second, orientation, pixels);
}
/*
@ -817,6 +880,76 @@ void parse_command(xcb_connection_t *conn, const char *command) {
return;
}
if (STARTS_WITH(command, "mark")) {
if (last_focused == NULL) {
LOG("There is no window to mark\n");
return;
}
const char *rest = command + strlen("mark");
while (*rest == ' ')
rest++;
if (*rest == '\0') {
LOG("interactive mark starting\n");
start_application("i3-input -p 'mark ' -l 1 -P 'Mark: '");
} else {
LOG("mark with \"%s\"\n", rest);
client_mark(conn, last_focused, rest);
}
return;
}
if (STARTS_WITH(command, "goto")) {
const char *rest = command + strlen("goto");
while (*rest == ' ')
rest++;
if (*rest == '\0') {
LOG("interactive go to mark starting\n");
start_application("i3-input -p 'goto ' -l 1 -P 'Goto: '");
} else {
LOG("go to \"%s\"\n", rest);
jump_to_mark(conn, rest);
}
return;
}
if (STARTS_WITH(command, "stack-limit ")) {
if (last_focused == NULL || client_is_floating(last_focused)) {
LOG("No container focused\n");
return;
}
const char *rest = command + strlen("stack-limit ");
if (strncmp(rest, "rows ", strlen("rows ")) == 0) {
last_focused->container->stack_limit = STACK_LIMIT_ROWS;
rest += strlen("rows ");
} else if (strncmp(rest, "cols ", strlen("cols ")) == 0) {
last_focused->container->stack_limit = STACK_LIMIT_COLS;
rest += strlen("cols ");
} else {
LOG("Syntax: stack-limit <cols|rows> <limit>\n");
return;
}
last_focused->container->stack_limit_value = atoi(rest);
if (last_focused->container->stack_limit_value == 0)
last_focused->container->stack_limit = STACK_LIMIT_NONE;
return;
}
if (STARTS_WITH(command, "resize ")) {
if (last_focused == NULL)
return;
const char *rest = command + strlen("resize ");
parse_resize_command(conn, last_focused, rest);
return;
}
if (STARTS_WITH(command, "mode ")) {
const char *rest = command + strlen("mode ");
switch_mode(conn, rest);
return;
}
/* Is it an <exit>? */
if (STARTS_WITH(command, "exit")) {
LOG("User issued exit-command, exiting without error.\n");
@ -875,23 +1008,41 @@ void parse_command(xcb_connection_t *conn, const char *command) {
}
/* 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[0] == 'T') && (command[1] == '\0')) {
if (last_focused != NULL && client_is_floating(last_focused)) {
LOG("not switching, this is a floating client\n");
return;
}
LOG("Switching mode for current container\n");
switch_layout_mode(conn, CUR_CELL, (command[0] == 's' ? MODE_STACK : MODE_DEFAULT));
int new_mode = MODE_DEFAULT;
if (command[0] == 's')
new_mode = MODE_STACK;
if (command[0] == 'T')
new_mode = MODE_TABBED;
switch_layout_mode(conn, CUR_CELL, new_mode);
return;
}
/* Is it 'bn' (border normal), 'bp' (border 1pixel) or 'bb' (border borderless)? */
/* or even 'bt' (toggle border: 'bp' -> 'bb' -> 'bn' ) */
if (command[0] == 'b') {
if (last_focused == NULL) {
LOG("No window focused, cannot change border type\n");
return;
}
client_change_border(conn, last_focused, command[1]);
char com = command[1];
if (command[1] == 't') {
if (last_focused->titlebar_position == TITLEBAR_TOP &&
!last_focused->borderless)
com = 'p';
else if (last_focused->titlebar_position == TITLEBAR_OFF &&
!last_focused->borderless)
com = 'b';
else com = 'n';
}
client_change_border(conn, last_focused, com);
return;
}
@ -934,14 +1085,16 @@ void parse_command(xcb_connection_t *conn, const char *command) {
return;
}
Workspace *ws = last_focused->workspace;
toggle_floating_mode(conn, last_focused, false);
/* delete all empty columns/rows */
cleanup_table(conn, last_focused->workspace);
cleanup_table(conn, ws);
/* Fix colspan/rowspan if itd overlap */
fix_colrowspan(conn, last_focused->workspace);
fix_colrowspan(conn, ws);
render_workspace(conn, last_focused->workspace->screen, last_focused->workspace);
render_workspace(conn, ws->screen, ws);
/* Re-focus the client because cleanup_table sets the focus to the last
* focused client inside a container only. */

View File

@ -26,7 +26,14 @@
#include "table.h"
#include "workspace.h"
/* prototype for src/cfgparse.y, will be cleaned up as soon as we completely
* switched to the new scanner/parser. */
void parse_file(const char *f);
Config config;
struct modes_head modes;
bool config_use_lexer = false;
/*
* This function resolves ~ in pathnames.
@ -93,7 +100,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint
*/
void grab_all_keys(xcb_connection_t *conn) {
Binding *bind;
TAILQ_FOREACH(bind, &bindings, bindings) {
TAILQ_FOREACH(bind, bindings, bindings) {
/* The easy case: the user specified a keycode directly. */
if (bind->keycode > 0) {
grab_keycode_for_binding(conn, bind, bind->keycode);
@ -107,14 +114,23 @@ void grab_all_keys(xcb_connection_t *conn) {
continue;
}
#ifdef OLD_XCB_KEYSYMS_API
bind->number_keycodes = 1;
xcb_keycode_t code = xcb_key_symbols_get_keycode(keysyms, keysym);
LOG("Translated symbol \"%s\" to 1 keycode (%d)\n", bind->symbol, code);
grab_keycode_for_binding(conn, bind, code);
bind->translated_to = smalloc(sizeof(xcb_keycode_t));
memcpy(bind->translated_to, &code, sizeof(xcb_keycode_t));
#else
uint32_t last_keycode = 0;
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 = 0;
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 */
@ -128,9 +144,32 @@ void grab_all_keys(xcb_connection_t *conn) {
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);
#endif
}
}
/*
* Switches the key bindings to the given mode, if the mode exists
*
*/
void switch_mode(xcb_connection_t *conn, const char *new_mode) {
struct Mode *mode;
LOG("Switching to mode %s\n", new_mode);
SLIST_FOREACH(mode, &modes, modes) {
if (strcasecmp(mode->name, new_mode) != 0)
continue;
ungrab_all_keys(conn);
bindings = mode->bindings;
grab_all_keys(conn);
return;
}
LOG("ERROR: Mode not found\n");
}
/*
* Reads the configuration from ~/.i3/config or /etc/i3/config if not found.
*
@ -143,13 +182,23 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
/* First ungrab the keys */
ungrab_all_keys(conn);
/* Clear the old binding and assignment lists */
struct Mode *mode;
Binding *bind;
while (!TAILQ_EMPTY(&bindings)) {
bind = TAILQ_FIRST(&bindings);
TAILQ_REMOVE(&bindings, bind, bindings);
FREE(bind->command);
FREE(bind);
while (!SLIST_EMPTY(&modes)) {
mode = SLIST_FIRST(&modes);
FREE(mode->name);
/* Clear the old binding list */
bindings = mode->bindings;
while (!TAILQ_EMPTY(bindings)) {
bind = TAILQ_FIRST(bindings);
TAILQ_REMOVE(bindings, bind, bindings);
FREE(bind->translated_to);
FREE(bind->command);
FREE(bind);
}
FREE(bindings);
SLIST_REMOVE(&modes, mode, Mode, modes);
}
struct Assignment *assign;
@ -161,6 +210,16 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
}
}
SLIST_INIT(&modes);
struct Mode *default_mode = scalloc(sizeof(struct Mode));
default_mode->name = sstrdup("default");
default_mode->bindings = scalloc(sizeof(struct bindings_head));
TAILQ_INIT(default_mode->bindings);
SLIST_INSERT_HEAD(&modes, default_mode, modes);
bindings = default_mode->bindings;
SLIST_HEAD(variables_head, Variable) variables;
#define OPTION_STRING(name) \
@ -202,14 +261,18 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
config.client.focused.background = get_colorpixel(conn, "#285577");
config.client.focused.text = get_colorpixel(conn, "#ffffff");
config.client.focused_inactive.border = get_colorpixel(conn, "#4c7899");
config.client.focused_inactive.background = get_colorpixel(conn, "#555555");
config.client.focused_inactive.border = get_colorpixel(conn, "#333333");
config.client.focused_inactive.background = get_colorpixel(conn, "#5f676a");
config.client.focused_inactive.text = get_colorpixel(conn, "#ffffff");
config.client.unfocused.border = get_colorpixel(conn, "#333333");
config.client.unfocused.background = get_colorpixel(conn, "#222222");
config.client.unfocused.text = get_colorpixel(conn, "#888888");
config.client.urgent.border = get_colorpixel(conn, "#2f343a");
config.client.urgent.background = get_colorpixel(conn, "#900000");
config.client.urgent.text = get_colorpixel(conn, "#ffffff");
config.bar.focused.border = get_colorpixel(conn, "#4c7899");
config.bar.focused.background = get_colorpixel(conn, "#285577");
config.bar.focused.text = get_colorpixel(conn, "#ffffff");
@ -218,6 +281,31 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
config.bar.unfocused.background = get_colorpixel(conn, "#222222");
config.bar.unfocused.text = get_colorpixel(conn, "#888888");
config.bar.urgent.border = get_colorpixel(conn, "#2f343a");
config.bar.urgent.background = get_colorpixel(conn, "#900000");
config.bar.urgent.text = get_colorpixel(conn, "#ffffff");
if (config_use_lexer) {
/* Yes, this will be cleaned up soon. */
if (override_configpath != NULL) {
parse_file(override_configpath);
} else {
FILE *handle;
char *globbed = glob_path("~/.i3/config");
if ((handle = fopen(globbed, "r")) == NULL) {
if ((handle = fopen("/etc/i3/config", "r")) == NULL) {
die("Neither \"%s\" nor /etc/i3/config could be opened\n", globbed);
} else {
parse_file("/etc/i3/config");
}
} else {
parse_file(globbed);
}
}
if (reload)
grab_all_keys(conn);
} else {
FILE *handle;
if (override_configpath != NULL) {
if ((handle = fopen(override_configpath, "r")) == NULL)
@ -260,8 +348,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
OPTION_COLORTRIPLE("client.focused", client.focused);
OPTION_COLORTRIPLE("client.focused_inactive", client.focused_inactive);
OPTION_COLORTRIPLE("client.unfocused", client.unfocused);
OPTION_COLORTRIPLE("client.urgent", client.urgent);
OPTION_COLORTRIPLE("bar.focused", bar.focused);
OPTION_COLORTRIPLE("bar.unfocused", bar.unfocused);
OPTION_COLORTRIPLE("bar.urgent", bar.urgent);
/* exec-lines (autostart) */
if (strcasecmp(key, "exec") == 0) {
@ -312,13 +402,22 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
rest++;
if (*rest != ' ')
die("Invalid binding (keysym)\n");
#if defined(__OpenBSD__)
size_t len = strlen(sym);
if (len > (rest - sym))
len = (rest - sym);
new->symbol = smalloc(len + 1);
memcpy(new->symbol, sym, len+1);
new->symbol[len]='\0';
#else
new->symbol = strndup(sym, (rest - sym));
#endif
}
rest++;
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);
TAILQ_INSERT_TAIL(bindings, new, bindings);
continue;
}
@ -374,7 +473,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
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;
workspace_get(ws_num-1)->preferred_screen = screen;
name += strlen("screen ") + strlen(screen);
}
@ -393,7 +492,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
LOG("setting name to \"%s\"\n", name);
if (*name != '\0')
workspace_set_name(&(workspaces[ws_num - 1]), name);
workspace_set_name(workspace_get(ws_num - 1), name);
free(ws_str);
continue;
}
@ -444,7 +543,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
while (*target == '~')
target++;
if (atoi(target) >= 1 && atoi(target) <= 10) {
if (atoi(target) >= 1) {
if (new->floating == ASSIGN_FLOATING_ONLY)
new->floating = ASSIGN_FLOATING;
new->workspace = atoi(target);
@ -493,9 +592,6 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
grab_all_keys(conn);
fclose(handle);
REQUIRED_OPTION(terminal);
REQUIRED_OPTION(font);
while (!SLIST_EMPTY(&variables)) {
struct Variable *v = SLIST_FIRST(&variables);
SLIST_REMOVE_HEAD(&variables, variables);
@ -503,10 +599,14 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
free(v->value);
free(v);
}
}
REQUIRED_OPTION(terminal);
REQUIRED_OPTION(font);
/* Set an empty name for every workspace which got no name */
for (int i = 0; i < 10; i++) {
Workspace *ws = &(workspaces[i]);
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->name != NULL) {
/* If the font was not specified when the workspace name
* was loaded, we need to predict the text width now */
@ -516,7 +616,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
continue;
}
workspace_set_name(&(workspaces[i]), NULL);
workspace_set_name(ws, NULL);
}
return;

View File

@ -26,6 +26,7 @@
#include "layout.h"
#include "client.h"
#include "floating.h"
#include "workspace.h"
/*
* Toggles floating mode for the given client.
@ -43,17 +44,18 @@ void toggle_floating_mode(xcb_connection_t *conn, Client *client, bool automatic
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)
Workspace *ws = client->workspace;
SLIST_FOREACH(next_tiling, &(ws->focus_stack), focus_clients)
if (!client_is_floating(next_tiling))
break;
/* If there are no tiling clients on this workspace, there can only be one
* container: the first one */
if (next_tiling == TAILQ_END(&(client->workspace->focus_stack)))
con = client->workspace->table[0][0];
if (next_tiling == TAILQ_END(&(ws->focus_stack)))
con = ws->table[0][0];
else con = next_tiling->container;
/* Remove the client from the list of floating clients */
TAILQ_REMOVE(&(client->workspace->floating_clients), client, floating_clients);
TAILQ_REMOVE(&(ws->floating_clients), client, floating_clients);
LOG("destination container = %p\n", con);
Client *old_focused = con->currently_focused;
@ -152,7 +154,6 @@ void floating_assign_to_workspace(Client *client, Workspace *new_workspace) {
TAILQ_INSERT_TAIL(&(client->workspace->floating_clients), client, floating_clients);
if (client->fullscreen)
client->workspace->fullscreen_client = client;
}
/*
@ -255,10 +256,37 @@ void floating_drag_window(xcb_connection_t *conn, Client *client, xcb_button_pre
/* fake_absolute_configure_notify flushes */
}
drag_pointer(conn, client, event, XCB_NONE, BORDER_TOP /* irrelevant */, drag_window_callback);
}
/*
* Called when the user right-clicked on the titlebar of a floating window to
* resize it.
* Calls the drag_pointer function with the resize_window callback
*
*/
void floating_resize_window(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event) {
LOG("floating_resize_window\n");
void resize_window_callback(Rect *old_rect, uint32_t new_x, uint32_t new_y) {
int32_t new_width = old_rect->width + (new_x - event->root_x);
int32_t new_height = old_rect->height + (new_y - event->root_y);
/* Obey minimum window size */
if (new_width < 75 || new_height < 50)
return;
/* Reposition the client correctly while moving */
client->rect.width = new_width;
client->rect.height = new_height;
/* resize_client flushes */
resize_client(conn, client);
}
drag_pointer(conn, client, event, XCB_NONE, BORDER_TOP /* irrelevant */, resize_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

View File

@ -116,7 +116,7 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_
/* Find the binding */
Binding *bind;
TAILQ_FOREACH(bind, &bindings, bindings) {
TAILQ_FOREACH(bind, bindings, bindings) {
/* First compare the modifiers */
if (bind->mods != state_filtered)
continue;
@ -137,7 +137,7 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_
/* 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… */
if (bind == TAILQ_END(&bindings)) {
if (bind == TAILQ_END(bindings)) {
xcb_allow_events(conn, ReplayKeyboard, event->time);
xcb_flush(conn);
return 1;
@ -170,7 +170,7 @@ static void check_crossing_screen_boundary(uint32_t x, uint32_t y) {
c_ws->current_row = current_row;
c_ws->current_col = current_col;
c_ws = &workspaces[screen->current_workspace];
c_ws = 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);
@ -275,226 +275,6 @@ int handle_mapping_notify(void *ignored, xcb_connection_t *conn, xcb_mapping_not
return 0;
}
/*
* Checks if the button press was on a stack window, handles focus setting and returns true
* if so, or false otherwise.
*
*/
static bool button_press_stackwin(xcb_connection_t *conn, xcb_button_press_event_t *event) {
struct Stack_Window *stack_win;
SLIST_FOREACH(stack_win, &stack_wins, stack_windows) {
if (stack_win->window != event->event)
continue;
/* A stack window was clicked, we check if it was button4 or button5
which are scroll up / scroll down. */
if (event->detail == XCB_BUTTON_INDEX_4 || event->detail == XCB_BUTTON_INDEX_5) {
direction_t direction = (event->detail == XCB_BUTTON_INDEX_4 ? D_UP : D_DOWN);
focus_window_in_container(conn, CUR_CELL, direction);
return true;
}
/* It was no scrolling, so we calculate the destination client by
dividing the Y position of the event through the height of a window
decoration and then set the focus to this client. */
i3Font *font = load_font(conn, config.font);
int decoration_height = (font->height + 2 + 2);
int destination = (event->event_y / decoration_height),
c = 0;
Client *client;
LOG("Click on stack_win for client %d\n", destination);
CIRCLEQ_FOREACH(client, &(stack_win->container->clients), clients)
if (c++ == destination) {
set_focus(conn, client, true);
return true;
}
return true;
}
return false;
}
/*
* Checks if the button press was on a bar, switches to the workspace and returns true
* if so, or false otherwise.
*
*/
static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *event) {
i3Screen *screen;
TAILQ_FOREACH(screen, virtual_screens, screens) {
if (screen->bar != event->event)
continue;
LOG("Click on a bar\n");
/* Check if the button was one of button4 or button5 (scroll up / scroll down) */
if (event->detail == XCB_BUTTON_INDEX_4 || event->detail == XCB_BUTTON_INDEX_5) {
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) {
workspace_show(conn, i+1);
return true;
}
return true;
}
int drawn = 0;
/* Because workspaces can be on different screens, we need to loop
through all of them and decide to count it based on its ->screen */
for (int i = 0; i < 10; i++) {
if (workspaces[i].screen != screen)
continue;
LOG("Checking if click was on workspace %d with drawn = %d, tw = %d\n",
i, drawn, workspaces[i].text_width);
if (event->event_x > (drawn + 1) &&
event->event_x <= (drawn + 1 + workspaces[i].text_width + 5 + 5)) {
workspace_show(conn, i+1);
return true;
}
drawn += workspaces[i].text_width + 5 + 5 + 2;
}
return true;
}
return false;
}
int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_event_t *event) {
LOG("Button %d pressed\n", event->state);
/* This was either a focus for a clients parent (= titlebar)… */
Client *client = table_get(&by_child, event->event);
bool border_click = false;
if (client == NULL) {
client = table_get(&by_parent, event->event);
border_click = true;
}
/* See if this was a click with the configured modifier. If so, we need
* to move around the client if it was floating. if not, we just process
* as usual. */
if (config.floating_modifier != 0 &&
(event->state & config.floating_modifier) != 0) {
if (client == NULL) {
LOG("Not handling, floating_modifier was pressed and no client found\n");
return 1;
}
if (client_is_floating(client)) {
floating_drag_window(conn, client, event);
return 1;
}
}
if (client == NULL) {
/* The client was neither on a clients titlebar nor on a client itself, maybe on a stack_window? */
if (button_press_stackwin(conn, event))
return 1;
/* Or on a bar? */
if (button_press_bar(conn, event))
return 1;
LOG("Could not handle this button press\n");
return 1;
}
/* Set focus in any case */
set_focus(conn, client, true);
/* Lets see if this was on the borders (= resize). If not, were done */
LOG("press button on x=%d, y=%d\n", event->event_x, event->event_y);
resize_orientation_t orientation = O_VERTICAL;
Container *con = client->container;
int first, second;
if (client->dock) {
LOG("dock. done.\n");
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
xcb_flush(conn);
return 1;
}
LOG("event->event_x = %d, client->rect.width = %d\n", event->event_x, client->rect.width);
/* Some clients (xfontsel for example) seem to pass clicks on their
* window to the parent window, thus we receive an event here which in
* reality is a border_click. Check for the position and fix state. */
if (border_click &&
event->event_x >= client->child_rect.x &&
event->event_x <= (client->child_rect.x + client->child_rect.width) &&
event->event_y >= client->child_rect.y &&
event->event_y <= (client->child_rect.y + client->child_rect.height)) {
LOG("Fixing border_click = false because of click in child\n");
border_click = false;
}
if (!border_click) {
LOG("client. done.\n");
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
/* Floating clients should be raised on click */
if (client_is_floating(client))
xcb_raise_window(conn, client->frame);
xcb_flush(conn);
return 1;
}
/* Dont handle events inside the titlebar, only borders are interesting */
i3Font *font = load_font(conn, config.font);
if (event->event_y >= 2 && event->event_y <= (font->height + 2 + 2)) {
LOG("click on titlebar\n");
/* Floating clients can be dragged by grabbing their titlebar */
if (client_is_floating(client)) {
/* Firstly, we raise it. Maybe the user just wanted to raise it without grabbing */
xcb_raise_window(conn, client->frame);
xcb_flush(conn);
floating_drag_window(conn, client, event);
}
return 1;
}
if (client_is_floating(client))
return floating_border_click(conn, client, event);
if (event->event_y < 2) {
/* This was a press on the top border */
if (con->row == 0)
return 1;
first = con->row - 1;
second = con->row;
orientation = O_HORIZONTAL;
} else if (event->event_y >= (client->rect.height - 2)) {
/* …bottom border */
first = con->row + (con->rowspan - 1);
if (!cell_exists(con->col, first) ||
(first == (con->workspace->rows-1)))
return 1;
second = first + 1;
orientation = O_HORIZONTAL;
} else if (event->event_x <= 2) {
/* …left border */
if (con->col == 0)
return 1;
first = con->col - 1;
second = con->col;
} else if (event->event_x > 2) {
/* …right border */
first = con->col + (con->colspan - 1);
LOG("column %d\n", first);
if (!cell_exists(first, con->row) ||
(first == (con->workspace->cols-1)))
return 1;
second = first + 1;
}
return resize_graphical_handler(conn, con->workspace, first, second, orientation, event);
}
/*
* A new window appeared on the screen (=was mapped), so lets manage it.
*
@ -581,6 +361,22 @@ int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure
return 1;
}
/* Dock clients can be reconfigured in their height */
if (client->dock) {
LOG("Reconfiguring height of this dock client\n");
if (!(event->value_mask & XCB_CONFIG_WINDOW_HEIGHT)) {
LOG("Ignoring configure request, no height given\n");
return 1;
}
client->desired_height = event->height;
render_workspace(conn, c_ws->screen, c_ws);
xcb_flush(conn);
return 1;
}
if (client->fullscreen) {
LOG("Client is in fullscreen mode\n");
@ -706,7 +502,7 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
/* If this workspace is currently active, we dont delete it */
i3Screen *screen;
TAILQ_FOREACH(screen, virtual_screens, screens)
if (screen->current_workspace == client->workspace->num) {
if (screen->current_workspace == client->workspace) {
workspace_active = true;
workspace_empty = false;
break;
@ -783,9 +579,11 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state,
if (client->dock)
return 1;
if (client->container != NULL && client->container->mode == MODE_STACK)
if (client->container != NULL &&
(client->container->mode == MODE_STACK ||
client->container->mode == MODE_TABBED))
render_container(conn, client->container);
else decorate_window(conn, client, client->frame, client->titlegc, 0);
else decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
xcb_flush(conn);
return 1;
@ -848,9 +646,11 @@ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t
if (client->dock)
return 1;
if (client->container != NULL && client->container->mode == MODE_STACK)
if (client->container != NULL &&
(client->container->mode == MODE_STACK ||
client->container->mode == MODE_TABBED))
render_container(conn, client->container);
else decorate_window(conn, client, client->frame, client->titlegc, 0);
else decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
xcb_flush(conn);
return 1;
@ -926,12 +726,16 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *
if (client->dock)
return 1;
if (client->container == NULL || client->container->mode != MODE_STACK)
decorate_window(conn, client, client->frame, client->titlegc, 0);
if (client->container == NULL ||
(client->container->mode != MODE_STACK &&
client->container->mode != MODE_TABBED))
decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
else {
uint32_t background_color;
if (client->urgent)
background_color = config.client.urgent.background;
/* Distinguish if the window is currently focused… */
if (CUR_CELL != NULL && CUR_CELL->currently_focused == client)
else if (CUR_CELL != NULL && CUR_CELL->currently_focused == client)
background_color = config.client.focused.background;
/* …or if it is the focused window in a not focused container */
else background_color = config.client.focused_inactive.background;
@ -949,8 +753,13 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *
/* Draw a black background */
xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
xcb_rectangle_t crect = {2, 0, client->rect.width - (2 + 2), client->rect.height - 2};
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
if (client->titlebar_position == TITLEBAR_OFF) {
xcb_rectangle_t crect = {1, 0, client->rect.width - (1 + 1), client->rect.height - 1};
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
} else {
xcb_rectangle_t crect = {2, 0, client->rect.width - (2 + 2), client->rect.height - 2};
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
}
}
xcb_flush(conn);
return 1;
@ -1097,6 +906,46 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w
return 1;
}
/*
* Handles the WM_HINTS property for extracting the urgency state of the window.
*
*/
int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
xcb_atom_t name, xcb_get_property_reply_t *reply) {
Client *client = table_get(&by_child, window);
if (client == NULL) {
LOG("Received WM_HINTS for unknown client\n");
return 1;
}
xcb_wm_hints_t hints;
if (reply != NULL) {
if (!xcb_get_wm_hints_from_reply(&hints, reply))
return 1;
} else {
if (!xcb_get_wm_hints_reply(conn, xcb_get_wm_hints_unchecked(conn, client->child), &hints, NULL))
return 1;
}
/* Update the flag on the client directly */
client->urgent = (xcb_wm_hints_get_urgency(&hints) != 0);
CLIENT_LOG(client);
LOG("Urgency flag changed to %d\n", client->urgent);
workspace_update_urgent_flag(client->workspace);
redecorate_window(conn, client);
/* If the workspace this client is on is not visible, we need to redraw
* the workspace bar */
if (!workspace_is_visible(client->workspace)) {
i3Screen *screen = client->workspace->screen;
render_workspace(conn, screen, screen->current_workspace);
xcb_flush(conn);
}
return 1;
}
/*
* Handles the transient for hints set by a window, signalizing that this window is a popup window
* for some other window.

View File

@ -77,10 +77,16 @@ static void ipc_handle_message(uint8_t *message, int size,
LOG("payload as a string = %s\n", message);
switch (message_type) {
case I3_IPC_MESSAGE_TYPE_COMMAND:
parse_command(global_conn, (const char*)message);
case I3_IPC_MESSAGE_TYPE_COMMAND: {
/* To get a properly terminated buffer, we copy
* message_size bytes out of the buffer */
char *command = scalloc(message_size);
strncpy(command, (const char*)message, message_size);
parse_command(global_conn, (const char*)command);
free(command);
break;
}
default:
LOG("unhandled ipc message\n");
break;
@ -148,20 +154,30 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
}
uint8_t *message = (uint8_t*)buf;
message += strlen(I3_IPC_MAGIC);
n -= strlen(I3_IPC_MAGIC);
while (n > 0) {
LOG("IPC: n = %d\n", n);
message += strlen(I3_IPC_MAGIC);
n -= strlen(I3_IPC_MAGIC);
/* The next 32 bit after the magic are the message size */
uint32_t message_size = *((uint32_t*)message);
message += sizeof(uint32_t);
n -= sizeof(uint32_t);
/* The next 32 bit after the magic are the message size */
uint32_t message_size = *((uint32_t*)message);
message += sizeof(uint32_t);
n -= sizeof(uint32_t);
/* The last 32 bits of the header are the message type */
uint32_t message_type = *((uint32_t*)message);
message += sizeof(uint32_t);
n -= sizeof(uint32_t);
if (message_size > n) {
LOG("IPC: Either the message size was wrong or the message was not read completely, dropping\n");
return;
}
ipc_handle_message(message, n, message_size, message_type);
/* The last 32 bits of the header are the message type */
uint32_t message_type = *((uint32_t*)message);
message += sizeof(uint32_t);
n -= sizeof(uint32_t);
ipc_handle_message(message, n, message_size, message_type);
n -= message_size;
message += message_size;
}
}
/*

View File

@ -27,6 +27,7 @@
#include "client.h"
#include "floating.h"
#include "handlers.h"
#include "workspace.h"
/*
* Updates *destination with new_value and returns true if it was changed or false
@ -63,16 +64,27 @@ int get_unoccupied_x(Workspace *workspace) {
}
/* See get_unoccupied_x() */
int get_unoccupied_y(Workspace *workspace, int col) {
int unoccupied = workspace->rect.height;
float default_factor = ((float)workspace->rect.height / workspace->rows) / workspace->rect.height;
int get_unoccupied_y(Workspace *workspace) {
int height = workspace->rect.height;
i3Font *font = load_font(global_conn, config.font);
/* Reserve space for dock clients */
Client *client;
SLIST_FOREACH(client, &(workspace->screen->dock_clients), dock_clients)
height -= client->desired_height;
/* Space for the internal bar */
height -= (font->height + 6);
int unoccupied = height;
float default_factor = ((float)height / workspace->rows) / height;
LOG("get_unoccupied_y(), starting with %d, default_factor = %f\n", unoccupied, default_factor);
for (int rows = 0; rows < workspace->rows; rows++) {
LOG("height_factor[%d] = %f\n", rows, workspace->height_factor[rows]);
if (workspace->height_factor[rows] == 0)
unoccupied -= workspace->rect.height * default_factor;
unoccupied -= height * default_factor;
}
LOG("unoccupied space: %d\n", unoccupied);
@ -86,12 +98,14 @@ int get_unoccupied_y(Workspace *workspace, int col) {
*
*/
void redecorate_window(xcb_connection_t *conn, Client *client) {
if (client->container != NULL && client->container->mode == MODE_STACK) {
if (client->container != NULL &&
(client->container->mode == MODE_STACK ||
client->container->mode == MODE_TABBED)) {
render_container(conn, client->container);
/* We clear the frame to generate exposure events, because the color used
in drawing may be different */
xcb_clear_area(conn, true, client->frame, 0, 0, client->rect.width, client->rect.height);
} else decorate_window(conn, client, client->frame, client->titlegc, 0);
} else decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
xcb_flush(conn);
}
@ -100,7 +114,8 @@ void redecorate_window(xcb_connection_t *conn, Client *client) {
* When in stacking mode, the window decorations are drawn onto an own window.
*
*/
void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t drawable, xcb_gcontext_t gc, int offset) {
void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t drawable,
xcb_gcontext_t gc, int offset_x, int offset_y) {
i3Font *font = load_font(conn, config.font);
int decoration_height = font->height + 2 + 2;
struct Colortriple *color;
@ -111,18 +126,23 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
return;
last_focused = SLIST_FIRST(&(client->workspace->focus_stack));
if (client_is_floating(client)) {
if (last_focused == client)
color = &(config.client.focused);
else color = &(config.client.unfocused);
} else {
if (client->container->currently_focused == client) {
/* Distinguish if the window is currently focused… */
if (last_focused == client && c_ws == client->workspace)
/* Is the window urgent? */
if (client->urgent)
color = &(config.client.urgent);
else {
if (client_is_floating(client)) {
if (last_focused == client)
color = &(config.client.focused);
/* …or if it is the focused window in a not focused container */
else color = &(config.client.focused_inactive);
} else color = &(config.client.unfocused);
else color = &(config.client.unfocused);
} else {
if (client->container->currently_focused == client) {
/* Distinguish if the window is currently focused… */
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);
} else color = &(config.client.unfocused);
}
}
/* Our plan is the following:
@ -132,16 +152,20 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
*/
/* Draw a rectangle in background color around the window */
if (client->borderless)
if (client->borderless && (client->container == NULL ||
(client->container->mode != MODE_STACK &&
client->container->mode != MODE_TABBED)))
xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
else xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, color->background);
/* In stacking mode, we only render the rect for this specific decoration */
if (client->container != NULL && client->container->mode == MODE_STACK) {
if (client->container != NULL && (client->container->mode == MODE_STACK || client->container->mode == MODE_TABBED)) {
/* We need to use the containers width because it is the more recent value - when
in stacking mode, clients get reconfigured only on demand (the not active client
is not reconfigured), so the clients rect.width would be wrong */
xcb_rectangle_t rect = {0, offset, client->container->width, offset + decoration_height };
xcb_rectangle_t rect = {offset_x, offset_y,
offset_x + client->container->width,
offset_y + decoration_height };
xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect);
} else {
xcb_rectangle_t rect = {0, 0, client->rect.width, client->rect.height};
@ -150,19 +174,28 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
/* Draw the inner background to have a black frame around clients (such as mplayer)
which cannot be resized exactly in our frames and therefore are centered */
xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
xcb_rectangle_t crect = {2, decoration_height,
client->rect.width - (2 + 2), client->rect.height - 2 - decoration_height};
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
if (client->titlebar_position == TITLEBAR_OFF) {
xcb_rectangle_t crect = {1, 1, client->rect.width - (1 + 1), client->rect.height - (1 + 1)};
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
} else {
xcb_rectangle_t crect = {2, decoration_height,
client->rect.width - (2 + 2), client->rect.height - 2 - decoration_height};
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
}
}
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, offset_x, offset_y, offset_x + client->rect.width, offset_y);
if ((client->container == NULL ||
client->container->mode != MODE_STACK ||
(client->container->mode != MODE_STACK &&
client->container->mode != MODE_TABBED) ||
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);
xcb_draw_line(conn, drawable, gc, color->border,
offset_x + 2, /* x */
offset_y + font->height + 3, /* y */
offset_x + client->rect.width - 3, /* to_x */
offset_y + font->height + 3 /* to_y */);
}
/* If the client has a title, we draw it */
@ -176,11 +209,11 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
and we dont handle the old window name (COMPOUND_TEXT) but only _NET_WM_NAME, which
is UTF-8 */
if (client->name_len == -1)
xcb_image_text_8(conn, strlen(client->name), drawable, gc, 3 /* X */,
offset + font->height /* Y = baseline of font */, client->name);
xcb_image_text_8(conn, strlen(client->name), drawable, gc, offset_x + 3 /* X */,
offset_y + font->height /* Y = baseline of font */, client->name);
else
xcb_image_text_16(conn, client->name_len, drawable, gc, 3 /* X */,
offset + font->height /* Y = baseline of font */, (xcb_char2b_t*)client->name);
xcb_image_text_16(conn, client->name_len, drawable, gc, offset_x + 3 /* X */,
offset_y + font->height /* Y = baseline of font */, (xcb_char2b_t*)client->name);
}
}
@ -210,7 +243,7 @@ void reposition_client(xcb_connection_t *conn, Client *client) {
LOG("Client is on workspace %p with screen %p\n", client->workspace, client->workspace->screen);
LOG("but screen at %d, %d is %p\n", client->rect.x, client->rect.y, screen);
floating_assign_to_workspace(client, &workspaces[screen->current_workspace]);
floating_assign_to_workspace(client, screen->current_workspace);
}
/*
@ -242,6 +275,7 @@ void resize_client(xcb_connection_t *conn, Client *client) {
Rect *rect = &(client->child_rect);
switch ((client->container != NULL ? client->container->mode : MODE_DEFAULT)) {
case MODE_STACK:
case MODE_TABBED:
rect->x = 2;
rect->y = 0;
rect->width = client->rect.width - (2 + 2);
@ -267,6 +301,9 @@ void resize_client(xcb_connection_t *conn, Client *client) {
break;
}
rect->width -= (2 * client->border_width);
rect->height -= (2 * client->border_width);
/* Obey the ratio, if any */
if (client->proportional_height != 0 &&
client->proportional_width != 0) {
@ -360,17 +397,38 @@ void render_container(xcb_connection_t *conn, Container *container) {
i3Font *font = load_font(conn, config.font);
int decoration_height = (font->height + 2 + 2);
struct Stack_Window *stack_win = &(container->stack_win);
/* The size for each tab (width), necessary as a separate variable
* because num_clients gets fixed to 1 in tabbed mode. */
int size_each = (num_clients == 0 ? container->width : container->width / num_clients);
int stack_lines = num_clients;
/* Check if we need to remap our stack title window, it gets unmapped when the container
is empty in src/handlers.c:unmap_notify() */
if (stack_win->rect.height == 0 && num_clients > 0)
if (stack_win->rect.height == 0 && num_clients > 0) {
LOG("remapping stack win\n");
xcb_map_window(conn, stack_win->window);
} else LOG("not remapping stackwin, height = %d, num_clients = %d\n",
stack_win->rect.height, num_clients);
if (container->mode == MODE_TABBED) {
/* By setting num_clients to 1 we force that the stack window will be only one line
* high. The rest of the code is useful in both cases. */
LOG("tabbed mode, setting num_clients = 1\n");
if (stack_lines > 1)
stack_lines = 1;
}
if (container->stack_limit == STACK_LIMIT_COLS) {
stack_lines = ceil((float)num_clients / container->stack_limit_value);
} else if (container->stack_limit == STACK_LIMIT_ROWS) {
stack_lines = min(num_clients, container->stack_limit_value);
}
/* Check if we need to reconfigure our stack title window */
if (update_if_necessary(&(stack_win->rect.x), container->x) |
update_if_necessary(&(stack_win->rect.y), container->y) |
update_if_necessary(&(stack_win->rect.width), container->width) |
update_if_necessary(&(stack_win->rect.height), decoration_height * num_clients)) {
update_if_necessary(&(stack_win->rect.height), decoration_height * stack_lines)) {
/* Configuration can happen in two slightly different ways:
@ -406,6 +464,22 @@ void render_container(xcb_connection_t *conn, Container *container) {
/* Prepare the pixmap for usage */
cached_pixmap_prepare(conn, &(stack_win->pixmap));
int current_row = 0, current_col = 0;
int wrap = 0;
if (container->stack_limit == STACK_LIMIT_COLS) {
/* wrap stores the number of rows after which we will
* wrap to a new column. */
wrap = ceil((float)num_clients / container->stack_limit_value);
} else if (container->stack_limit == STACK_LIMIT_ROWS) {
/* When limiting rows, the wrap variable serves a
* slightly different purpose: it holds the number of
* pixels which each client will get. This is constant
* during the following loop, so it saves us some
* divisions and ceil()ing. */
wrap = (stack_win->rect.width / ceil((float)num_clients / container->stack_limit_value));
}
/* Render the decorations of all clients */
CIRCLEQ_FOREACH(client, &(container->clients), clients) {
/* If the client is in fullscreen mode, it does not get reconfigured */
@ -415,18 +489,67 @@ void render_container(xcb_connection_t *conn, Container *container) {
}
/* Check if we changed client->x or client->y by updating it.
* Note the bitwise OR instead of logical OR to force evaluation of both statements */
* Note the bitwise OR instead of logical OR to force evaluation of all 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)) |
update_if_necessary(&(client->rect.y), container->y + (decoration_height * stack_lines)) |
update_if_necessary(&(client->rect.width), container->width) |
update_if_necessary(&(client->rect.height), container->height - (decoration_height * num_clients)))
update_if_necessary(&(client->rect.height), container->height - (decoration_height * stack_lines)))
resize_client(conn, client);
client->force_reconfigure = false;
int offset_x = 0;
int offset_y = 0;
if (container->mode == MODE_STACK) {
if (container->stack_limit == STACK_LIMIT_COLS) {
offset_x = current_col * (stack_win->rect.width / container->stack_limit_value);
offset_y = current_row * decoration_height;
current_row++;
if ((current_row % wrap) == 0) {
current_col++;
current_row = 0;
}
} else if (container->stack_limit == STACK_LIMIT_ROWS) {
offset_x = current_col * wrap;
offset_y = current_row * decoration_height;
current_row++;
if ((current_row % container->stack_limit_value) == 0) {
current_col++;
current_row = 0;
}
} else {
offset_y = current_client * decoration_height;
}
current_client++;
} else if (container->mode == MODE_TABBED)
offset_x = current_client++ * size_each;
decorate_window(conn, client, stack_win->pixmap.id, stack_win->pixmap.gc,
current_client++ * decoration_height);
offset_x, offset_y);
}
/* Check if we need to fill one column because of an uneven
* amount of windows */
if (container->mode == MODE_STACK) {
if (container->stack_limit == STACK_LIMIT_COLS && (current_col % 2) != 0) {
xcb_change_gc_single(conn, stack_win->pixmap.gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
int offset_x = current_col * (stack_win->rect.width / container->stack_limit_value);
int offset_y = current_row * decoration_height;
xcb_rectangle_t rect = {offset_x, offset_y,
offset_x + container->width,
offset_y + decoration_height };
xcb_poly_fill_rectangle(conn, stack_win->pixmap.id, stack_win->pixmap.gc, 1, &rect);
} else if (container->stack_limit == STACK_LIMIT_ROWS && (current_row % 2) != 0) {
xcb_change_gc_single(conn, stack_win->pixmap.gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
int offset_x = current_col * wrap;
int offset_y = current_row * decoration_height;
xcb_rectangle_t rect = {offset_x, offset_y,
offset_x + container->width,
offset_y + decoration_height };
xcb_poly_fill_rectangle(conn, stack_win->pixmap.id, stack_win->pixmap.gc, 1, &rect);
}
}
xcb_copy_area(conn, stack_win->pixmap.id, stack_win->window, stack_win->pixmap.gc,
@ -468,13 +591,18 @@ static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int wid
xcb_change_gc_single(conn, screen->bargc, XCB_GC_FONT, font->id);
int drawn = 0;
for (int c = 0; c < 10; c++) {
if (workspaces[c].screen != screen)
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen != screen)
continue;
struct Colortriple *color = (screen->current_workspace == c ? &(config.bar.focused) :
&(config.bar.unfocused));
Workspace *ws = &workspaces[c];
struct Colortriple *color;
if (screen->current_workspace == ws)
color = &(config.bar.focused);
else if (ws->urgent)
color = &(config.bar.urgent);
else color = &(config.bar.unfocused);
/* Draw the outer rect */
xcb_draw_rect(conn, screen->bar, screen->bargc, color->border,
@ -509,7 +637,10 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bo
Client *client;
uint32_t values[1];
FOR_TABLE(workspace)
FOR_TABLE(workspace) {
if (workspace->table[cols][rows] == NULL)
continue;
CIRCLEQ_FOREACH(client, &(workspace->table[cols][rows]->clients), clients) {
/* Change event mask for the decorations */
values[0] = FRAME_EVENT_MASK;
@ -523,6 +654,7 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bo
values[0] &= ~(XCB_EVENT_MASK_ENTER_WINDOW);
xcb_change_window_attributes(conn, client->child, XCB_CW_EVENT_MASK, values);
}
}
}
/*
@ -555,7 +687,9 @@ void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws)
/* Go through the whole table and render whats necessary */
FOR_TABLE(r_ws) {
Container *container = r_ws->table[cols][rows];
int single_width = -1, single_height;
if (container == NULL)
continue;
int single_width = -1, single_height = -1;
/* Update position of the container */
container->row = rows;
container->col = cols;
@ -572,11 +706,18 @@ void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws)
single_width = container->width;
}
//if (container->height_factor == 0)
container->height = (height / r_ws->rows);
//else container->height = get_unoccupied_y(r_ws, cols) * container->height_factor;
single_height = container->height;
container->height *= container->rowspan;
LOG("height is %d\n", height);
container->height = 0;
for (int c = 0; c < container->rowspan; c++) {
if (r_ws->height_factor[rows+c] == 0)
container->height += (height / r_ws->rows);
else container->height += get_unoccupied_y(r_ws) * r_ws->height_factor[rows+c];
if (single_height == -1)
single_height = container->height;
}
/* Render the container if it is not empty */
render_container(conn, container);
@ -602,8 +743,12 @@ void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws)
void render_layout(xcb_connection_t *conn) {
i3Screen *screen;
if (virtual_screens == NULL)
return;
TAILQ_FOREACH(screen, virtual_screens, screens)
render_workspace(conn, screen, &(workspaces[screen->current_workspace]));
if (screen->current_workspace != NULL)
render_workspace(conn, screen, screen->current_workspace);
xcb_flush(conn);
}

View File

@ -19,6 +19,7 @@
#include <limits.h>
#include <locale.h>
#include <fcntl.h>
#include <getopt.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKB.h>
@ -38,6 +39,7 @@
#include "data.h"
#include "debug.h"
#include "handlers.h"
#include "click.h"
#include "i3.h"
#include "layout.h"
#include "queue.h"
@ -59,7 +61,7 @@ Display *xkbdpy;
xcb_key_symbols_t *keysyms;
/* The list of key bindings */
struct bindings_head bindings = TAILQ_HEAD_INITIALIZER(bindings);
struct bindings_head *bindings;
/* The list of exec-lines */
struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);
@ -147,6 +149,14 @@ int main(int argc, char *argv[], char *env[]) {
xcb_connection_t *conn;
xcb_property_handlers_t prophs;
xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
static struct option long_options[] = {
{"no-autostart", no_argument, 0, 'a'},
{"config", required_argument, 0, 'c'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int option_index = 0;
setlocale(LC_ALL, "");
@ -156,7 +166,7 @@ int main(int argc, char *argv[], char *env[]) {
start_argv = argv;
while ((opt = getopt(argc, argv, "c:va")) != -1) {
while ((opt = getopt_long(argc, argv, "c:vahl", long_options, &option_index)) != -1) {
switch (opt) {
case 'a':
LOG("Autostart disabled using -a\n");
@ -168,8 +178,15 @@ int main(int argc, char *argv[], char *env[]) {
case 'v':
printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n");
exit(EXIT_SUCCESS);
case 'l':
config_use_lexer = true;
break;
default:
fprintf(stderr, "Usage: %s [-c configfile]\n", argv[0]);
fprintf(stderr, "Usage: %s [-c configfile] [-a] [-v]\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "-a: disable autostart\n");
fprintf(stderr, "-v: display version and exit\n");
fprintf(stderr, "-c <configfile>: use the provided configfile instead\n");
exit(EXIT_FAILURE);
}
}
@ -189,6 +206,14 @@ int main(int argc, char *argv[], char *env[]) {
load_configuration(conn, override_configpath, false);
/* Create the initial container on the first workspace. This used to
* be part of init_table, but since it possibly requires an X
* connection and a loaded configuration (default mode for new
* containers may be stacking, which requires a new window to be
* created), it had to be delayed. */
expand_table_cols(TAILQ_FIRST(workspaces));
expand_table_rows(TAILQ_FIRST(workspaces));
/* Place requests for the atoms we need as soon as possible */
#define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(conn, 0, strlen(#name), #name);
@ -385,6 +410,9 @@ int main(int argc, char *argv[], char *env[]) {
/* Watch WM_CLIENT_LEADER (= logical parent window for toolbars etc.) */
xcb_property_set_handler(&prophs, atoms[WM_CLIENT_LEADER], UINT_MAX, handle_clientleader_change, NULL);
/* Watch WM_HINTS (contains the urgent property) */
xcb_property_set_handler(&prophs, WM_HINTS, UINT_MAX, handle_hints, NULL);
/* Set up the atoms we support */
check_error(conn, xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED],
ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED");
@ -422,12 +450,13 @@ int main(int argc, char *argv[], char *env[]) {
i3Screen *screen = get_screen_containing(reply->root_x, reply->root_y);
if (screen == NULL) {
LOG("ERROR: No screen at %d x %d\n", reply->root_x, reply->root_y);
return 0;
LOG("ERROR: No screen at %d x %d, starting on the first screen\n",
reply->root_x, reply->root_y);
screen = TAILQ_FIRST(virtual_screens);
}
LOG("Starting on %d\n", screen->current_workspace);
c_ws = &workspaces[screen->current_workspace];
c_ws = screen->current_workspace;
manage_existing_windows(conn, &prophs, root);

View File

@ -50,11 +50,11 @@ void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *pr
/* Request the window attributes for every window */
children = xcb_query_tree_children(reply);
for(i = 0; i < len; ++i)
for (i = 0; i < len; ++i)
cookies[i] = xcb_get_window_attributes(conn, children[i]);
/* Call manage_window with the attributes for every window */
for(i = 0; i < len; ++i)
for (i = 0; i < len; ++i)
manage_window(prophs, conn, children[i], cookies[i], true);
free(reply);
@ -99,12 +99,14 @@ void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
/* Reparent the window and add it to our list of managed windows */
reparent_window(conn, window, attr->visual, geom->root, geom->depth,
geom->x, geom->y, geom->width, geom->height);
geom->x, geom->y, geom->width, geom->height,
geom->border_width);
/* Generate callback events for every property we watch */
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_CLASS);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NORMAL_HINTS);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_HINTS);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_TRANSIENT_FOR);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[WM_CLIENT_LEADER]);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
@ -124,7 +126,8 @@ out:
*/
void reparent_window(xcb_connection_t *conn, xcb_window_t child,
xcb_visualid_t visual, xcb_window_t root, uint8_t depth,
int16_t x, int16_t y, uint16_t width, uint16_t height) {
int16_t x, int16_t y, uint16_t width, uint16_t height,
uint32_t border_width) {
xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
utf8_title_cookie, title_cookie,
@ -174,11 +177,15 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
new->rect.height = height;
new->width_increment = 1;
new->height_increment = 1;
new->border_width = border_width;
/* Pre-initialize the values for floating */
new->floating_rect.x = -1;
new->floating_rect.width = width;
new->floating_rect.height = height;
if (config.default_border != NULL)
client_init_border(conn, new, config.default_border[1]);
mask = 0;
/* Dont generate events for our new window, it should *not* be managed */
@ -231,7 +238,8 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
1 /* left mouse button */, XCB_MOD_MASK_1);
3 /* right mouse button */,
XCB_BUTTON_MASK_ANY /* dont filter for any modifiers */);
/* Get _NET_WM_WINDOW_TYPE (to see if its a dock) */
xcb_atom_t *atom;
@ -324,13 +332,13 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
LOG("Assignment \"%s\" matches, so putting it on workspace %d\n",
assign->windowclass_title, assign->workspace);
if (c_ws->screen->current_workspace == (assign->workspace-1)) {
if (c_ws->screen->current_workspace->num == (assign->workspace-1)) {
LOG("We are already there, no need to do anything\n");
break;
}
LOG("Changing container/workspace and unmapping the client\n");
Workspace *t_ws = &(workspaces[assign->workspace-1]);
Workspace *t_ws = workspace_get(assign->workspace-1);
workspace_initialize(t_ws, c_ws->screen);
new->container = t_ws->table[t_ws->current_col][t_ws->current_row];

View File

@ -54,13 +54,6 @@ int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, i
LOG("Screen dimensions: (%d, %d) %d x %d\n", screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height);
/* FIXME: horizontal resizing causes empty spaces to exist */
if (orientation == O_HORIZONTAL) {
LOG("Sorry, horizontal resizing is not yet activated due to creating layout bugs."
"If you are brave, enable the code for yourself and try fixing it.\n");
return 1;
}
uint32_t mask = 0;
uint32_t values[2];
@ -133,13 +126,26 @@ int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, i
xcb_destroy_window(conn, grabwin);
xcb_flush(conn);
if (orientation == O_VERTICAL) {
LOG("Resize was from X = %d to X = %d\n", event->root_x, new_position);
if (event->root_x == new_position) {
LOG("Nothing changed, not updating anything\n");
return 1;
}
int pixels;
if (orientation == O_VERTICAL)
pixels = (new_position - event->root_x);
else pixels = (new_position - event->root_y);
resize_container(conn, ws, first, second, orientation, pixels);
return 1;
}
/*
* Resizes a column/row by the given amount of pixels. Called by
* resize_graphical_handler (the user clicked) or parse_resize_command (the
* user issued the command)
*
*/
void resize_container(xcb_connection_t *conn, Workspace *ws, int first, int second,
resize_orientation_t orientation, int pixels) {
/* TODO: refactor this, both blocks are very identical */
if (orientation == O_VERTICAL) {
int default_width = ws->rect.width / ws->cols;
int old_unoccupied_x = get_unoccupied_x(ws);
@ -180,8 +186,8 @@ int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, i
LOG("middle = %f\n", ws->width_factor[first]);
int old_width = ws->width_factor[first] * old_unoccupied_x;
LOG("first->width = %d, new_position = %d, event->root_x = %d\n", old_width, new_position, event->root_x);
ws->width_factor[first] *= (float)(old_width + (new_position - event->root_x)) / old_width;
LOG("first->width = %d, pixels = %d\n", pixels);
ws->width_factor[first] *= (float)(old_width + pixels) / old_width;
LOG("-> %f\n", ws->width_factor[first]);
@ -190,33 +196,72 @@ int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, i
ws->width_factor[second] = ((float)ws->rect.width / ws->cols) / new_unoccupied_x;
LOG("middle = %f\n", ws->width_factor[second]);
old_width = ws->width_factor[second] * old_unoccupied_x;
LOG("second->width = %d, new_position = %d, event->root_x = %d\n", old_width, new_position, event->root_x);
ws->width_factor[second] *= (float)(old_width - (new_position - event->root_x)) / old_width;
LOG("second->width = %d, pixels = %d\n", pixels);
ws->width_factor[second] *= (float)(old_width - pixels) / old_width;
LOG("-> %f\n", ws->width_factor[second]);
LOG("new unoccupied_x = %d\n", get_unoccupied_x(ws));
LOG("\n\n\n");
} else {
#if 0
LOG("Resize was from Y = %d to Y = %d\n", event->root_y, new_position);
if (event->root_y == new_position) {
LOG("Nothing changed, not updating anything\n");
return 1;
}
int default_height = ws->rect.height / ws->rows;
int old_unoccupied_y = get_unoccupied_y(ws);
/* Convert 0 (for default height_factor) to actual numbers */
if (first->height_factor == 0)
first->height_factor = ((float)ws->rect.height / ws->rows) / ws->rect.height;
if (second->height_factor == 0)
second->height_factor = ((float)ws->rect.height / ws->rows) / ws->rect.height;
/* We pre-calculate the unoccupied space to see if we need to adapt sizes before
* doing the resize */
int new_unoccupied_y = old_unoccupied_y;
first->height_factor *= (float)(first->height + (new_position - event->root_y)) / first->height;
second->height_factor *= (float)(second->height - (new_position - event->root_y)) / second->height;
#endif
if (old_unoccupied_y == 0)
old_unoccupied_y = ws->rect.height;
if (ws->height_factor[first] == 0)
new_unoccupied_y += default_height;
if (ws->height_factor[second] == 0)
new_unoccupied_y += default_height;
LOG("\n\n\n");
LOG("old = %d, new = %d\n", old_unoccupied_y, new_unoccupied_y);
/* If the space used for customly resized columns has changed we need to adapt the
* other customly resized columns, if any */
if (new_unoccupied_y != old_unoccupied_y)
for (int row = 0; row < ws->rows; row++) {
if (ws->height_factor[row] == 0)
continue;
LOG("Updating other column (%d) (current width_factor = %f)\n", row, ws->height_factor[row]);
ws->height_factor[row] = (ws->height_factor[row] * old_unoccupied_y) / new_unoccupied_y;
LOG("to %f\n", ws->height_factor[row]);
}
LOG("old_unoccupied_y = %d\n", old_unoccupied_y);
LOG("Updating first (before = %f)\n", ws->height_factor[first]);
/* Convert 0 (for default width_factor) to actual numbers */
if (ws->height_factor[first] == 0)
ws->height_factor[first] = ((float)ws->rect.height / ws->rows) / new_unoccupied_y;
LOG("middle = %f\n", ws->height_factor[first]);
int old_height = ws->height_factor[first] * old_unoccupied_y;
LOG("first->width = %d, pixels = %d\n", pixels);
ws->height_factor[first] *= (float)(old_height + pixels) / old_height;
LOG("-> %f\n", ws->height_factor[first]);
LOG("Updating second (before = %f)\n", ws->height_factor[second]);
if (ws->height_factor[second] == 0)
ws->height_factor[second] = ((float)ws->rect.height / ws->rows) / new_unoccupied_y;
LOG("middle = %f\n", ws->height_factor[second]);
old_height = ws->height_factor[second] * old_unoccupied_y;
LOG("second->width = %d, pixels = %d\n", pixels);
ws->height_factor[second] *= (float)(old_height - pixels) / old_height;
LOG("-> %f\n", ws->height_factor[second]);
LOG("new unoccupied_y = %d\n", get_unoccupied_y(ws));
LOG("\n\n\n");
}
render_layout(conn);
return 1;
}

View File

@ -25,11 +25,14 @@
#include "util.h"
#include "i3.h"
#include "layout.h"
#include "config.h"
#include "workspace.h"
int current_workspace = 0;
Workspace workspaces[10];
int num_workspaces = 1;
struct workspaces_head *workspaces;
/* Convenience pointer to the current workspace */
Workspace *c_ws = &workspaces[0];
Workspace *c_ws;
int current_col = 0;
int current_row = 0;
@ -38,18 +41,16 @@ int current_row = 0;
*
*/
void init_table() {
memset(workspaces, 0, sizeof(workspaces));
workspaces = scalloc(sizeof(struct workspaces_head));
TAILQ_INIT(workspaces);
for (int i = 0; i < 10; i++) {
workspaces[i].screen = NULL;
workspaces[i].num = i;
TAILQ_INIT(&(workspaces[i].floating_clients));
expand_table_cols(&(workspaces[i]));
expand_table_rows(&(workspaces[i]));
}
c_ws = scalloc(sizeof(Workspace));
workspace_set_name(c_ws, NULL);
TAILQ_INIT(&(c_ws->floating_clients));
TAILQ_INSERT_TAIL(workspaces, c_ws, workspaces);
}
static void new_container(Workspace *workspace, Container **container, int col, int row) {
static void new_container(Workspace *workspace, Container **container, int col, int row, bool skip_layout_switch) {
Container *new;
new = *container = calloc(sizeof(Container), 1);
CIRCLEQ_INIT(&(new->clients));
@ -58,6 +59,10 @@ static void new_container(Workspace *workspace, Container **container, int col,
new->col = col;
new->row = row;
new->workspace = workspace;
if (!skip_layout_switch)
switch_layout_mode(global_conn, new, config.container_mode);
new->stack_limit = config.container_stack_limit;
new->stack_limit_value = config.container_stack_limit_value;
}
/*
@ -72,8 +77,14 @@ void expand_table_rows(Workspace *workspace) {
for (int c = 0; c < workspace->cols; c++) {
workspace->table[c] = realloc(workspace->table[c], sizeof(Container*) * workspace->rows);
new_container(workspace, &(workspace->table[c][workspace->rows-1]), c, workspace->rows-1);
new_container(workspace, &(workspace->table[c][workspace->rows-1]), c, workspace->rows-1, true);
}
/* We need to switch the layout in a separate step because it could
* happen that render_layout() (being called by switch_layout_mode())
* would access containers which were not yet initialized. */
for (int c = 0; c < workspace->cols; c++)
switch_layout_mode(global_conn, workspace->table[c][workspace->rows-1], config.container_mode);
}
/*
@ -105,7 +116,7 @@ void expand_table_rows_at_head(Workspace *workspace) {
}
for (int cols = 0; cols < workspace->cols; cols++)
new_container(workspace, &(workspace->table[cols][0]), cols, 0);
new_container(workspace, &(workspace->table[cols][0]), cols, 0, false);
}
/*
@ -120,8 +131,12 @@ void expand_table_cols(Workspace *workspace) {
workspace->table = realloc(workspace->table, sizeof(Container**) * workspace->cols);
workspace->table[workspace->cols-1] = calloc(sizeof(Container*) * workspace->rows, 1);
for (int c = 0; c < workspace->rows; c++)
new_container(workspace, &(workspace->table[workspace->cols-1][c]), workspace->cols-1, c);
new_container(workspace, &(workspace->table[workspace->cols-1][c]), workspace->cols-1, c, true);
for (int c = 0; c < workspace->rows; c++)
switch_layout_mode(global_conn, workspace->table[workspace->cols-1][c], config.container_mode);
}
/*
@ -153,7 +168,7 @@ void expand_table_cols_at_head(Workspace *workspace) {
}
for (int rows = 0; rows < workspace->rows; rows++)
new_container(workspace, &(workspace->table[0][rows]), 0, rows);
new_container(workspace, &(workspace->table[0][rows]), 0, rows, false);
}
/*
@ -198,11 +213,29 @@ static void shrink_table_cols(Workspace *workspace) {
*
*/
static void shrink_table_rows(Workspace *workspace) {
float free_space = workspace->height_factor[workspace->rows-1];
workspace->rows--;
for (int cols = 0; cols < workspace->cols; cols++)
workspace->table[cols] = realloc(workspace->table[cols], sizeof(Container*) * workspace->rows);
}
/* Shrink the height_factor array */
workspace->height_factor = realloc(workspace->height_factor, sizeof(float) * workspace->rows);
/* Distribute the free space */
if (free_space == 0)
return;
for (int rows = (workspace->rows-1); rows >= 0; rows--) {
if (workspace->height_factor[rows] == 0)
continue;
LOG("Added free space (%f) to %d (had %f)\n", free_space, rows,
workspace->height_factor[rows]);
workspace->height_factor[rows] += free_space;
break;
}
}
/*
* Performs simple bounds checking for the given column/row
@ -216,7 +249,7 @@ bool cell_exists(int col, int row) {
static void free_container(xcb_connection_t *conn, Workspace *workspace, int col, int row) {
Container *old_container = workspace->table[col][row];
if (old_container->mode == MODE_STACK)
if (old_container->mode == MODE_STACK || old_container->mode == MODE_TABBED)
leave_stack_mode(conn, old_container);
free(old_container);

View File

@ -18,6 +18,9 @@
#include <stdarg.h>
#include <assert.h>
#include <iconv.h>
#if defined(__OpenBSD__)
#include <sys/cdefs.h>
#endif
#include <xcb/xcb_icccm.h>
@ -270,7 +273,8 @@ void set_focus(xcb_connection_t *conn, Client *client, bool set_anyways) {
Client *last_focused = get_last_focused_client(conn, client->container, NULL);
/* In stacking containers, raise the client in respect to the one which was focused before */
if (client->container->mode == MODE_STACK && client->container->workspace->fullscreen_client == NULL) {
if ((client->container->mode == MODE_STACK || client->container->mode == MODE_TABBED) &&
client->container->workspace->fullscreen_client == NULL) {
/* We need to get the client again, this time excluding the current client, because
* we might have just gone into stacking mode and need to raise */
Client *last_focused = get_last_focused_client(conn, client->container, client);
@ -339,15 +343,22 @@ void leave_stack_mode(xcb_connection_t *conn, Container *container) {
*
*/
void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode) {
if (mode == MODE_STACK) {
if (mode == MODE_STACK || mode == MODE_TABBED) {
/* When were already in stacking mode, nothing has to be done */
if (container->mode == MODE_STACK)
if ((mode == MODE_STACK && container->mode == MODE_STACK) ||
(mode == MODE_TABBED && container->mode == MODE_TABBED))
return;
/* When entering stacking mode, we need to open a window on which we can draw the
title bars of the clients, it has height 1 because we dont bother here with
calculating the correct height - it will be adjusted when rendering anyways. */
Rect rect = {container->x, container->y, container->width, 1};
if (container->mode == MODE_STACK || container->mode == MODE_TABBED)
goto after_stackwin;
/* When entering stacking mode, we need to open a window on
* which we can draw the title bars of the clients, it has
* height 1 because we dont bother here with calculating the
* correct height - it will be adjusted when rendering anyways.
* Also, we need to use max(width, 1) because windows cannot
* be created with either width == 0 or height == 0. */
Rect rect = {container->x, container->y, max(container->width, 1), 1};
uint32_t mask = 0;
uint32_t values[2];
@ -377,9 +388,10 @@ void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode)
SLIST_INSERT_HEAD(&stack_wins, stack_win, stack_windows);
} else {
if (container->mode == MODE_STACK)
if (container->mode == MODE_STACK || container->mode == MODE_TABBED)
leave_stack_mode(conn, container);
}
after_stackwin:
container->mode = mode;
/* Force reconfiguration of each client */
@ -446,12 +458,13 @@ Client *get_matching_client(xcb_connection_t *conn, const char *window_classtitl
}
LOG("Getting clients for class \"%s\" / title \"%s\"\n", to_class, to_title);
for (int workspace = 0; workspace < 10; workspace++) {
if (workspaces[workspace].screen == NULL)
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen == NULL)
continue;
Client *client;
SLIST_FOREACH(client, &(workspaces[workspace].focus_stack), focus_clients) {
SLIST_FOREACH(client, &(ws->focus_stack), focus_clients) {
LOG("Checking client with class=%s, name=%s\n", client->window_class, client->name);
if (!client_matches_class_name(client, to_class, to_title, to_title_ucs, to_title_ucs_len))
continue;
@ -466,3 +479,40 @@ done:
FREE(to_title_ucs);
return matching;
}
#if defined(__OpenBSD__)
/*
* Taken from FreeBSD
* Find the first occurrence of the byte string s in byte string l.
*
*/
void *memmem(const void *l, size_t l_len, const void *s, size_t s_len) {
register char *cur, *last;
const char *cl = (const char *)l;
const char *cs = (const char *)s;
/* we need something to compare */
if (l_len == 0 || s_len == 0)
return NULL;
/* "s" must be smaller or equal to "l" */
if (l_len < s_len)
return NULL;
/* special case where s_len == 1 */
if (s_len == 1)
return memchr(l, (int)*cs, l_len);
/* the last position where its possible to find "s" in "l" */
last = (char *)cl + l_len - s_len;
for (cur = (char *)cl; cur <= last; cur++)
if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
return cur;
return NULL;
}
#endif

View File

@ -27,6 +27,40 @@
#include "workspace.h"
#include "client.h"
/*
* Returns a pointer to the workspace with the given number (starting at 0),
* creating the workspace if necessary (by allocating the necessary amount of
* memory and initializing the data structures correctly).
*
*/
Workspace *workspace_get(int number) {
Workspace *ws = NULL;
TAILQ_FOREACH(ws, workspaces, workspaces)
if (ws->num == number)
return ws;
/* If we are still there, we could not find the requested workspace. */
int last_ws = TAILQ_LAST(workspaces, workspaces_head)->num;
LOG("We need to initialize that one, last ws = %d\n", last_ws);
for (int c = last_ws; c < number; c++) {
LOG("Creating new ws\n");
ws = scalloc(sizeof(Workspace));
ws->num = c+1;
TAILQ_INIT(&(ws->floating_clients));
expand_table_cols(ws);
expand_table_rows(ws);
workspace_set_name(ws, NULL);
TAILQ_INSERT_TAIL(workspaces, ws, workspaces);
}
LOG("done\n");
return ws;
}
/*
* Sets the name (or just its number) for the given workspace. This has to
* be called for every workspace as the rendering function
@ -62,7 +96,7 @@ void workspace_set_name(Workspace *ws, const char *name) {
*
*/
bool workspace_is_visible(Workspace *ws) {
return (ws->screen->current_workspace == ws->num);
return (ws->screen->current_workspace == ws);
}
/*
@ -73,7 +107,7 @@ 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 were switching to */
Workspace *t_ws = &(workspaces[workspace-1]);
Workspace *t_ws = workspace_get(workspace-1);
LOG("show_workspace(%d)\n", workspace);
@ -91,7 +125,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
/* Store the old client */
Client *old_client = CUR_CELL->currently_focused;
c_ws = &(workspaces[t_ws->screen->current_workspace]);
c_ws = t_ws->screen->current_workspace;
current_col = c_ws->current_col;
current_row = c_ws->current_row;
if (CUR_CELL->currently_focused != NULL)
@ -109,7 +143,7 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
}
/* Check if we need to change something or if were already there */
if (c_ws->screen->current_workspace == (workspace-1)) {
if (c_ws->screen->current_workspace->num == (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);
@ -121,9 +155,8 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
return;
}
t_ws->screen->current_workspace = workspace-1;
Workspace *old_workspace = c_ws;
c_ws = &workspaces[workspace-1];
c_ws = t_ws->screen->current_workspace = workspace_get(workspace-1);
/* Unmap all clients of the old workspace */
workspace_unmap_clients(conn, old_workspace);
@ -229,7 +262,6 @@ void workspace_initialize(Workspace *ws, i3Screen *screen) {
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));
@ -243,8 +275,8 @@ void workspace_initialize(Workspace *ws, i3Screen *screen) {
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]);
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->preferred_screen == NULL ||
!screens_are_equal(get_screen_from_preference(slist, ws->preferred_screen), screen))
continue;
@ -255,22 +287,28 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *
if (result == NULL) {
/* No assignment found, returning first unused workspace */
for (int c = 0; c < 10; c++) {
if (workspaces[c].screen != NULL)
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen != NULL)
continue;
result = &(workspaces[c]);
result = ws;
break;
}
}
if (result != NULL) {
workspace_initialize(result, screen);
return result;
if (result == NULL) {
LOG("No existing free workspace found to assign, creating a new one\n");
Workspace *ws;
int last_ws = 0;
TAILQ_FOREACH(ws, workspaces, workspaces)
last_ws = ws->num;
result = workspace_get(last_ws + 1);
}
LOG("WARNING: No free workspace found to assign!\n");
return NULL;
workspace_initialize(result, screen);
return result;
}
/*
@ -361,3 +399,21 @@ void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws) {
ignore_enter_notify_forall(conn, u_ws, false);
}
/*
* Goes through all clients on the given workspace and updates the workspaces
* urgent flag accordingly.
*
*/
void workspace_update_urgent_flag(Workspace *ws) {
Client *current;
SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) {
if (!current->urgent)
continue;
ws->urgent = true;
return;
}
ws->urgent = false;
}

View File

@ -133,7 +133,7 @@ static void initialize_screen(xcb_connection_t *conn, i3Screen *screen, Workspac
i3Font *font = load_font(conn, config.font);
workspace->screen = screen;
screen->current_workspace = workspace->num;
screen->current_workspace = workspace;
/* Create a bar for each screen */
Rect bar_rect = {screen->rect.x,
@ -298,12 +298,13 @@ void xinerama_requery_screens(xcb_connection_t *conn) {
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);
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces)
ws->reassigned = (ws->screen == NULL);
TAILQ_FOREACH(screen, new_screens, screens) {
screen->num = screen_count;
screen->current_workspace = -1;
screen->current_workspace = NULL;
TAILQ_FOREACH(old_screen, virtual_screens, screens) {
if (old_screen->num != screen_count)
@ -332,10 +333,11 @@ void xinerama_requery_screens(xcb_connection_t *conn) {
/* Copy the list head for the dock clients */
screen->dock_clients = old_screen->dock_clients;
SLIST_INIT(&(old_screen->dock_clients));
/* Update the dimensions */
for (int c = 0; c < 10; c++) {
Workspace *ws = &(workspaces[c]);
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->screen != old_screen)
continue;
@ -347,13 +349,14 @@ void xinerama_requery_screens(xcb_connection_t *conn) {
break;
}
if (screen->current_workspace == -1) {
if (screen->current_workspace == NULL) {
/* 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);
ws->reassigned = true;
/* As this workspace just got visible (we got a new screen
* without workspace), we need to map its clients */
@ -362,39 +365,58 @@ void xinerama_requery_screens(xcb_connection_t *conn) {
screen_count++;
}
/* Check for workspaces which are out of bounds */
for (int c = 0; c < 10; c++) {
if (workspaces[c].reassigned)
/* check for dock_clients which are out of bounds */
TAILQ_FOREACH(old_screen, virtual_screens, screens) {
if (SLIST_EMPTY(&(old_screen->dock_clients)))
continue;
LOG("dock_clients out of bounds at screen %p, reassigning\n", old_screen);
if (SLIST_EMPTY(&(first->dock_clients))) {
first->dock_clients = old_screen->dock_clients;
continue;
}
/* We need to merge the lists */
Client *dock_client;
while (!SLIST_EMPTY(&(old_screen->dock_clients))) {
dock_client = SLIST_FIRST(&(old_screen->dock_clients));
SLIST_INSERT_HEAD(&(first->dock_clients), dock_client, dock_clients);
SLIST_REMOVE_HEAD(&(old_screen->dock_clients), dock_clients);
}
}
/* Check for workspaces which are out of bounds */
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->reassigned)
continue;
/* f_ws is a shortcut to the workspace to fix */
Workspace *f_ws = &(workspaces[c]);
Client *client;
LOG("Closing bar window (%p)\n", f_ws->screen->bar);
xcb_destroy_window(conn, f_ws->screen->bar);
LOG("Closing bar window (%p)\n", ws->screen->bar);
xcb_destroy_window(conn, ws->screen->bar);
LOG("Workspace %d's screen out of bounds, assigning to first screen\n", c+1);
f_ws->screen = first;
memcpy(&(f_ws->rect), &(first->rect), sizeof(Rect));
LOG("Workspace %d's screen out of bounds, assigning to first screen\n", ws->num + 1);
ws->screen = first;
memcpy(&(ws->rect), &(first->rect), sizeof(Rect));
/* Force reconfiguration for each client on that workspace */
FOR_TABLE(f_ws)
CIRCLEQ_FOREACH(client, &(f_ws->table[cols][rows]->clients), clients)
FOR_TABLE(ws)
CIRCLEQ_FOREACH(client, &(ws->table[cols][rows]->clients), clients)
client->force_reconfigure = true;
/* Render the workspace to reconfigure the clients. However, they will be visible now, so… */
render_workspace(conn, first, f_ws);
render_workspace(conn, first, ws);
/* …unless we want to see them at the moment, we should hide that workspace */
if (workspace_is_visible(f_ws))
if (workspace_is_visible(ws))
continue;
workspace_unmap_clients(conn, f_ws);
workspace_unmap_clients(conn, ws);
if (c_ws == f_ws) {
if (c_ws == ws) {
LOG("Need to adjust c_ws...\n");
c_ws = &(workspaces[first->current_workspace]);
c_ws = first->current_workspace;
}
}
xcb_flush(conn);