first step of the big refactoring ("tree" branch).
From here on, we can track changes. It made no sense to put the development up to this point into git.
This commit is contained in:
parent
41b6631f68
commit
c145f7e529
11
Makefile
11
Makefile
|
@ -4,7 +4,7 @@ include $(TOPDIR)/common.mk
|
|||
|
||||
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||
AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c
|
||||
FILES:=$(filter-out $(AUTOGENERATED),$(wildcard src/*.c))
|
||||
FILES:=src/ipc.c src/nc.c src/log.c src/util.c src/tree.c src/xcb.c src/manage.c src/workspace.c src/x.c src/floating.c src/click.c src/config.c src/handlers.c src/randr.c src/xinerama.c src/con.c src/load_layout.c src/render.c
|
||||
FILES:=$(FILES:.c=.o)
|
||||
HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h))
|
||||
|
||||
|
@ -13,7 +13,7 @@ HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h))
|
|||
# updated if necessary, but we also want to save rebuilds of the object
|
||||
# files, so we cannot let the object files depend on loglevels.h.
|
||||
ifeq ($(MAKECMDGOALS),loglevels.h)
|
||||
UNUSED:=$(warning Generating loglevels.h)
|
||||
#UNUSED:=$(warning Generating loglevels.h)
|
||||
else
|
||||
UNUSED:=$(shell $(MAKE) loglevels.h)
|
||||
endif
|
||||
|
@ -25,12 +25,7 @@ src/%.o: src/%.c ${HEADERS}
|
|||
|
||||
all: src/cfgparse.y.o src/cfgparse.yy.o ${FILES}
|
||||
echo "LINK i3"
|
||||
$(CC) -o i3 ${FILES} src/cfgparse.y.o src/cfgparse.yy.o $(LDFLAGS)
|
||||
echo ""
|
||||
echo "SUBDIR i3-msg"
|
||||
$(MAKE) TOPDIR=$(TOPDIR) -C i3-msg
|
||||
echo "SUBDIR i3-input"
|
||||
$(MAKE) TOPDIR=$(TOPDIR) -C i3-input
|
||||
$(CC) -o i3 $^ $(LDFLAGS)
|
||||
|
||||
loglevels.h:
|
||||
echo "LOGLEVELS"
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
README file for the "tree" branch of i3
|
||||
=======================================
|
||||
|
||||
This is a *massive* refactoring of i3. It was driven by thinking about whether
|
||||
a different data structure could make things easier (for users and developers).
|
||||
The old data structure of a table provided relatively flexible layouts but was
|
||||
*very* hard to implement.
|
||||
|
||||
The new data structure is a tree. You can have horizontally and vertically split
|
||||
containers. Each container can contain either nothing yet (waiting for a window
|
||||
or for the user to put more containers in it), one or more containers or exactly
|
||||
one window. RandR Outputs and workspaces are not treated specially, but they
|
||||
are just containers inside the tree.
|
||||
|
||||
This structure allows for easy serialization, meaning multiple things:
|
||||
- we can store and reload the layout for inplace restarts (this is already working)
|
||||
- we can store parts of the layout and load them at any position in our tree
|
||||
(partly working)
|
||||
- we can load a layout specifying the physical positions of RandR outputs
|
||||
for pathologic screen setups
|
||||
- we can load a default layout for each workspace to specify the position
|
||||
of dock clients
|
||||
- we can use test-driven development to a much higher degree because we have
|
||||
access to the whole tree
|
||||
|
||||
Ripping out the core data structures of i3 and replacing them of course has
|
||||
some side-effects, which I will describe here (the list may not be complete,
|
||||
new side-effects may not be known yet):
|
||||
- Empty containers are allowed. They can swallow windows based on certain
|
||||
criteria. We can implement session-saving this way.
|
||||
- Assignments (put windows on certain workspaces, put workspaces on certain
|
||||
outputs) are just special cases of the point above.
|
||||
- Window decorations are now always rendered on the parent window. This means
|
||||
we don’t have to carry around ugly Stack_Windows any more.
|
||||
- Operations always (?) operate on containers, so you can make a container
|
||||
(containing multiple windows) fullscreen or floating (for example) and no
|
||||
longer just single windows.
|
||||
- All X11 requests are now pushed to X11 in a separate step (rendering is one
|
||||
step, updating X11 another). This makes talking to X11 a lot less error-prone
|
||||
and allows for simpler code.
|
||||
|
||||
======================
|
||||
SOME WORDS OF WARNING:
|
||||
======================
|
||||
|
||||
The current state of the branch is not nearly the quality you know of i3. It
|
||||
is in flux, changes and crashes are to be expected. Many features do not work
|
||||
yet. It is only suitable if you want to help developing or have a look at what
|
||||
is coming. Do *NOT* use it for production! You have been warned.
|
16
common.mk
16
common.mk
|
@ -20,22 +20,6 @@ CFLAGS += -Iinclude
|
|||
CFLAGS += -I/usr/local/include
|
||||
CFLAGS += -DI3_VERSION=\"${GIT_VERSION}\"
|
||||
|
||||
# Check if pkg-config is installed, because without pkg-config, the following
|
||||
# check for the version of libxcb cannot be done.
|
||||
ifeq ($(shell which pkg-config 2>/dev/null 1>/dev/null || echo 1),1)
|
||||
$(error "pkg-config was not found")
|
||||
endif
|
||||
|
||||
ifeq ($(shell pkg-config --exists xcb-keysyms || echo 1),1)
|
||||
$(error "pkg-config could not find xcb-keysyms.pc")
|
||||
endif
|
||||
|
||||
ifeq ($(shell pkg-config --exact-version=0.3.3 xcb-keysyms && echo 1),1)
|
||||
# xcb-keysyms fixed API from 0.3.3 to 0.3.4, so for some months, we will
|
||||
# have this here. Distributions should upgrade their libxcb in the meantime.
|
||||
CFLAGS += -DOLD_XCB_KEYSYMS_API
|
||||
endif
|
||||
|
||||
LDFLAGS += -lm
|
||||
LDFLAGS += -lxcb-event
|
||||
LDFLAGS += -lxcb-property
|
||||
|
|
|
@ -127,12 +127,11 @@ int main(int argc, char *argv[]) {
|
|||
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
|
||||
if (o == 's') {
|
||||
socket_path = strdup(optarg);
|
||||
break;
|
||||
} else if (o == 't') {
|
||||
if (strcasecmp(optarg, "command") == 0)
|
||||
message_type = I3_IPC_MESSAGE_TYPE_COMMAND;
|
||||
else if (strcasecmp(optarg, "get_workspaces") == 0)
|
||||
message_type = I3_IPC_MESSAGE_TYPE_GET_WORKSPACES;
|
||||
else if (strcasecmp(optarg, "tree") == 0)
|
||||
message_type = I3_IPC_MESSAGE_TYPE_GET_TREE;
|
||||
else {
|
||||
printf("Unknown message type\n");
|
||||
printf("Known types: command, get_workspaces\n");
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* This header file includes all relevant files of i3 and the most often used
|
||||
* system header files. This reduces boilerplate (the amount of code duplicated
|
||||
* at the beginning of each source file) and is not significantly slower at
|
||||
* compile-time.
|
||||
*
|
||||
*/
|
||||
#ifndef _ALL_H
|
||||
#define _ALL_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <glob.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "commands.h"
|
||||
#include "ipc.h"
|
||||
#include "tree.h"
|
||||
#include "log.h"
|
||||
#include "xcb.h"
|
||||
#include "manage.h"
|
||||
#include "workspace.h"
|
||||
#include "i3.h"
|
||||
#include "x.h"
|
||||
#include "click.h"
|
||||
#include "floating.h"
|
||||
#include "config.h"
|
||||
#include "handlers.h"
|
||||
#include "randr.h"
|
||||
#include "xinerama.h"
|
||||
#include "con.h"
|
||||
#include "load_layout.h"
|
||||
#include "render.h"
|
||||
|
||||
#endif
|
|
@ -13,10 +13,12 @@
|
|||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#if 0
|
||||
bool focus_window_in_container(xcb_connection_t *conn, Container *container,
|
||||
direction_t direction);
|
||||
#endif
|
||||
|
||||
/** Parses a command, see file CMDMODE for more information */
|
||||
void parse_command(xcb_connection_t *conn, const char *command);
|
||||
void parse_command(const char *command);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef _CON_H
|
||||
#define _CON_H
|
||||
|
||||
Con *con_new(Con *parent);
|
||||
bool con_is_leaf(Con *con);
|
||||
bool con_accepts_window(Con *con);
|
||||
Con *con_get_output(Con *con);
|
||||
Con *con_get_workspace(Con *con);
|
||||
Con *con_get_fullscreen_con(Con *con);
|
||||
bool con_is_floating(Con *con);
|
||||
Con *con_by_window_id(xcb_window_t window);
|
||||
Con *con_by_frame_id(xcb_window_t frame);
|
||||
Con *con_for_window(i3Window *window, Match **store_match);
|
||||
void con_attach(Con *con, Con *parent);
|
||||
void con_detach(Con *con);
|
||||
|
||||
enum { WINDOW_ADD = 0, WINDOW_REMOVE = 1 };
|
||||
void con_fix_percent(Con *con, int action);
|
||||
|
||||
#endif
|
|
@ -124,6 +124,9 @@ struct Config {
|
|||
} bar;
|
||||
};
|
||||
|
||||
char *glob_path(const char *path);
|
||||
bool path_exists(const char *path);
|
||||
|
||||
/**
|
||||
* Reads the configuration from ~/.i3/config or /etc/i3/config if not found.
|
||||
*
|
||||
|
|
384
include/data.h
384
include/data.h
|
@ -26,42 +26,25 @@
|
|||
*
|
||||
* Let’s start from the biggest to the smallest:
|
||||
*
|
||||
* - An Output is a physical output on your graphics driver. Outputs which
|
||||
* are currently in use have (output->active == true). Each output has a
|
||||
* position and a mode. An output usually corresponds to one connected
|
||||
* screen (except if you are running multiple screens in clone mode).
|
||||
*
|
||||
* - Each Output contains Workspaces. The concept is known from various
|
||||
* other window managers. Basically, a workspace is a specific set of
|
||||
* windows, usually grouped thematically (irc, www, work, …). You can switch
|
||||
* between these.
|
||||
*
|
||||
* - Each Workspace has a table, which is our layout abstraction. You manage
|
||||
* your windows by moving them around in your table. It grows as necessary.
|
||||
*
|
||||
* - Each cell of the table has a container, which can be in default or
|
||||
* stacking mode. In default mode, each client is given equally much space
|
||||
* in the container. In stacking mode, only one client is shown at a time,
|
||||
* but all the titlebars are rendered at the top.
|
||||
*
|
||||
* - Inside the container are clients, which is X11-speak for a window.
|
||||
* TODO
|
||||
*
|
||||
*/
|
||||
|
||||
/* Forward definitions */
|
||||
typedef struct Cell Cell;
|
||||
typedef struct Font i3Font;
|
||||
typedef struct Container Container;
|
||||
typedef struct Client Client;
|
||||
typedef struct Binding Binding;
|
||||
typedef struct Workspace Workspace;
|
||||
typedef struct Rect Rect;
|
||||
typedef struct xoutput Output;
|
||||
typedef struct Con Con;
|
||||
typedef struct Match Match;
|
||||
typedef struct Window i3Window;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Helper types
|
||||
*****************************************************************************/
|
||||
typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
|
||||
typedef enum { HORIZ, VERT, NO_ORIENTATION } orientation_t;
|
||||
|
||||
enum {
|
||||
BIND_NONE = 0,
|
||||
|
@ -94,15 +77,6 @@ struct Rect {
|
|||
uint32_t height;
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* Defines a position in the table
|
||||
*
|
||||
*/
|
||||
struct Cell {
|
||||
int row;
|
||||
int column;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for the cache of colorpixels.
|
||||
*
|
||||
|
@ -129,22 +103,6 @@ struct Cached_Pixmap {
|
|||
xcb_drawable_t referred_drawable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains data for the windows needed to draw the titlebars on in stacking
|
||||
* mode
|
||||
*
|
||||
*/
|
||||
struct Stack_Window {
|
||||
xcb_window_t window;
|
||||
struct Cached_Pixmap pixmap;
|
||||
Rect rect;
|
||||
|
||||
/** Backpointer to the container this stack window is in */
|
||||
Container *container;
|
||||
|
||||
SLIST_ENTRY(Stack_Window) stack_windows;
|
||||
};
|
||||
|
||||
struct Ignore_Event {
|
||||
int sequence;
|
||||
time_t added;
|
||||
|
@ -167,86 +125,6 @@ struct keyvalue_element {
|
|||
* Major types
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* The concept of Workspaces is known from various other window
|
||||
* managers. Basically, a workspace is a specific set of windows, usually
|
||||
* grouped thematically (irc, www, work, …). You can switch between these.
|
||||
*
|
||||
*/
|
||||
struct Workspace {
|
||||
/** Number of this workspace, starting from 0 */
|
||||
int num;
|
||||
|
||||
/** Name of the workspace (in UTF-8) */
|
||||
char *utf8_name;
|
||||
|
||||
/** Name of the workspace (in UCS-2) */
|
||||
char *name;
|
||||
|
||||
/** Length of the workspace’s name (in glyphs) */
|
||||
int name_len;
|
||||
|
||||
/** Width of the workspace’s name (in pixels) rendered in config.font */
|
||||
int text_width;
|
||||
|
||||
/** x, y, width, height */
|
||||
Rect rect;
|
||||
|
||||
/** table dimensions */
|
||||
int cols;
|
||||
/** table dimensions */
|
||||
int rows;
|
||||
|
||||
/** These are stored here only while this workspace is _not_ shown
|
||||
* (see show_workspace()) */
|
||||
int current_row;
|
||||
/** These are stored here only while this workspace is _not_ shown
|
||||
* (see show_workspace()) */
|
||||
int current_col;
|
||||
|
||||
/** Should clients on this workspace be automatically floating? */
|
||||
bool auto_float;
|
||||
/** Are the floating clients on this workspace currently hidden? */
|
||||
bool floating_hidden;
|
||||
|
||||
/** The name of the RandR output this screen should be on */
|
||||
char *preferred_output;
|
||||
|
||||
/** True if any client on this workspace has its urgent flag set */
|
||||
bool urgent;
|
||||
|
||||
/** the client who is started in fullscreen mode on this workspace,
|
||||
* NULL if there is none */
|
||||
Client *fullscreen_client;
|
||||
|
||||
/** The focus stack contains the clients in the correct order of focus
|
||||
so that the focus can be reverted correctly when a client is
|
||||
closed */
|
||||
SLIST_HEAD(focus_stack_head, Client) focus_stack;
|
||||
|
||||
/** This tail queue contains the floating clients in order of when
|
||||
* they were first set to floating (new floating clients are just
|
||||
* appended) */
|
||||
TAILQ_HEAD(floating_clients_head, Client) floating_clients;
|
||||
|
||||
/** Backpointer to the output this workspace is on */
|
||||
Output *output;
|
||||
|
||||
/** This is a two-dimensional dynamic array of
|
||||
* Container-pointers. I’ve always wanted to be a three-star
|
||||
* programmer :) */
|
||||
Container ***table;
|
||||
|
||||
/** width_factor and height_factor contain the amount of space
|
||||
* (percentage) a column/row has of all the space which is available
|
||||
* for resized windows. This ensures that non-resized windows (newly
|
||||
* opened, for example) have the same size as always */
|
||||
float *width_factor;
|
||||
float *height_factor;
|
||||
|
||||
TAILQ_ENTRY(Workspace) workspaces;
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds a keybinding, consisting of a keycode combined with modifiers and the
|
||||
* command which is executed as soon as the key is pressed (see src/command.c)
|
||||
|
@ -329,172 +207,6 @@ struct Font {
|
|||
TAILQ_ENTRY(Font) fonts;
|
||||
};
|
||||
|
||||
/**
|
||||
* A client is X11-speak for a window.
|
||||
*
|
||||
*/
|
||||
struct Client {
|
||||
/** initialized will be set to true if the client was fully
|
||||
* initialized by manage_window() and all functions can be used
|
||||
* normally */
|
||||
bool initialized;
|
||||
|
||||
/** if you set a client to floating and set it back to managed, it
|
||||
* does remember its old position and *tries* to get back there */
|
||||
Cell old_position;
|
||||
|
||||
/** Backpointer. A client is inside a container */
|
||||
Container *container;
|
||||
/** Because dock clients don’t have a container, we have this
|
||||
* workspace-backpointer */
|
||||
Workspace *workspace;
|
||||
|
||||
/** x, y, width, height of the frame */
|
||||
Rect rect;
|
||||
/** Position in floating mode and in tiling mode are saved
|
||||
* separately */
|
||||
Rect floating_rect;
|
||||
/** x, y, width, height of the child (relative to its frame) */
|
||||
Rect child_rect;
|
||||
|
||||
/** contains the size calculated from the hints set by the window or 0
|
||||
* if the client did not send any hints */
|
||||
int proportional_height;
|
||||
int proportional_width;
|
||||
|
||||
int base_height;
|
||||
int base_width;
|
||||
|
||||
/** The amount of pixels which X will draw around the client. */
|
||||
int border_width;
|
||||
|
||||
/** contains the minimum increment size as specified for the window
|
||||
* (in pixels). */
|
||||
int width_increment;
|
||||
int height_increment;
|
||||
|
||||
/** Height which was determined by reading the _NET_WM_STRUT_PARTIAL
|
||||
* top/bottom of the screen reservation */
|
||||
int desired_height;
|
||||
|
||||
/** Name (= window title) */
|
||||
char *name;
|
||||
/** name_len stores the real string length (glyphs) of the window
|
||||
* title if the client uses _NET_WM_NAME. Otherwise, it is set to -1
|
||||
* to indicate that name should be just passed to X as 8-bit string
|
||||
* and therefore will not be rendered correctly. This behaviour is to
|
||||
* support legacy applications which do not set _NET_WM_NAME */
|
||||
int name_len;
|
||||
/** This will be set to true as soon as the first _NET_WM_NAME comes
|
||||
* in. If set to true, legacy window names are ignored. */
|
||||
bool uses_net_wm_name;
|
||||
|
||||
/** Holds the WM_CLASS (which consists of two strings, the instance
|
||||
* and the class), useful for matching the client in commands */
|
||||
char *window_class_instance;
|
||||
char *window_class_class;
|
||||
|
||||
/** Holds the client’s mark, for vim-like jumping */
|
||||
char *mark;
|
||||
|
||||
/** Holds the xcb_window_t (just an ID) for the leader window (logical
|
||||
* parent for toolwindows and similar floating windows) */
|
||||
xcb_window_t leader;
|
||||
|
||||
/** fullscreen is pretty obvious */
|
||||
bool fullscreen;
|
||||
|
||||
/** floating? (= not in tiling layout) This cannot be simply a bool
|
||||
* because we want to keep track of whether the status was set by the
|
||||
* application (by setting WM_CLASS to tools for example) or by the
|
||||
* user. The user’s choice overwrites automatic mode, of course. The
|
||||
* order of the values is important because we check with >=
|
||||
* FLOATING_AUTO_ON if a client is floating. */
|
||||
enum { FLOATING_AUTO_OFF = 0, FLOATING_USER_OFF = 1, FLOATING_AUTO_ON = 2, FLOATING_USER_ON = 3 } floating;
|
||||
|
||||
/** Ensure TITLEBAR_TOP maps to 0 because we use calloc for
|
||||
* initialization later */
|
||||
enum { TITLEBAR_TOP = 0, TITLEBAR_LEFT, TITLEBAR_RIGHT, TITLEBAR_BOTTOM, TITLEBAR_OFF } titlebar_position;
|
||||
|
||||
/** Contains a bool specifying whether this window should not be drawn
|
||||
* with the usual decorations */
|
||||
bool borderless;
|
||||
|
||||
/** If a client is set as a dock, it is placed at the very bottom of
|
||||
* the screen and its requested size is used */
|
||||
bool dock;
|
||||
|
||||
/** True if the client set the urgency flag in its WM_HINTS property */
|
||||
bool urgent;
|
||||
|
||||
/* After leaving fullscreen mode, a client needs to be reconfigured
|
||||
* (configuration = setting X, Y, width and height). By setting the
|
||||
* force_reconfigure flag, render_layout() will reconfigure the
|
||||
* client. */
|
||||
bool force_reconfigure;
|
||||
|
||||
/* When reparenting a window, an unmap-notify is sent. As we delete
|
||||
* windows when they’re unmapped, we need to ignore that
|
||||
* one. Therefore, this flag is set when reparenting. */
|
||||
bool awaiting_useless_unmap;
|
||||
|
||||
/* XCB contexts */
|
||||
xcb_window_t frame; /**< Our window: The frame around the
|
||||
* client */
|
||||
xcb_gcontext_t titlegc; /**< The titlebar’s graphic context
|
||||
* inside the frame */
|
||||
xcb_window_t child; /**< The client’s window */
|
||||
|
||||
/** The following entry provides the necessary list pointers to use
|
||||
* Client with LIST_* macros */
|
||||
CIRCLEQ_ENTRY(Client) clients;
|
||||
SLIST_ENTRY(Client) dock_clients;
|
||||
SLIST_ENTRY(Client) focus_clients;
|
||||
TAILQ_ENTRY(Client) floating_clients;
|
||||
};
|
||||
|
||||
/**
|
||||
* A container is either in default, stacking or tabbed mode. There is one for
|
||||
* each cell of the table.
|
||||
*
|
||||
*/
|
||||
struct Container {
|
||||
/* Those are speaking for themselves: */
|
||||
Client *currently_focused;
|
||||
int colspan;
|
||||
int rowspan;
|
||||
|
||||
/* Position of the container inside our table */
|
||||
int row;
|
||||
int col;
|
||||
/* Xinerama: X/Y of the container */
|
||||
int x;
|
||||
int y;
|
||||
/* Width/Height of the container. Changeable by the user */
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/* When in stacking mode, we draw the titlebars of each client onto a
|
||||
* separate window */
|
||||
struct Stack_Window stack_win;
|
||||
|
||||
/* Backpointer to the workspace this container is in */
|
||||
Workspace *workspace;
|
||||
|
||||
/* Ensure MODE_DEFAULT maps to 0 because we use calloc for
|
||||
* initialization later */
|
||||
enum { MODE_DEFAULT = 0, MODE_STACK, MODE_TABBED } mode;
|
||||
|
||||
/* When in stacking, one can either have unlimited windows inside the
|
||||
* container or set a limit for the rows or columns the stack window
|
||||
* should display to use the screen more efficiently. */
|
||||
enum { STACK_LIMIT_NONE = 0, STACK_LIMIT_COLS, STACK_LIMIT_ROWS } stack_limit;
|
||||
|
||||
/* The number of columns or rows to limit to, see stack_limit */
|
||||
int stack_limit_value;
|
||||
|
||||
CIRCLEQ_HEAD(client_head, Client) clients;
|
||||
};
|
||||
|
||||
/**
|
||||
* An Output is a physical output on your graphics driver. Outputs which
|
||||
|
@ -518,9 +230,6 @@ struct xoutput {
|
|||
bool changed;
|
||||
bool to_be_disabled;
|
||||
|
||||
/** Current workspace selected on this virtual screen */
|
||||
Workspace *current_workspace;
|
||||
|
||||
/** x, y, width, height */
|
||||
Rect rect;
|
||||
|
||||
|
@ -535,4 +244,85 @@ struct xoutput {
|
|||
TAILQ_ENTRY(xoutput) outputs;
|
||||
};
|
||||
|
||||
struct Window {
|
||||
xcb_window_t id;
|
||||
|
||||
const char *class;
|
||||
};
|
||||
|
||||
struct Match {
|
||||
enum { M_WINDOW, M_CON } what;
|
||||
|
||||
char *title;
|
||||
int title_len;
|
||||
char *application;
|
||||
char *class;
|
||||
char *instance;
|
||||
xcb_window_t id;
|
||||
bool floating;
|
||||
|
||||
enum { M_GLOBAL, M_OUTPUT, M_WORKSPACE } levels;
|
||||
|
||||
enum { M_USER, M_RESTART } source;
|
||||
|
||||
/* wo das fenster eingefügt werden soll. bei here wird es direkt
|
||||
* diesem Con zugewiesen, also layout saving. bei active ist es
|
||||
* ein assignment, welches an der momentan fokussierten stelle einfügt */
|
||||
enum { M_HERE, M_ACTIVE } insert_where;
|
||||
|
||||
TAILQ_ENTRY(Match) matches;
|
||||
};
|
||||
|
||||
struct Con {
|
||||
bool mapped;
|
||||
enum { CT_ROOT = 0, CT_OUTPUT = 1, CT_CON = 2, CT_FLOATING_CON = 3 } type;
|
||||
orientation_t orientation;
|
||||
struct Con *parent;
|
||||
/* parent before setting it to floating */
|
||||
struct Con *old_parent;
|
||||
|
||||
struct Rect rect;
|
||||
struct Rect window_rect;
|
||||
struct Rect deco_rect;
|
||||
|
||||
char *name;
|
||||
|
||||
double percent;
|
||||
|
||||
struct Window *window;
|
||||
|
||||
/* ids/gc for the frame window */
|
||||
xcb_window_t frame;
|
||||
xcb_gcontext_t gc;
|
||||
|
||||
/* Only workspace-containers can have floating clients */
|
||||
TAILQ_HEAD(floating_head, Con) floating_head;
|
||||
|
||||
TAILQ_HEAD(nodes_head, Con) nodes_head;
|
||||
TAILQ_HEAD(focus_head, Con) focus_head;
|
||||
|
||||
TAILQ_HEAD(swallow_head, Match) swallow_head;
|
||||
|
||||
enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode;
|
||||
enum { L_DEFAULT = 0, L_STACKED = 1, L_TABBED = 2 } layout;
|
||||
/** floating? (= not in tiling layout) This cannot be simply a bool
|
||||
* because we want to keep track of whether the status was set by the
|
||||
* application (by setting _NET_WM_WINDOW_TYPE appropriately) or by the
|
||||
* user. The user’s choice overwrites automatic mode, of course. The
|
||||
* order of the values is important because we check with >=
|
||||
* FLOATING_AUTO_ON if a client is floating. */
|
||||
enum {
|
||||
FLOATING_AUTO_OFF = 0,
|
||||
FLOATING_USER_OFF = 1,
|
||||
FLOATING_AUTO_ON = 2,
|
||||
FLOATING_USER_ON = 3
|
||||
} floating;
|
||||
|
||||
|
||||
TAILQ_ENTRY(Con) nodes;
|
||||
TAILQ_ENTRY(Con) focused;
|
||||
TAILQ_ENTRY(Con) all_cons;
|
||||
TAILQ_ENTRY(Con) floating_windows;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,14 +11,15 @@
|
|||
#ifndef _FLOATING_H
|
||||
#define _FLOATING_H
|
||||
|
||||
#include "tree.h"
|
||||
|
||||
/** Callback for dragging */
|
||||
typedef void(*callback_t)(xcb_connection_t*, Client*, Rect*, uint32_t, uint32_t, void*);
|
||||
typedef void(*callback_t)(Con*, Rect*, uint32_t, uint32_t, void*);
|
||||
|
||||
/** Macro to create a callback function for dragging */
|
||||
#define DRAGGING_CB(name) \
|
||||
static void name(xcb_connection_t *conn, Client *client, \
|
||||
Rect *old_rect, uint32_t new_x, uint32_t new_y, \
|
||||
void *extra)
|
||||
static void name(Con *con, Rect *old_rect, uint32_t new_x, \
|
||||
uint32_t new_y, void *extra)
|
||||
|
||||
/** On which border was the dragging initiated? */
|
||||
typedef enum { BORDER_LEFT = (1 << 0),
|
||||
|
@ -36,9 +37,9 @@ typedef enum { BORDER_LEFT = (1 << 0),
|
|||
* the user.
|
||||
*
|
||||
*/
|
||||
void toggle_floating_mode(xcb_connection_t *conn, Client *client,
|
||||
bool automatic);
|
||||
void toggle_floating_mode(Con *con, bool automatic);
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Removes the floating client from its workspace and attaches it to the new
|
||||
* workspace. This is centralized here because it may happen if you move it
|
||||
|
@ -56,13 +57,14 @@ void floating_assign_to_workspace(Client *client, Workspace *new_workspace);
|
|||
int floating_border_click(xcb_connection_t *conn, Client *client,
|
||||
xcb_button_press_event_t *event);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* Called when the user clicked on the titlebar of a floating window.
|
||||
* Calls the drag_pointer function with the drag_window callback
|
||||
*
|
||||
*/
|
||||
void floating_drag_window(xcb_connection_t *conn, Client *client,
|
||||
xcb_button_press_event_t *event);
|
||||
void floating_drag_window(Con *con, xcb_button_press_event_t *event);
|
||||
#if 0
|
||||
|
||||
/**
|
||||
* Called when the user clicked on a floating window while holding the
|
||||
|
@ -97,6 +99,7 @@ void floating_move(xcb_connection_t *conn, Client *currently_focused,
|
|||
*/
|
||||
void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* 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
|
||||
|
@ -105,7 +108,7 @@ void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace);
|
|||
* the event and the new coordinates (x, y).
|
||||
*
|
||||
*/
|
||||
void drag_pointer(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event,
|
||||
void drag_pointer(Con *con, xcb_button_press_event_t *event,
|
||||
xcb_window_t confine_to, border_t border, callback_t callback,
|
||||
void *extra);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
int handle_key_press(void *ignored, xcb_connection_t *conn,
|
||||
xcb_key_press_event_t *event);
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* When the user moves the mouse pointer onto a window, this callback gets
|
||||
* called.
|
||||
|
@ -200,5 +201,6 @@ int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state,
|
|||
int handle_clientleader_change(void *data, xcb_connection_t *conn,
|
||||
uint8_t state, xcb_window_t window,
|
||||
xcb_atom_t name, xcb_get_property_reply_t *prop);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#define NUM_ATOMS 21
|
||||
|
||||
extern xcb_connection_t *global_conn;
|
||||
extern xcb_connection_t *conn;
|
||||
extern xcb_key_symbols_t *keysyms;
|
||||
extern char **start_argv;
|
||||
extern Display *xkbdpy;
|
||||
|
|
|
@ -27,13 +27,8 @@
|
|||
#define I3_IPC_MESSAGE_TYPE_COMMAND 0
|
||||
|
||||
/** Requests the current workspaces from i3 */
|
||||
#define I3_IPC_MESSAGE_TYPE_GET_WORKSPACES 1
|
||||
#define I3_IPC_MESSAGE_TYPE_GET_TREE 1
|
||||
|
||||
/** Subscribe to the specified events */
|
||||
#define I3_IPC_MESSAGE_TYPE_SUBSCRIBE 2
|
||||
|
||||
/** Requests the current outputs from i3 */
|
||||
#define I3_IPC_MESSAGE_TYPE_GET_OUTPUTS 3
|
||||
|
||||
/*
|
||||
* Messages from i3 to clients
|
||||
|
@ -44,13 +39,7 @@
|
|||
#define I3_IPC_REPLY_TYPE_COMMAND 0
|
||||
|
||||
/** Workspaces reply type */
|
||||
#define I3_IPC_REPLY_TYPE_WORKSPACES 1
|
||||
|
||||
/** Subscription reply type */
|
||||
#define I3_IPC_REPLY_TYPE_SUBSCRIBE 2
|
||||
|
||||
/** Outputs reply type */
|
||||
#define I3_IPC_REPLY_TYPE_OUTPUTS 3
|
||||
#define I3_IPC_REPLY_TYPE_TREE 1
|
||||
|
||||
/*
|
||||
* Events from i3 to clients. Events have the first bit set high.
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
#define _IPC_H
|
||||
|
||||
#include <ev.h>
|
||||
#include <stdbool.h>
|
||||
#include <yajl/yajl_gen.h>
|
||||
#include <yajl/yajl_parse.h>
|
||||
|
||||
#include "data.h"
|
||||
#include "tree.h"
|
||||
|
||||
#include "i3/ipc.h"
|
||||
|
||||
|
@ -74,4 +80,6 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa
|
|||
*/
|
||||
void ipc_shutdown();
|
||||
|
||||
void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef _LOAD_LAYOUT_H
|
||||
#define _LOAD_LAYOUT_H
|
||||
|
||||
void tree_append_json(const char *filename);
|
||||
|
||||
#endif
|
|
@ -20,8 +20,7 @@
|
|||
* manage them
|
||||
*
|
||||
*/
|
||||
void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t
|
||||
*prophs, xcb_window_t root);
|
||||
void manage_existing_windows(xcb_window_t root);
|
||||
|
||||
/**
|
||||
* Restores the geometry of each window by reparenting it to the root window
|
||||
|
@ -31,17 +30,17 @@ void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t
|
|||
* side-effects which are to be expected when continuing to run i3.
|
||||
*
|
||||
*/
|
||||
void restore_geometry(xcb_connection_t *conn);
|
||||
void restore_geometry();
|
||||
|
||||
/**
|
||||
* Do some sanity checks and then reparent the window.
|
||||
*
|
||||
*/
|
||||
void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
|
||||
xcb_window_t window,
|
||||
void manage_window(xcb_window_t window,
|
||||
xcb_get_window_attributes_cookie_t cookie,
|
||||
bool needs_to_be_mapped);
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* reparent_window() gets called when a new window was opened and becomes a
|
||||
* child of the root window, or it gets called by us when we manage the
|
||||
|
@ -56,3 +55,4 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
|
|||
uint32_t border_width);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -22,7 +22,7 @@ extern struct outputs_head outputs;
|
|||
* XRandR information to setup workspaces for each screen.
|
||||
*
|
||||
*/
|
||||
void initialize_randr(xcb_connection_t *conn, int *event_base);
|
||||
void randr_init(int *event_base);
|
||||
|
||||
/**
|
||||
* Disables RandR support by creating exactly one output with the size of the
|
||||
|
@ -35,13 +35,13 @@ void disable_randr(xcb_connection_t *conn);
|
|||
* Initializes the specified output, assigning the specified workspace to it.
|
||||
*
|
||||
*/
|
||||
void initialize_output(xcb_connection_t *conn, Output *output, Workspace *workspace);
|
||||
//void initialize_output(xcb_connection_t *conn, Output *output, Workspace *workspace);
|
||||
|
||||
/**
|
||||
* (Re-)queries the outputs via RandR and stores them in the list of outputs.
|
||||
*
|
||||
*/
|
||||
void randr_query_outputs(xcb_connection_t *conn);
|
||||
void randr_query_outputs();
|
||||
|
||||
/**
|
||||
* Returns the first output which is active.
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*/
|
||||
|
||||
#ifndef _RENDER_H
|
||||
#define _RENDER_H
|
||||
|
||||
void render_con(Con *con);
|
||||
|
||||
#endif
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* vim:ts=8:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
*
|
||||
* (c) 2009 Michael Stapelberg and contributors
|
||||
*
|
||||
* See file LICENSE for license information.
|
||||
*
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "data.h"
|
||||
|
||||
#ifndef _TABLE_H
|
||||
#define _TABLE_H
|
||||
|
||||
#define CUR_TABLE (c_ws->table)
|
||||
#define CUR_CELL (CUR_TABLE[current_col][current_row])
|
||||
|
||||
extern Workspace *c_ws;
|
||||
extern TAILQ_HEAD(workspaces_head, Workspace) *workspaces;
|
||||
//extern int num_workspaces;
|
||||
extern int current_col;
|
||||
extern int current_row;
|
||||
|
||||
/** Initialize table */
|
||||
void init_table();
|
||||
|
||||
/** Add one row to the table */
|
||||
void expand_table_rows(Workspace *workspace);
|
||||
|
||||
/** Adds one row at the head of the table */
|
||||
void expand_table_rows_at_head(Workspace *workspace);
|
||||
|
||||
/** Add one column to the table */
|
||||
void expand_table_cols(Workspace *workspace);
|
||||
|
||||
/**
|
||||
* Inserts one column at the table’s head
|
||||
*
|
||||
*/
|
||||
void expand_table_cols_at_head(Workspace *workspace);
|
||||
|
||||
/**
|
||||
* Performs simple bounds checking for the given column/row
|
||||
*
|
||||
*/
|
||||
bool cell_exists(Workspace *ws, int col, int row);
|
||||
|
||||
/**
|
||||
* Shrinks the table by "compacting" it, that is, removing completely empty
|
||||
* rows/columns
|
||||
*
|
||||
*/
|
||||
void cleanup_table(xcb_connection_t *conn, Workspace *workspace);
|
||||
|
||||
/**
|
||||
* Fixes col/rowspan (makes sure there are no overlapping windows)
|
||||
*
|
||||
*/
|
||||
void fix_colrowspan(xcb_connection_t *conn, Workspace *workspace);
|
||||
|
||||
/**
|
||||
* Prints the table’s contents in human-readable form for debugging
|
||||
*
|
||||
*/
|
||||
void dump_table(xcb_connection_t *conn, Workspace *workspace);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*/
|
||||
|
||||
#ifndef _TREE_H
|
||||
#define _TREE_H
|
||||
|
||||
extern Con *croot;
|
||||
/* TODO: i am not sure yet how much access to the focused container should
|
||||
* be permitted to source files */
|
||||
extern Con *focused;
|
||||
TAILQ_HEAD(all_cons_head, Con);
|
||||
extern struct all_cons_head all_cons;
|
||||
|
||||
void tree_init();
|
||||
Con *tree_open_con(Con *con);
|
||||
void tree_split(Con *con, orientation_t orientation);
|
||||
void con_focus(Con *con);
|
||||
void level_up();
|
||||
void level_down();
|
||||
void tree_render();
|
||||
void tree_close_con();
|
||||
void tree_next(char way, orientation_t orientation);
|
||||
void tree_move(char way, orientation_t orientation);
|
||||
void tree_close(Con *con);
|
||||
bool tree_restore();
|
||||
|
||||
#endif
|
|
@ -62,6 +62,13 @@ void *smalloc(size_t size);
|
|||
*/
|
||||
void *scalloc(size_t size);
|
||||
|
||||
/**
|
||||
* Safe-wrapper around realloc which exits if realloc returns NULL (meaning
|
||||
* that there is no more memory available).
|
||||
*
|
||||
*/
|
||||
void *srealloc(void *ptr, size_t size);
|
||||
|
||||
/**
|
||||
* Safe-wrapper around strdup which exits if malloc returns NULL (meaning that
|
||||
* there is no more memory available)
|
||||
|
@ -118,6 +125,7 @@ void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie,
|
|||
*/
|
||||
char *convert_utf8_to_ucs2(char *input, int *real_strlen);
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Returns the client which comes next in focus stack (= was selected before) for
|
||||
* the given container, optionally excluding the given client.
|
||||
|
@ -125,7 +133,9 @@ char *convert_utf8_to_ucs2(char *input, int *real_strlen);
|
|||
*/
|
||||
Client *get_last_focused_client(xcb_connection_t *conn, Container *container,
|
||||
Client *exclude);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Sets the given client as focused by updating the data structures correctly,
|
||||
* updating the X input focus and finally re-decorating both windows (to
|
||||
|
@ -156,6 +166,7 @@ void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode);
|
|||
*/
|
||||
Client *get_matching_client(xcb_connection_t *conn,
|
||||
const char *window_classtitle, Client *specific);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Restart i3 in-place
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <xcb/xcb.h>
|
||||
|
||||
#include "data.h"
|
||||
#include "tree.h"
|
||||
#include "randr.h"
|
||||
|
||||
#ifndef _WORKSPACE_H
|
||||
|
@ -22,8 +23,9 @@
|
|||
* memory and initializing the data structures correctly).
|
||||
*
|
||||
*/
|
||||
Workspace *workspace_get(int number);
|
||||
Con *workspace_get(const char *num);
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Sets the name (or just its number) for the given workspace. This has to
|
||||
* be called for every workspace as the rendering function
|
||||
|
@ -41,9 +43,11 @@ void workspace_set_name(Workspace *ws, const char *name);
|
|||
*/
|
||||
bool workspace_is_visible(Workspace *ws);
|
||||
|
||||
#endif
|
||||
/** Switches to the given workspace */
|
||||
void workspace_show(xcb_connection_t *conn, int workspace);
|
||||
void workspace_show(const char *num);
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Assigns the given workspace to the given screen by correctly updating its
|
||||
* state and reconfiguring all the clients on this workspace.
|
||||
|
@ -106,5 +110,5 @@ int workspace_width(Workspace *ws);
|
|||
*
|
||||
*/
|
||||
int workspace_height(Workspace *ws);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*/
|
||||
|
||||
#ifndef _X_H
|
||||
#define _X_H
|
||||
|
||||
void x_con_init(Con *con);
|
||||
void x_con_kill(Con *con);
|
||||
void x_window_kill(xcb_window_t window);
|
||||
void x_draw_decoration(Con *con);
|
||||
void x_push_changes(Con *con);
|
||||
void x_raise_con(Con *con);
|
||||
|
||||
#endif
|
|
@ -85,7 +85,7 @@ i3Font *load_font(xcb_connection_t *conn, const char *pattern);
|
|||
* validity. This has to be done by the caller.
|
||||
*
|
||||
*/
|
||||
uint32_t get_colorpixel(xcb_connection_t *conn, char *hex);
|
||||
uint32_t get_colorpixel(char *hex);
|
||||
|
||||
/**
|
||||
* Convenience wrapper around xcb_create_window which takes care of depth,
|
||||
|
@ -127,12 +127,14 @@ void xcb_draw_rect(xcb_connection_t *conn, xcb_drawable_t drawable,
|
|||
*/
|
||||
void fake_configure_notify(xcb_connection_t *conn, Rect r, xcb_window_t window);
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Generates a configure_notify_event with absolute coordinates (relative to
|
||||
* the X root window, not to the client’s frame) for the given client.
|
||||
*
|
||||
*/
|
||||
void fake_absolute_configure_notify(xcb_connection_t *conn, Client *client);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Finds out which modifier mask is the one for numlock, as the user may
|
||||
|
|
|
@ -18,6 +18,6 @@
|
|||
* Xinerama information to setup workspaces for each screen.
|
||||
*
|
||||
*/
|
||||
void initialize_xinerama(xcb_connection_t *conn);
|
||||
void xinerama_init();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -94,12 +94,12 @@ new_container { return TOKNEWCONTAINER; }
|
|||
new_window { return TOKNEWWINDOW; }
|
||||
focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; }
|
||||
workspace_bar { return TOKWORKSPACEBAR; }
|
||||
default { yylval.number = MODE_DEFAULT; return TOKCONTAINERMODE; }
|
||||
stacking { yylval.number = MODE_STACK; return TOKCONTAINERMODE; }
|
||||
tabbed { yylval.number = MODE_TABBED; return TOKCONTAINERMODE; }
|
||||
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; }
|
||||
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; }
|
||||
|
|
|
@ -3,25 +3,12 @@
|
|||
* 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"
|
||||
#include "log.h"
|
||||
#include "all.h"
|
||||
|
||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
extern int yylex(struct context *context);
|
||||
|
@ -372,6 +359,7 @@ new_container:
|
|||
DLOG("new containers will be in mode %d\n", $<number>3);
|
||||
config.container_mode = $<number>3;
|
||||
|
||||
#if 0
|
||||
/* 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
|
||||
|
@ -388,6 +376,7 @@ new_container:
|
|||
ws->table[0][0],
|
||||
config.container_mode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
| TOKNEWCONTAINER WHITESPACE TOKSTACKLIMIT WHITESPACE TOKSTACKLIMIT WHITESPACE NUMBER
|
||||
{
|
||||
|
@ -395,6 +384,7 @@ new_container:
|
|||
config.container_stack_limit = $<number>5;
|
||||
config.container_stack_limit_value = $<number>7;
|
||||
|
||||
#if 0
|
||||
/* See the comment above */
|
||||
Workspace *ws;
|
||||
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
||||
|
@ -404,6 +394,7 @@ new_container:
|
|||
con->stack_limit = config.container_stack_limit;
|
||||
con->stack_limit_value = config.container_stack_limit_value;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -454,12 +445,14 @@ workspace:
|
|||
if (ws_num < 1) {
|
||||
DLOG("Invalid workspace assignment, workspace number %d out of range\n", ws_num);
|
||||
} else {
|
||||
#if 0
|
||||
Workspace *ws = workspace_get(ws_num - 1);
|
||||
ws->preferred_output = $<string>7;
|
||||
if ($<string>8 != NULL) {
|
||||
workspace_set_name(ws, $<string>8);
|
||||
free($<string>8);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
| TOKWORKSPACE WHITESPACE NUMBER WHITESPACE workspace_name
|
||||
|
@ -469,10 +462,12 @@ workspace:
|
|||
DLOG("Invalid workspace assignment, workspace number %d out of range\n", ws_num);
|
||||
} else {
|
||||
DLOG("workspace name to: %s\n", $<string>5);
|
||||
#if 0
|
||||
if ($<string>5 != NULL) {
|
||||
workspace_set_name(workspace_get(ws_num - 1), $<string>5);
|
||||
free($<string>5);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
;
|
||||
|
@ -584,7 +579,7 @@ colorpixel:
|
|||
char *hex;
|
||||
if (asprintf(&hex, "#%s", $<string>2) == -1)
|
||||
die("asprintf()");
|
||||
$<number>$ = get_colorpixel(global_conn, hex);
|
||||
$<number>$ = get_colorpixel(hex);
|
||||
free(hex);
|
||||
}
|
||||
;
|
||||
|
|
69
src/click.c
69
src/click.c
|
@ -11,34 +11,17 @@
|
|||
* 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"
|
||||
#include "log.h"
|
||||
#include "randr.h"
|
||||
#include "all.h"
|
||||
|
||||
#if 0
|
||||
static struct Stack_Window *get_stack_window(xcb_window_t window_id) {
|
||||
struct Stack_Window *current;
|
||||
|
||||
|
@ -251,50 +234,63 @@ static bool floating_mod_on_tiled_client(xcb_connection_t *conn, Client *client,
|
|||
|
||||
return resize_graphical_handler(conn, ws, first, second, orientation, event);
|
||||
}
|
||||
#endif
|
||||
|
||||
int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_event_t *event) {
|
||||
DLOG("Button %d pressed\n", event->state);
|
||||
/* This was either a focus for a client’s parent (= titlebar)… */
|
||||
Client *client = table_get(&by_child, event->event);
|
||||
Con *con;
|
||||
LOG("Button %d pressed\n", event->state);
|
||||
|
||||
con = con_by_window_id(event->event);
|
||||
bool border_click = false;
|
||||
if (client == NULL) {
|
||||
client = table_get(&by_parent, event->event);
|
||||
if (con == NULL) {
|
||||
con = con_by_frame_id(event->event);
|
||||
border_click = true;
|
||||
}
|
||||
//if (con && con->type == CT_FLOATING_CON)
|
||||
//con = TAILQ_FIRST(&(con->nodes_head));
|
||||
|
||||
/* 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) == config.floating_modifier) {
|
||||
if (client == NULL) {
|
||||
DLOG("Not handling, floating_modifier was pressed and no client found\n");
|
||||
//if (config.floating_modifier != 0 &&
|
||||
//(event->state & config.floating_modifier) == config.floating_modifier) {
|
||||
if (con == NULL) {
|
||||
LOG("Not handling, floating_modifier was pressed and no client found\n");
|
||||
return 1;
|
||||
}
|
||||
if (client->fullscreen) {
|
||||
DLOG("Not handling, client is in fullscreen mode\n");
|
||||
#if 0
|
||||
if (con->fullscreen) {
|
||||
LOG("Not handling, client is in fullscreen mode\n");
|
||||
return 1;
|
||||
}
|
||||
if (client_is_floating(client)) {
|
||||
DLOG("button %d pressed\n", event->detail);
|
||||
#endif
|
||||
if (con->type == CT_FLOATING_CON) {
|
||||
LOG("button %d pressed\n", event->detail);
|
||||
if (event->detail == 1) {
|
||||
DLOG("left mouse button, dragging\n");
|
||||
floating_drag_window(conn, client, event);
|
||||
} else if (event->detail == 3) {
|
||||
LOG("left mouse button, dragging\n");
|
||||
floating_drag_window(con, event);
|
||||
}
|
||||
#if 0
|
||||
else if (event->detail == 3) {
|
||||
bool proportional = (event->state & BIND_SHIFT);
|
||||
DLOG("right mouse button\n");
|
||||
floating_resize_window(conn, client, proportional, event);
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (!floating_mod_on_tiled_client(conn, client, event)) {
|
||||
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
||||
xcb_flush(conn);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
//}
|
||||
|
||||
#if 0
|
||||
if (client == NULL) {
|
||||
/* The client was neither on a client’s titlebar nor on a client itself, maybe on a stack_window? */
|
||||
if (button_press_stackwin(conn, event))
|
||||
|
@ -405,4 +401,5 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
|
|||
}
|
||||
|
||||
return resize_graphical_handler(conn, ws, first, second, orientation, event);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
* © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
|
||||
*
|
||||
* con.c contains all functions which deal with containers directly (creating
|
||||
* containers, searching containers, getting specific properties from
|
||||
* containers, …).
|
||||
*
|
||||
*/
|
||||
#include "all.h"
|
||||
|
||||
char *colors[] = {
|
||||
"#ff0000",
|
||||
"#00FF00",
|
||||
"#0000FF",
|
||||
"#ff00ff",
|
||||
"#00ffff",
|
||||
"#ffff00",
|
||||
"#aa0000",
|
||||
"#00aa00",
|
||||
"#0000aa",
|
||||
"#aa00aa"
|
||||
};
|
||||
|
||||
|
||||
Con *con_new(Con *parent) {
|
||||
Con *new = scalloc(sizeof(Con));
|
||||
TAILQ_INSERT_TAIL(&all_cons, new, all_cons);
|
||||
new->type = CT_CON;
|
||||
new->name = strdup("");
|
||||
static int cnt = 0;
|
||||
LOG("opening window %d\n", cnt);
|
||||
|
||||
/* TODO: remove window coloring after test-phase */
|
||||
LOG("color %s\n", colors[cnt]);
|
||||
new->name = strdup(colors[cnt]);
|
||||
uint32_t cp = get_colorpixel(colors[cnt]);
|
||||
cnt++;
|
||||
if ((cnt % (sizeof(colors) / sizeof(char*))) == 0)
|
||||
cnt = 0;
|
||||
|
||||
x_con_init(new);
|
||||
|
||||
xcb_change_window_attributes(conn, new->frame, XCB_CW_BACK_PIXEL, &cp);
|
||||
|
||||
TAILQ_INIT(&(new->floating_head));
|
||||
TAILQ_INIT(&(new->nodes_head));
|
||||
TAILQ_INIT(&(new->focus_head));
|
||||
TAILQ_INIT(&(new->swallow_head));
|
||||
|
||||
if (parent != NULL)
|
||||
con_attach(new, parent);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
void con_attach(Con *con, Con *parent) {
|
||||
con->parent = parent;
|
||||
TAILQ_INSERT_TAIL(&(parent->nodes_head), con, nodes);
|
||||
/* We insert to the TAIL because con_focus() will correct this.
|
||||
* This way, we have the option to insert Cons without having
|
||||
* to focus them. */
|
||||
TAILQ_INSERT_TAIL(&(parent->focus_head), con, focused);
|
||||
}
|
||||
|
||||
void con_detach(Con *con) {
|
||||
if (con->type == CT_FLOATING_CON) {
|
||||
/* TODO: remove */
|
||||
} else {
|
||||
TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes);
|
||||
TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true when this node is a leaf node (has no children)
|
||||
*
|
||||
*/
|
||||
bool con_is_leaf(Con *con) {
|
||||
return TAILQ_EMPTY(&(con->nodes_head));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this node accepts a window (if the node swallows windows,
|
||||
* it might already have swallowed enough and cannot hold any more).
|
||||
*
|
||||
*/
|
||||
bool con_accepts_window(Con *con) {
|
||||
/* 1: workspaces never accept direct windows */
|
||||
if (con->parent->type == CT_OUTPUT)
|
||||
return false;
|
||||
|
||||
/* TODO: if this is a swallowing container, we need to check its max_clients */
|
||||
return (con->window == NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the output container (first container with CT_OUTPUT in hierarchy) this
|
||||
* node is on.
|
||||
*
|
||||
*/
|
||||
Con *con_get_output(Con *con) {
|
||||
Con *result = con;
|
||||
while (result != NULL && result->type != CT_OUTPUT)
|
||||
result = result->parent;
|
||||
/* We must be able to get an output because focus can never be set higher
|
||||
* in the tree (root node cannot be focused). */
|
||||
assert(result != NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the workspace container this node is on.
|
||||
*
|
||||
*/
|
||||
Con *con_get_workspace(Con *con) {
|
||||
Con *result = con;
|
||||
while (result != NULL && result->parent->type != CT_OUTPUT)
|
||||
result = result->parent;
|
||||
assert(result != NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the first fullscreen node below this node.
|
||||
*
|
||||
*/
|
||||
Con *con_get_fullscreen_con(Con *con) {
|
||||
Con *current;
|
||||
|
||||
LOG("looking for fullscreen node\n");
|
||||
/* TODO: is breadth-first-search really appropriate? (check as soon as
|
||||
* fullscreen levels and fullscreen for containers is implemented) */
|
||||
Con **queue = NULL;
|
||||
int queue_len = 0;
|
||||
TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
|
||||
queue_len++;
|
||||
queue = srealloc(queue, queue_len * sizeof(Con*));
|
||||
queue[queue_len-1] = current;
|
||||
}
|
||||
|
||||
while (queue_len > 0) {
|
||||
current = queue[queue_len-1];
|
||||
LOG("checking %p\n", current);
|
||||
if (current->fullscreen_mode != CF_NONE) {
|
||||
free(queue);
|
||||
return current;
|
||||
}
|
||||
LOG("deleting from queue\n");
|
||||
queue_len--;
|
||||
queue = realloc(queue, queue_len * sizeof(Con*));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the node is floating.
|
||||
*
|
||||
*/
|
||||
bool con_is_floating(Con *con) {
|
||||
assert(con != NULL);
|
||||
LOG("checking if con %p is floating\n", con);
|
||||
return (con->floating >= FLOATING_AUTO_ON);
|
||||
}
|
||||
|
||||
Con *con_by_window_id(xcb_window_t window) {
|
||||
Con *con;
|
||||
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||
if (con->window != NULL && con->window->id == window)
|
||||
return con;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Con *con_by_frame_id(xcb_window_t frame) {
|
||||
Con *con;
|
||||
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||
if (con->frame == frame)
|
||||
return con;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool match_matches_window(Match *match, i3Window *window) {
|
||||
/* TODO: pcre, full matching, … */
|
||||
if (match->class != NULL && strcasecmp(match->class, window->class) == 0) {
|
||||
LOG("match made by window class (%s)\n", window->class);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (match->id != XCB_NONE && window->id == match->id) {
|
||||
LOG("match made by window id (%d)\n", window->id);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG("window %d (%s) could not be matched\n", window->id, window->class);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the first container which wants to swallow this window
|
||||
* TODO: priority
|
||||
*
|
||||
*/
|
||||
Con *con_for_window(i3Window *window, Match **store_match) {
|
||||
Con *con;
|
||||
Match *match;
|
||||
LOG("searching con for window %p\n", window);
|
||||
LOG("class == %s\n", window->class);
|
||||
|
||||
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||
TAILQ_FOREACH(match, &(con->swallow_head), matches) {
|
||||
if (!match_matches_window(match, window))
|
||||
continue;
|
||||
if (store_match != NULL)
|
||||
*store_match = match;
|
||||
return con;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the percent attribute of the children of the given container. This
|
||||
* function needs to be called when a window is added or removed from a
|
||||
* container.
|
||||
*
|
||||
*/
|
||||
void con_fix_percent(Con *con, int action) {
|
||||
Con *child;
|
||||
int children = 0;
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
|
||||
children++;
|
||||
/* TODO: better document why this math works */
|
||||
double fix;
|
||||
if (action == WINDOW_ADD)
|
||||
fix = (1.0 - (1.0 / (children+1)));
|
||||
else
|
||||
fix = 1.0 / (1.0 - (1.0 / (children+1)));
|
||||
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||
if (child->percent <= 0.0)
|
||||
continue;
|
||||
child->percent *= fix;
|
||||
}
|
||||
}
|
55
src/config.c
55
src/config.c
|
@ -12,26 +12,12 @@
|
|||
* mode).
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <glob.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* We need Xlib for XStringToKeysym */
|
||||
#include <X11/Xlib.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
|
||||
#include "i3.h"
|
||||
#include "util.h"
|
||||
#include "config.h"
|
||||
#include "xcb.h"
|
||||
#include "table.h"
|
||||
#include "workspace.h"
|
||||
#include "log.h"
|
||||
#include "all.h"
|
||||
|
||||
Config config;
|
||||
struct modes_head modes;
|
||||
|
@ -40,20 +26,34 @@ struct modes_head modes;
|
|||
* This function resolves ~ in pathnames.
|
||||
*
|
||||
*/
|
||||
static char *glob_path(const char *path) {
|
||||
char *glob_path(const char *path) {
|
||||
static glob_t globbuf;
|
||||
if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0)
|
||||
die("glob() failed");
|
||||
char *result = sstrdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
|
||||
globfree(&globbuf);
|
||||
|
||||
/* If the file does not exist yet, we still may need to resolve tilde,
|
||||
* so call wordexp */
|
||||
if (strcmp(result, path) == 0) {
|
||||
wordexp_t we;
|
||||
wordexp(path, &we, WRDE_NOCMD);
|
||||
if (we.we_wordc > 0) {
|
||||
free(result);
|
||||
result = sstrdup(we.we_wordv[0]);
|
||||
}
|
||||
wordfree(&we);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Checks if the given path exists by calling stat().
|
||||
*
|
||||
*/
|
||||
static bool path_exists(const char *path) {
|
||||
bool path_exists(const char *path) {
|
||||
struct stat buf;
|
||||
return (stat(path, &buf) == 0);
|
||||
}
|
||||
|
@ -134,14 +134,6 @@ void translate_keysyms() {
|
|||
continue;
|
||||
}
|
||||
|
||||
#ifdef OLD_XCB_KEYSYMS_API
|
||||
bind->number_keycodes = 1;
|
||||
xcb_keycode_t code = xcb_key_symbols_get_keycode(keysyms, keysym);
|
||||
DLOG("Translated symbol \"%s\" to 1 keycode (%d)\n", bind->symbol, code);
|
||||
grab_keycode_for_binding(global_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) {
|
||||
|
@ -163,7 +155,6 @@ void translate_keysyms() {
|
|||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,9 +314,11 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
}
|
||||
|
||||
/* Clear workspace names */
|
||||
#if 0
|
||||
Workspace *ws;
|
||||
TAILQ_FOREACH(ws, workspaces, workspaces)
|
||||
workspace_set_name(ws, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
SLIST_INIT(&modes);
|
||||
|
@ -348,9 +341,9 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
/* Initialize default colors */
|
||||
#define INIT_COLOR(x, cborder, cbackground, ctext) \
|
||||
do { \
|
||||
x.border = get_colorpixel(conn, cborder); \
|
||||
x.background = get_colorpixel(conn, cbackground); \
|
||||
x.text = get_colorpixel(conn, ctext); \
|
||||
x.border = get_colorpixel(cborder); \
|
||||
x.background = get_colorpixel(cbackground); \
|
||||
x.text = get_colorpixel(ctext); \
|
||||
} while (0)
|
||||
|
||||
INIT_COLOR(config.client.focused, "#4c7899", "#285577", "#ffffff");
|
||||
|
@ -370,6 +363,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
|
||||
REQUIRED_OPTION(font);
|
||||
|
||||
#if 0
|
||||
/* Set an empty name for every workspace which got no name */
|
||||
Workspace *ws;
|
||||
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
||||
|
@ -384,4 +378,5 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
|
||||
workspace_set_name(ws, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
121
src/floating.c
121
src/floating.c
|
@ -10,38 +10,81 @@
|
|||
* src/floating.c: contains all functions for handling floating clients
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
|
||||
#include "i3.h"
|
||||
#include "config.h"
|
||||
#include "data.h"
|
||||
#include "util.h"
|
||||
#include "xcb.h"
|
||||
#include "debug.h"
|
||||
#include "layout.h"
|
||||
#include "client.h"
|
||||
#include "floating.h"
|
||||
#include "workspace.h"
|
||||
#include "log.h"
|
||||
#include "all.h"
|
||||
|
||||
extern xcb_connection_t *conn;
|
||||
|
||||
/*
|
||||
* Toggles floating mode for the given client.
|
||||
* Correctly takes care of the position/size (separately stored for tiling/floating mode)
|
||||
* and repositions/resizes/redecorates the client.
|
||||
* Toggles floating mode for the given container.
|
||||
*
|
||||
* If the automatic flag is set to true, this was an automatic update by a change of the
|
||||
* window class from the application which can be overwritten by the user.
|
||||
*
|
||||
*/
|
||||
void toggle_floating_mode(xcb_connection_t *conn, Client *client, bool automatic) {
|
||||
Container *con = client->container;
|
||||
i3Font *font = load_font(conn, config.font);
|
||||
void toggle_floating_mode(Con *con, bool automatic) {
|
||||
//i3Font *font = load_font(conn, config.font);
|
||||
|
||||
/* see if the client is already floating */
|
||||
if (con_is_floating(con)) {
|
||||
LOG("already floating, re-setting to tiling\n");
|
||||
assert(con->old_parent != NULL);
|
||||
|
||||
/* 1: detach from parent container */
|
||||
TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes);
|
||||
TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
|
||||
|
||||
/* 2: kill parent container */
|
||||
TAILQ_REMOVE(&(con->parent->parent->floating_head), con->parent, floating_windows);
|
||||
tree_close(con->parent);
|
||||
|
||||
/* 3: re-attach to previous parent */
|
||||
con->parent = con->old_parent;
|
||||
TAILQ_INSERT_TAIL(&(con->parent->nodes_head), con, nodes);
|
||||
TAILQ_INSERT_TAIL(&(con->parent->focus_head), con, focused);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* 1: detach the container from its parent */
|
||||
/* TODO: refactor this with tree_close() */
|
||||
TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes);
|
||||
TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
|
||||
|
||||
Con *child;
|
||||
int children = 0;
|
||||
TAILQ_FOREACH(child, &(con->parent->nodes_head), nodes)
|
||||
children++;
|
||||
/* TODO: better document why this math works */
|
||||
double fix = 1.0 / (1.0 - (1.0 / (children+1)));
|
||||
TAILQ_FOREACH(child, &(con->parent->nodes_head), nodes) {
|
||||
if (child->percent <= 0.0)
|
||||
continue;
|
||||
child->percent *= fix;
|
||||
}
|
||||
|
||||
/* 2: create a new container to render the decoration on, add
|
||||
* it as a floating window to the workspace */
|
||||
Con *nc = con_new(NULL);
|
||||
nc->parent = con_get_workspace(con);
|
||||
nc->rect = con->rect;
|
||||
nc->orientation = NO_ORIENTATION;
|
||||
nc->type = CT_FLOATING_CON;
|
||||
TAILQ_INSERT_TAIL(&(nc->parent->floating_head), nc, floating_windows);
|
||||
|
||||
/* 3: attach the child to the new parent container */
|
||||
con->old_parent = con->parent;
|
||||
con->parent = nc;
|
||||
con->floating = FLOATING_USER_ON;
|
||||
nc->rect.x = 400;
|
||||
nc->rect.y = 400;
|
||||
TAILQ_INSERT_TAIL(&(nc->nodes_head), con, nodes);
|
||||
TAILQ_INSERT_TAIL(&(nc->focus_head), con, focused);
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
if (client->dock) {
|
||||
DLOG("Not putting dock client into floating mode\n");
|
||||
return;
|
||||
|
@ -138,8 +181,10 @@ void toggle_floating_mode(xcb_connection_t *conn, Client *client, bool automatic
|
|||
/* Re-render the tiling layout of this container */
|
||||
render_container(conn, con);
|
||||
xcb_flush(conn);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Removes the floating client from its workspace and attaches it to the new workspace.
|
||||
* This is centralized here because it may happen if you move it via keyboard and
|
||||
|
@ -258,16 +303,17 @@ int floating_border_click(xcb_connection_t *conn, Client *client, xcb_button_pre
|
|||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
DRAGGING_CB(drag_window_callback) {
|
||||
struct xcb_button_press_event_t *event = extra;
|
||||
|
||||
/* Reposition the client correctly while moving */
|
||||
client->rect.x = old_rect->x + (new_x - event->root_x);
|
||||
client->rect.y = old_rect->y + (new_y - event->root_y);
|
||||
reposition_client(conn, client);
|
||||
con->rect.x = old_rect->x + (new_x - event->root_x);
|
||||
con->rect.y = old_rect->y + (new_y - event->root_y);
|
||||
//reposition_client(conn, con);
|
||||
/* Because reposition_client does not send a faked configure event (only resize does),
|
||||
* we need to initiate that on our own */
|
||||
fake_absolute_configure_notify(conn, client);
|
||||
//fake_absolute_configure_notify(conn, client);
|
||||
/* fake_absolute_configure_notify flushes */
|
||||
}
|
||||
|
||||
|
@ -276,12 +322,14 @@ DRAGGING_CB(drag_window_callback) {
|
|||
* Calls the drag_pointer function with the drag_window callback
|
||||
*
|
||||
*/
|
||||
void floating_drag_window(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event) {
|
||||
void floating_drag_window(Con *con, xcb_button_press_event_t *event) {
|
||||
DLOG("floating_drag_window\n");
|
||||
|
||||
drag_pointer(conn, client, event, XCB_NONE, BORDER_TOP /* irrelevant */, drag_window_callback, event);
|
||||
drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, drag_window_callback, event);
|
||||
tree_render();
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* This is an ugly data structure which we need because there is no standard
|
||||
* way of having nested functions (only available as a gcc extension at the
|
||||
|
@ -368,7 +416,7 @@ void floating_resize_window(xcb_connection_t *conn, Client *client,
|
|||
drag_pointer(conn, client, event, XCB_NONE, BORDER_TOP /* irrelevant */, resize_window_callback, ¶ms);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
/*
|
||||
* 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
|
||||
|
@ -377,13 +425,14 @@ void floating_resize_window(xcb_connection_t *conn, Client *client,
|
|||
* the event and the new coordinates (x, y).
|
||||
*
|
||||
*/
|
||||
void drag_pointer(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event,
|
||||
xcb_window_t confine_to, border_t border, callback_t callback, void *extra) {
|
||||
void drag_pointer(Con *con, xcb_button_press_event_t *event, xcb_window_t
|
||||
confine_to, border_t border, callback_t callback, void *extra)
|
||||
{
|
||||
xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
|
||||
uint32_t new_x, new_y;
|
||||
Rect old_rect;
|
||||
if (client != NULL)
|
||||
memcpy(&old_rect, &(client->rect), sizeof(Rect));
|
||||
if (con != NULL)
|
||||
memcpy(&old_rect, &(con->rect), sizeof(Rect));
|
||||
|
||||
/* Grab the pointer */
|
||||
/* TODO: returncode */
|
||||
|
@ -409,7 +458,7 @@ void drag_pointer(xcb_connection_t *conn, Client *client, xcb_button_press_event
|
|||
int nr = inside_event->response_type;
|
||||
if (nr == 0) {
|
||||
/* An error occured */
|
||||
handle_event(NULL, conn, inside_event);
|
||||
//handle_event(NULL, conn, inside_event);
|
||||
free(inside_event);
|
||||
continue;
|
||||
}
|
||||
|
@ -448,7 +497,7 @@ void drag_pointer(xcb_connection_t *conn, Client *client, xcb_button_press_event
|
|||
new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x;
|
||||
new_y = ((xcb_motion_notify_event_t*)last_motion_notify)->root_y;
|
||||
|
||||
callback(conn, client, &old_rect, new_x, new_y, extra);
|
||||
callback(con, &old_rect, new_x, new_y, extra);
|
||||
FREE(last_motion_notify);
|
||||
}
|
||||
done:
|
||||
|
@ -456,6 +505,7 @@ done:
|
|||
xcb_flush(conn);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Changes focus in the given direction for floating clients.
|
||||
*
|
||||
|
@ -555,3 +605,4 @@ void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) {
|
|||
|
||||
xcb_flush(conn);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* vim:ts=8:expandtab
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
*
|
||||
|
@ -8,38 +8,14 @@
|
|||
* See file LICENSE for license information.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_atom.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <xcb/randr.h>
|
||||
|
||||
#include <X11/XKBlib.h>
|
||||
|
||||
#include "i3.h"
|
||||
#include "debug.h"
|
||||
#include "table.h"
|
||||
#include "layout.h"
|
||||
#include "commands.h"
|
||||
#include "data.h"
|
||||
#include "xcb.h"
|
||||
#include "util.h"
|
||||
#include "randr.h"
|
||||
#include "config.h"
|
||||
#include "queue.h"
|
||||
#include "resize.h"
|
||||
#include "client.h"
|
||||
#include "manage.h"
|
||||
#include "floating.h"
|
||||
#include "workspace.h"
|
||||
#include "log.h"
|
||||
#include "container.h"
|
||||
#include "ipc.h"
|
||||
#include "all.h"
|
||||
|
||||
/* After mapping/unmapping windows, a notify event is generated. However, we don’t want it,
|
||||
since it’d trigger an infinite loop of switching between the different windows when
|
||||
|
@ -88,42 +64,44 @@ static bool event_is_ignored(const int sequence) {
|
|||
*
|
||||
*/
|
||||
int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
|
||||
DLOG("Keypress %d, state raw = %d\n", event->detail, event->state);
|
||||
DLOG("Keypress %d, state raw = %d\n", event->detail, event->state);
|
||||
|
||||
/* Remove the numlock bit, all other bits are modifiers we can bind to */
|
||||
uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
||||
DLOG("(removed numlock, state = %d)\n", state_filtered);
|
||||
/* Only use the lower 8 bits of the state (modifier masks) so that mouse
|
||||
* button masks are filtered out */
|
||||
state_filtered &= 0xFF;
|
||||
DLOG("(removed upper 8 bits, state = %d)\n", state_filtered);
|
||||
/* Remove the numlock bit, all other bits are modifiers we can bind to */
|
||||
uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
||||
DLOG("(removed numlock, state = %d)\n", state_filtered);
|
||||
/* Only use the lower 8 bits of the state (modifier masks) so that mouse
|
||||
* button masks are filtered out */
|
||||
state_filtered &= 0xFF;
|
||||
DLOG("(removed upper 8 bits, state = %d)\n", state_filtered);
|
||||
|
||||
if (xkb_current_group == XkbGroup2Index)
|
||||
state_filtered |= BIND_MODE_SWITCH;
|
||||
if (xkb_current_group == XkbGroup2Index)
|
||||
state_filtered |= BIND_MODE_SWITCH;
|
||||
|
||||
DLOG("(checked mode_switch, state %d)\n", state_filtered);
|
||||
DLOG("(checked mode_switch, state %d)\n", state_filtered);
|
||||
|
||||
/* Find the binding */
|
||||
Binding *bind = get_binding(state_filtered, event->detail);
|
||||
/* Find the binding */
|
||||
Binding *bind = get_binding(state_filtered, event->detail);
|
||||
|
||||
/* No match? Then the user has Mode_switch enabled but does not have a
|
||||
* specific keybinding. Fall back to the default keybindings (without
|
||||
* Mode_switch). Makes it much more convenient for users of a hybrid
|
||||
* layout (like us, ru). */
|
||||
if (bind == NULL) {
|
||||
state_filtered &= ~(BIND_MODE_SWITCH);
|
||||
DLOG("no match, new state_filtered = %d\n", state_filtered);
|
||||
if ((bind = get_binding(state_filtered, event->detail)) == NULL) {
|
||||
ELOG("Could not lookup key binding (modifiers %d, keycode %d)\n",
|
||||
state_filtered, event->detail);
|
||||
return 1;
|
||||
}
|
||||
/* No match? Then the user has Mode_switch enabled but does not have a
|
||||
* specific keybinding. Fall back to the default keybindings (without
|
||||
* Mode_switch). Makes it much more convenient for users of a hybrid
|
||||
* layout (like us, ru). */
|
||||
if (bind == NULL) {
|
||||
state_filtered &= ~(BIND_MODE_SWITCH);
|
||||
DLOG("no match, new state_filtered = %d\n", state_filtered);
|
||||
if ((bind = get_binding(state_filtered, event->detail)) == NULL) {
|
||||
ELOG("Could not lookup key binding (modifiers %d, keycode %d)\n",
|
||||
state_filtered, event->detail);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
parse_command(conn, bind->command);
|
||||
return 1;
|
||||
parse_command(bind->command);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
/*
|
||||
* Called with coordinates of an enter_notify event or motion_notify event
|
||||
* to check if the user crossed virtual screen boundaries and adjust the
|
||||
|
@ -1076,3 +1054,4 @@ int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state
|
|||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
|
108
src/ipc.c
108
src/ipc.c
|
@ -10,29 +10,14 @@
|
|||
* ipc.c: Everything about the UNIX domain sockets for IPC
|
||||
*
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <ev.h>
|
||||
#include <yajl/yajl_gen.h>
|
||||
#include <yajl/yajl_parse.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "ipc.h"
|
||||
#include "i3.h"
|
||||
#include "util.h"
|
||||
#include "commands.h"
|
||||
#include "log.h"
|
||||
#include "table.h"
|
||||
#include "randr.h"
|
||||
#include "all.h"
|
||||
|
||||
/* Shorter names for all those yajl_gen_* functions */
|
||||
#define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__)
|
||||
|
@ -129,7 +114,7 @@ IPC_HANDLER(command) {
|
|||
* 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);
|
||||
parse_command((const char*)command);
|
||||
free(command);
|
||||
|
||||
/* For now, every command gets a positive acknowledge
|
||||
|
@ -139,6 +124,88 @@ IPC_HANDLER(command) {
|
|||
I3_IPC_REPLY_TYPE_COMMAND, strlen(reply));
|
||||
}
|
||||
|
||||
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
||||
y(map_open);
|
||||
ystr("id");
|
||||
y(integer, (long int)con);
|
||||
|
||||
ystr("type");
|
||||
y(integer, con->type);
|
||||
|
||||
ystr("orientation");
|
||||
y(integer, con->orientation);
|
||||
|
||||
ystr("layout");
|
||||
y(integer, con->layout);
|
||||
|
||||
ystr("rect");
|
||||
y(map_open);
|
||||
ystr("x");
|
||||
y(integer, con->rect.x);
|
||||
ystr("y");
|
||||
y(integer, con->rect.y);
|
||||
ystr("width");
|
||||
y(integer, con->rect.width);
|
||||
ystr("height");
|
||||
y(integer, con->rect.height);
|
||||
y(map_close);
|
||||
|
||||
ystr("name");
|
||||
ystr(con->name);
|
||||
|
||||
ystr("window");
|
||||
if (con->window)
|
||||
y(integer, con->window->id);
|
||||
else y(null);
|
||||
|
||||
ystr("nodes");
|
||||
y(array_open);
|
||||
Con *leaf;
|
||||
TAILQ_FOREACH(leaf, &(con->nodes_head), nodes) {
|
||||
dump_node(gen, leaf, inplace_restart);
|
||||
}
|
||||
y(array_close);
|
||||
|
||||
ystr("focus");
|
||||
y(array_open);
|
||||
TAILQ_FOREACH(leaf, &(con->nodes_head), nodes) {
|
||||
y(integer, (long int)leaf);
|
||||
}
|
||||
y(array_close);
|
||||
|
||||
ystr("fullscreen_mode");
|
||||
y(integer, con->fullscreen_mode);
|
||||
|
||||
if (inplace_restart) {
|
||||
if (con->window != NULL) {
|
||||
ystr("swallows");
|
||||
y(array_open);
|
||||
y(map_open);
|
||||
ystr("id");
|
||||
y(integer, con->window->id);
|
||||
y(map_close);
|
||||
y(array_close);
|
||||
}
|
||||
}
|
||||
|
||||
y(map_close);
|
||||
}
|
||||
|
||||
IPC_HANDLER(tree) {
|
||||
printf("tree\n");
|
||||
yajl_gen gen = yajl_gen_alloc(NULL, NULL);
|
||||
dump_node(gen, croot, false);
|
||||
|
||||
const unsigned char *payload;
|
||||
unsigned int length;
|
||||
y(get_buf, &payload, &length);
|
||||
|
||||
ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_TREE, length);
|
||||
y(free);
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Formats the reply message for a GET_WORKSPACES request and sends it to the
|
||||
* client
|
||||
|
@ -327,14 +394,13 @@ IPC_HANDLER(subscribe) {
|
|||
ipc_send_message(fd, (const unsigned char*)reply,
|
||||
I3_IPC_REPLY_TYPE_SUBSCRIBE, strlen(reply));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The index of each callback function corresponds to the numeric
|
||||
* value of the message type (see include/i3/ipc.h) */
|
||||
handler_t handlers[4] = {
|
||||
handler_t handlers[2] = {
|
||||
handle_command,
|
||||
handle_get_workspaces,
|
||||
handle_subscribe,
|
||||
handle_get_outputs
|
||||
handle_tree
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
#include <yajl/yajl_common.h>
|
||||
#include <yajl/yajl_gen.h>
|
||||
#include <yajl/yajl_parse.h>
|
||||
|
||||
#include "all.h"
|
||||
|
||||
/* TODO: refactor the whole parsing thing */
|
||||
|
||||
static char *last_key;
|
||||
static Con *json_node;
|
||||
static bool parsing_swallows;
|
||||
static bool parsing_rect;
|
||||
struct Match *current_swallow;
|
||||
|
||||
static int json_start_map(void *ctx) {
|
||||
LOG("start of map\n");
|
||||
if (parsing_swallows) {
|
||||
LOG("TODO: create new swallow\n");
|
||||
current_swallow = scalloc(sizeof(Match));
|
||||
TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
|
||||
} else {
|
||||
if (!parsing_rect)
|
||||
json_node = con_new(json_node);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int json_end_map(void *ctx) {
|
||||
LOG("end of map\n");
|
||||
if (!parsing_swallows && !parsing_rect)
|
||||
json_node = json_node->parent;
|
||||
if (parsing_rect)
|
||||
parsing_rect = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int json_end_array(void *ctx) {
|
||||
LOG("end of array\n");
|
||||
parsing_swallows = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int json_key(void *ctx, const unsigned char *val, unsigned int len) {
|
||||
LOG("key: %.*s\n", len, val);
|
||||
FREE(last_key);
|
||||
last_key = scalloc((len+1) * sizeof(char));
|
||||
memcpy(last_key, val, len);
|
||||
if (strcasecmp(last_key, "swallows") == 0) {
|
||||
parsing_swallows = true;
|
||||
}
|
||||
if (strcasecmp(last_key, "rect") == 0)
|
||||
parsing_rect = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int json_string(void *ctx, const unsigned char *val, unsigned int len) {
|
||||
LOG("string: %.*s for key %s\n", len, val, last_key);
|
||||
if (parsing_swallows) {
|
||||
/* TODO: the other swallowing keys */
|
||||
if (strcasecmp(last_key, "class") == 0) {
|
||||
current_swallow->class = scalloc((len+1) * sizeof(char));
|
||||
memcpy(current_swallow->class, val, len);
|
||||
}
|
||||
LOG("unhandled yet: swallow\n");
|
||||
} else {
|
||||
if (strcasecmp(last_key, "name") == 0) {
|
||||
json_node->name = scalloc((len+1) * sizeof(char));
|
||||
memcpy(json_node->name, val, len);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int json_int(void *ctx, long val) {
|
||||
LOG("int %d for key %s\n", val, last_key);
|
||||
if (strcasecmp(last_key, "orientation") == 0) {
|
||||
json_node->orientation = val;
|
||||
}
|
||||
if (strcasecmp(last_key, "layout") == 0) {
|
||||
json_node->layout = val;
|
||||
}
|
||||
if (strcasecmp(last_key, "type") == 0) {
|
||||
json_node->type = val;
|
||||
}
|
||||
if (strcasecmp(last_key, "fullscreen_mode") == 0) {
|
||||
json_node->fullscreen_mode = val;
|
||||
}
|
||||
|
||||
if (parsing_rect) {
|
||||
if (strcasecmp(last_key, "x") == 0)
|
||||
json_node->rect.x = val;
|
||||
else if (strcasecmp(last_key, "y") == 0)
|
||||
json_node->rect.y = val;
|
||||
else if (strcasecmp(last_key, "width") == 0)
|
||||
json_node->rect.width = val;
|
||||
else if (strcasecmp(last_key, "height") == 0)
|
||||
json_node->rect.height = val;
|
||||
else printf("WARNING: unknown key %s in rect\n", last_key);
|
||||
printf("rect now: (%d, %d, %d, %d)\n",
|
||||
json_node->rect.x, json_node->rect.y,
|
||||
json_node->rect.width, json_node->rect.height);
|
||||
}
|
||||
if (parsing_swallows) {
|
||||
if (strcasecmp(last_key, "id") == 0) {
|
||||
current_swallow->id = val;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int json_double(void *ctx, double val) {
|
||||
LOG("double %f for key %s\n", val, last_key);
|
||||
if (strcasecmp(last_key, "percent") == 0) {
|
||||
json_node->percent = val;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void tree_append_json(const char *filename) {
|
||||
/* TODO: percent of other windows are not correctly fixed at the moment */
|
||||
FILE *f;
|
||||
if ((f = fopen(filename, "r")) == NULL) {
|
||||
LOG("Cannot open file\n");
|
||||
return;
|
||||
}
|
||||
char *buf = malloc(65535); /* TODO */
|
||||
int n = fread(buf, 1, 65535, f);
|
||||
LOG("read %d bytes\n", n);
|
||||
yajl_gen g;
|
||||
yajl_handle hand;
|
||||
yajl_callbacks callbacks;
|
||||
memset(&callbacks, '\0', sizeof(yajl_callbacks));
|
||||
callbacks.yajl_start_map = json_start_map;
|
||||
callbacks.yajl_end_map = json_end_map;
|
||||
callbacks.yajl_end_array = json_end_array;
|
||||
callbacks.yajl_string = json_string;
|
||||
callbacks.yajl_map_key = json_key;
|
||||
callbacks.yajl_integer = json_int;
|
||||
callbacks.yajl_double = json_double;
|
||||
g = yajl_gen_alloc(NULL, NULL);
|
||||
hand = yajl_alloc(&callbacks, NULL, NULL, (void*)g);
|
||||
yajl_status stat;
|
||||
json_node = focused;
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
stat = yajl_parse(hand, (const unsigned char*)buf, n);
|
||||
if (stat != yajl_status_ok &&
|
||||
stat != yajl_status_insufficient_data)
|
||||
{
|
||||
unsigned char * str = yajl_get_error(hand, 1, (const unsigned char*)buf, n);
|
||||
fprintf(stderr, (const char *) str);
|
||||
yajl_free_error(hand, str);
|
||||
}
|
||||
|
||||
setlocale(LC_NUMERIC, "");
|
||||
yajl_parse_complete(hand);
|
||||
|
||||
fclose(f);
|
||||
//con_focus(json_node);
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
#include "loglevels.h"
|
||||
|
||||
static uint32_t loglevel = 0;
|
||||
static bool verbose = false;
|
||||
static bool verbose = true;
|
||||
|
||||
/**
|
||||
* Set verbosity of i3. If verbose is set to true, informative messages will
|
||||
|
|
136
src/manage.c
136
src/manage.c
|
@ -11,33 +11,17 @@
|
|||
* (or existing ones on restart).
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include "all.h"
|
||||
|
||||
extern struct Con *focused;
|
||||
|
||||
#include "xcb.h"
|
||||
#include "data.h"
|
||||
#include "util.h"
|
||||
#include "i3.h"
|
||||
#include "table.h"
|
||||
#include "config.h"
|
||||
#include "handlers.h"
|
||||
#include "layout.h"
|
||||
#include "manage.h"
|
||||
#include "floating.h"
|
||||
#include "client.h"
|
||||
#include "workspace.h"
|
||||
#include "log.h"
|
||||
#include "ewmh.h"
|
||||
|
||||
/*
|
||||
* Go through all existing windows (if the window manager is restarted) and manage them
|
||||
*
|
||||
*/
|
||||
void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *prophs, xcb_window_t root) {
|
||||
void manage_existing_windows(xcb_window_t root) {
|
||||
xcb_query_tree_reply_t *reply;
|
||||
int i, len;
|
||||
xcb_window_t *children;
|
||||
|
@ -57,7 +41,8 @@ void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *pr
|
|||
|
||||
/* Call manage_window with the attributes for every window */
|
||||
for (i = 0; i < len; ++i)
|
||||
manage_window(prophs, conn, children[i], cookies[i], true);
|
||||
manage_window(children[i], cookies[i], true);
|
||||
|
||||
|
||||
free(reply);
|
||||
free(cookies);
|
||||
|
@ -71,15 +56,16 @@ void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *pr
|
|||
* side-effects which are to be expected when continuing to run i3.
|
||||
*
|
||||
*/
|
||||
void restore_geometry(xcb_connection_t *conn) {
|
||||
Workspace *ws;
|
||||
Client *client;
|
||||
DLOG("Restoring geometry\n");
|
||||
void restore_geometry() {
|
||||
LOG("Restoring geometry\n");
|
||||
|
||||
TAILQ_FOREACH(ws, workspaces, workspaces)
|
||||
SLIST_FOREACH(client, &(ws->focus_stack), focus_clients)
|
||||
xcb_reparent_window(conn, client->child, root,
|
||||
client->rect.x, client->rect.y);
|
||||
Con *con;
|
||||
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||
if (con->window) {
|
||||
printf("placing window at %d %d\n", con->rect.x, con->rect.y);
|
||||
xcb_reparent_window(conn, con->window->id, root,
|
||||
con->rect.x, con->rect.y);
|
||||
}
|
||||
|
||||
/* Make sure our changes reach the X server, we restart/exit now */
|
||||
xcb_flush(conn);
|
||||
|
@ -89,58 +75,123 @@ void restore_geometry(xcb_connection_t *conn) {
|
|||
* Do some sanity checks and then reparent the window.
|
||||
*
|
||||
*/
|
||||
void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
|
||||
xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
|
||||
void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
|
||||
bool needs_to_be_mapped) {
|
||||
xcb_drawable_t d = { window };
|
||||
xcb_get_geometry_cookie_t geomc;
|
||||
xcb_get_geometry_reply_t *geom;
|
||||
xcb_get_window_attributes_reply_t *attr = 0;
|
||||
|
||||
printf("---> looking at window 0x%08x\n", window);
|
||||
|
||||
xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
|
||||
utf8_title_cookie, title_cookie,
|
||||
class_cookie, leader_cookie;
|
||||
|
||||
wm_type_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
|
||||
strut_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
|
||||
state_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_STATE], UINT32_MAX);
|
||||
utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_NAME], 128);
|
||||
leader_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[WM_CLIENT_LEADER], UINT32_MAX);
|
||||
title_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_NAME, 128);
|
||||
class_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_CLASS, 128);
|
||||
|
||||
|
||||
geomc = xcb_get_geometry(conn, d);
|
||||
|
||||
/* Check if the window is mapped (it could be not mapped when intializing and
|
||||
calling manage_window() for every window) */
|
||||
if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) {
|
||||
ELOG("Could not get attributes\n");
|
||||
LOG("Could not get attributes\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE)
|
||||
if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) {
|
||||
LOG("map_state unviewable\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Don’t manage clients with the override_redirect flag */
|
||||
LOG("override_redirect is %d\n", attr->override_redirect);
|
||||
if (attr->override_redirect)
|
||||
goto out;
|
||||
|
||||
/* Check if the window is already managed */
|
||||
if (table_get(&by_child, window))
|
||||
if (con_by_window_id(window) != NULL)
|
||||
goto out;
|
||||
|
||||
/* Get the initial geometry (position, size, …) */
|
||||
if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL)
|
||||
goto out;
|
||||
|
||||
LOG("reparenting!\n");
|
||||
|
||||
i3Window *cwindow = scalloc(sizeof(i3Window));
|
||||
cwindow->id = window;
|
||||
|
||||
class_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_CLASS, 128);
|
||||
xcb_get_property_reply_t *preply;
|
||||
preply = xcb_get_property_reply(conn, class_cookie, NULL);
|
||||
if (preply == NULL || xcb_get_property_value_length(preply) == 0) {
|
||||
LOG("cannot get wm_class\n");
|
||||
} else cwindow->class = strdup(xcb_get_property_value(preply));
|
||||
|
||||
Con *nc;
|
||||
Match *match;
|
||||
|
||||
/* TODO: assignments */
|
||||
/* TODO: two matches for one container */
|
||||
/* See if any container swallows this new window */
|
||||
nc = con_for_window(cwindow, &match);
|
||||
if (nc == NULL) {
|
||||
if (focused->type == CT_CON && con_accepts_window(focused)) {
|
||||
LOG("using current container, focused = %p, focused->name = %s\n",
|
||||
focused, focused->name);
|
||||
nc = focused;
|
||||
} else nc = tree_open_con(NULL);
|
||||
} else {
|
||||
if (match != NULL && match->insert_where == M_ACTIVE) {
|
||||
/* We need to go down the focus stack starting from nc */
|
||||
while (TAILQ_FIRST(&(nc->focus_head)) != TAILQ_END(&(nc->focus_head))) {
|
||||
printf("walking down one step...\n");
|
||||
nc = TAILQ_FIRST(&(nc->focus_head));
|
||||
}
|
||||
/* We need to open a new con */
|
||||
/* TODO: make a difference between match-once containers (directly assign
|
||||
* cwindow) and match-multiple (tree_open_con first) */
|
||||
nc = tree_open_con(nc->parent);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
nc->window = cwindow;
|
||||
|
||||
xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame, 0, 0);
|
||||
if (xcb_request_check(conn, rcookie) != NULL) {
|
||||
LOG("Could not reparent the window, aborting\n");
|
||||
goto out;
|
||||
//xcb_destroy_window(conn, nc->frame);
|
||||
}
|
||||
|
||||
xcb_change_save_set(conn, XCB_SET_MODE_INSERT, window);
|
||||
|
||||
tree_render();
|
||||
|
||||
#if 0
|
||||
/* 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->border_width);
|
||||
#endif
|
||||
|
||||
/* 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]);
|
||||
|
||||
free(geom);
|
||||
out:
|
||||
free(attr);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* reparent_window() gets called when a new window was opened and becomes a child of the root
|
||||
* window, or it gets called by us when we manage the already existing windows at startup.
|
||||
|
@ -517,3 +568,4 @@ map:
|
|||
|
||||
xcb_flush(conn);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*/
|
||||
#include <ev.h>
|
||||
#include "all.h"
|
||||
|
||||
static int xkb_event_base;
|
||||
|
||||
int xkb_current_group;
|
||||
|
||||
extern Con *focused;
|
||||
|
||||
char **start_argv;
|
||||
|
||||
xcb_connection_t *conn;
|
||||
xcb_event_handlers_t evenths;
|
||||
xcb_atom_t atoms[NUM_ATOMS];
|
||||
|
||||
xcb_window_t root;
|
||||
uint8_t root_depth;
|
||||
|
||||
xcb_key_symbols_t *keysyms;
|
||||
|
||||
/* The list of key bindings */
|
||||
struct bindings_head *bindings;
|
||||
|
||||
/* The list of exec-lines */
|
||||
struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);
|
||||
|
||||
/* The list of assignments */
|
||||
struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments);
|
||||
|
||||
/*
|
||||
* This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb.
|
||||
* See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop
|
||||
*
|
||||
*/
|
||||
static void xcb_got_event(EV_P_ struct ev_io *w, int revents) {
|
||||
/* empty, because xcb_prepare_cb and xcb_check_cb are used */
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush before blocking (and waiting for new events)
|
||||
*
|
||||
*/
|
||||
static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
|
||||
xcb_flush(conn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Instead of polling the X connection socket we leave this to
|
||||
* xcb_poll_for_event() which knows better than we can ever know.
|
||||
*
|
||||
*/
|
||||
static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
|
||||
xcb_generic_event_t *event;
|
||||
|
||||
while ((event = xcb_poll_for_event(conn)) != NULL) {
|
||||
xcb_event_handle(&evenths, event);
|
||||
free(event);
|
||||
}
|
||||
}
|
||||
|
||||
int handle_map_request(void *unused, xcb_connection_t *conn, xcb_map_request_event_t *event) {
|
||||
xcb_get_window_attributes_cookie_t cookie;
|
||||
|
||||
cookie = xcb_get_window_attributes_unchecked(conn, event->window);
|
||||
|
||||
LOG("window = 0x%08x, serial is %d.\n", event->window, event->sequence);
|
||||
//add_ignore_event(event->sequence);
|
||||
|
||||
manage_window(event->window, cookie, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_notify_event_t *event) {
|
||||
LOG("unmap event for 0x%08x\n", event->window);
|
||||
Con *con = con_by_window_id(event->window);
|
||||
if (con == NULL) {
|
||||
LOG("Not a managed window, ignoring\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
tree_close(con);
|
||||
tree_render();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *event) {
|
||||
Con *parent, *con;
|
||||
|
||||
/* event->count is the number of minimum remaining expose events for this window, so we
|
||||
skip all events but the last one */
|
||||
if (event->count != 0)
|
||||
return 1;
|
||||
LOG("expose-event, window = %08x\n", event->window);
|
||||
|
||||
if ((parent = con_by_frame_id(event->window)) == NULL) {
|
||||
LOG("expose event for unknown window, ignoring\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(con, &(parent->nodes_head), nodes) {
|
||||
LOG("expose for con %p / %s\n", con, con->name);
|
||||
if (con->window)
|
||||
x_draw_decoration(con);
|
||||
}
|
||||
xcb_flush(conn);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void parse_command(const char *command) {
|
||||
printf("received command: %s\n", command);
|
||||
|
||||
if (strcasecmp(command, "open") == 0)
|
||||
tree_open_con(NULL);
|
||||
else if (strcasecmp(command, "close") == 0)
|
||||
tree_close_con();
|
||||
else if (strcasecmp(command, "split h") == 0)
|
||||
tree_split(focused, HORIZ);
|
||||
else if (strcasecmp(command, "split v") == 0)
|
||||
tree_split(focused, VERT);
|
||||
else if (strcasecmp(command, "level up") == 0)
|
||||
level_up();
|
||||
else if (strcasecmp(command, "level down") == 0)
|
||||
level_down();
|
||||
else if (strcasecmp(command, "prev h") == 0)
|
||||
tree_next('p', HORIZ);
|
||||
else if (strcasecmp(command, "prev v") == 0)
|
||||
tree_next('p', VERT);
|
||||
else if (strcasecmp(command, "next h") == 0)
|
||||
tree_next('n', HORIZ);
|
||||
else if (strcasecmp(command, "next v") == 0)
|
||||
tree_next('n', VERT);
|
||||
else if (strncasecmp(command, "workspace ", strlen("workspace ")) == 0)
|
||||
workspace_show(command + strlen("workspace "));
|
||||
|
||||
else if (strcasecmp(command, "move before h") == 0)
|
||||
tree_move('p', HORIZ);
|
||||
else if (strcasecmp(command, "move before v") == 0)
|
||||
tree_move('p', VERT);
|
||||
else if (strcasecmp(command, "move after h") == 0)
|
||||
tree_move('n', HORIZ);
|
||||
else if (strcasecmp(command, "move after v") == 0)
|
||||
tree_move('n', VERT);
|
||||
else if (strncasecmp(command, "restore", strlen("restore")) == 0)
|
||||
tree_append_json(command + strlen("restore "));
|
||||
else if (strncasecmp(command, "exec", strlen("exec")) == 0)
|
||||
start_application(command + strlen("exec "));
|
||||
else if (strcasecmp(command, "restart") == 0)
|
||||
i3_restart();
|
||||
else if (strcasecmp(command, "floating") == 0)
|
||||
toggle_floating_mode(focused, false);
|
||||
|
||||
tree_render();
|
||||
|
||||
#if 0
|
||||
if (strcasecmp(command, "prev") == 0)
|
||||
tree_prev(O_CURRENT);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int screens;
|
||||
char *override_configpath = NULL;
|
||||
bool autostart = true;
|
||||
bool only_check_config = false;
|
||||
bool force_xinerama = false;
|
||||
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'},
|
||||
{"force-xinerama", no_argument, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
int option_index = 0, opt;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
/* Disable output buffering to make redirects in .xsession actually useful for debugging */
|
||||
if (!isatty(fileno(stdout)))
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
start_argv = argv;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "c:Cvahld:V", long_options, &option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
LOG("Autostart disabled using -a\n");
|
||||
autostart = false;
|
||||
break;
|
||||
case 'c':
|
||||
override_configpath = sstrdup(optarg);
|
||||
break;
|
||||
case 'C':
|
||||
LOG("Checking configuration file only (-C)\n");
|
||||
only_check_config = true;
|
||||
break;
|
||||
case 'v':
|
||||
printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'V':
|
||||
set_verbosity(true);
|
||||
break;
|
||||
case 'd':
|
||||
LOG("Enabling debug loglevel %s\n", optarg);
|
||||
add_loglevel(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
/* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */
|
||||
break;
|
||||
case 0:
|
||||
if (strcmp(long_options[option_index].name, "force-xinerama") == 0) {
|
||||
force_xinerama = true;
|
||||
ELOG("Using Xinerama instead of RandR. This option should be "
|
||||
"avoided at all cost because it does not refresh the list "
|
||||
"of screens, so you cannot configure displays at runtime. "
|
||||
"Please check if your driver really does not support RandR "
|
||||
"and disable this option as soon as you can.\n");
|
||||
break;
|
||||
}
|
||||
/* fall-through */
|
||||
default:
|
||||
fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "-a: disable autostart\n");
|
||||
fprintf(stderr, "-v: display version and exit\n");
|
||||
fprintf(stderr, "-V: enable verbose mode\n");
|
||||
fprintf(stderr, "-d <loglevel>: enable debug loglevel <loglevel>\n");
|
||||
fprintf(stderr, "-c <configfile>: use the provided configfile instead\n");
|
||||
fprintf(stderr, "-C: check configuration file and exit\n");
|
||||
fprintf(stderr, "--force-xinerama: Use Xinerama instead of RandR. This "
|
||||
"option should only be used if you are stuck with the "
|
||||
"nvidia closed source driver which does not support RandR.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
LOG("i3 (tree) version " I3_VERSION " starting\n");
|
||||
|
||||
conn = xcb_connect(NULL, &screens);
|
||||
if (xcb_connection_has_error(conn))
|
||||
errx(EXIT_FAILURE, "Cannot open display\n");
|
||||
|
||||
load_configuration(conn, override_configpath, false);
|
||||
if (only_check_config) {
|
||||
LOG("Done checking configuration file. Exiting.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
|
||||
root = root_screen->root;
|
||||
root_depth = root_screen->root_depth;
|
||||
|
||||
uint32_t mask = XCB_CW_EVENT_MASK;
|
||||
uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video
|
||||
projector), the root window gets a
|
||||
ConfigureNotify */
|
||||
XCB_EVENT_MASK_POINTER_MOTION |
|
||||
XCB_EVENT_MASK_PROPERTY_CHANGE |
|
||||
XCB_EVENT_MASK_ENTER_WINDOW };
|
||||
xcb_void_cookie_t cookie;
|
||||
cookie = xcb_change_window_attributes_checked(conn, root, mask, values);
|
||||
check_error(conn, cookie, "Another window manager seems to be running");
|
||||
|
||||
/* 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);
|
||||
|
||||
REQUEST_ATOM(_NET_SUPPORTED);
|
||||
REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN);
|
||||
REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK);
|
||||
REQUEST_ATOM(_NET_WM_NAME);
|
||||
REQUEST_ATOM(_NET_WM_STATE);
|
||||
REQUEST_ATOM(_NET_WM_WINDOW_TYPE);
|
||||
REQUEST_ATOM(_NET_WM_DESKTOP);
|
||||
REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
|
||||
REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DIALOG);
|
||||
REQUEST_ATOM(_NET_WM_WINDOW_TYPE_UTILITY);
|
||||
REQUEST_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR);
|
||||
REQUEST_ATOM(_NET_WM_WINDOW_TYPE_SPLASH);
|
||||
REQUEST_ATOM(_NET_WM_STRUT_PARTIAL);
|
||||
REQUEST_ATOM(WM_PROTOCOLS);
|
||||
REQUEST_ATOM(WM_DELETE_WINDOW);
|
||||
REQUEST_ATOM(UTF8_STRING);
|
||||
REQUEST_ATOM(WM_STATE);
|
||||
REQUEST_ATOM(WM_CLIENT_LEADER);
|
||||
REQUEST_ATOM(_NET_CURRENT_DESKTOP);
|
||||
REQUEST_ATOM(_NET_ACTIVE_WINDOW);
|
||||
REQUEST_ATOM(_NET_WORKAREA);
|
||||
|
||||
|
||||
xcb_event_handlers_init(conn, &evenths);
|
||||
xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL);
|
||||
|
||||
xcb_event_set_button_press_handler(&evenths, handle_button_press, NULL);
|
||||
|
||||
xcb_event_set_map_request_handler(&evenths, handle_map_request, NULL);
|
||||
|
||||
xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL);
|
||||
//xcb_event_set_destroy_notify_handler(&evenths, handle_destroy_notify_event, NULL);
|
||||
|
||||
xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL);
|
||||
|
||||
/* Setup NetWM atoms */
|
||||
#define GET_ATOM(name) \
|
||||
do { \
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \
|
||||
if (!reply) { \
|
||||
ELOG("Could not get atom " #name "\n"); \
|
||||
exit(-1); \
|
||||
} \
|
||||
atoms[name] = reply->atom; \
|
||||
free(reply); \
|
||||
} while (0)
|
||||
|
||||
GET_ATOM(_NET_SUPPORTED);
|
||||
GET_ATOM(_NET_WM_STATE_FULLSCREEN);
|
||||
GET_ATOM(_NET_SUPPORTING_WM_CHECK);
|
||||
GET_ATOM(_NET_WM_NAME);
|
||||
GET_ATOM(_NET_WM_STATE);
|
||||
GET_ATOM(_NET_WM_WINDOW_TYPE);
|
||||
GET_ATOM(_NET_WM_DESKTOP);
|
||||
GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK);
|
||||
GET_ATOM(_NET_WM_WINDOW_TYPE_DIALOG);
|
||||
GET_ATOM(_NET_WM_WINDOW_TYPE_UTILITY);
|
||||
GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR);
|
||||
GET_ATOM(_NET_WM_WINDOW_TYPE_SPLASH);
|
||||
GET_ATOM(_NET_WM_STRUT_PARTIAL);
|
||||
GET_ATOM(WM_PROTOCOLS);
|
||||
GET_ATOM(WM_DELETE_WINDOW);
|
||||
GET_ATOM(UTF8_STRING);
|
||||
GET_ATOM(WM_STATE);
|
||||
GET_ATOM(WM_CLIENT_LEADER);
|
||||
GET_ATOM(_NET_CURRENT_DESKTOP);
|
||||
GET_ATOM(_NET_ACTIVE_WINDOW);
|
||||
GET_ATOM(_NET_WORKAREA);
|
||||
|
||||
keysyms = xcb_key_symbols_alloc(conn);
|
||||
|
||||
xcb_get_numlock_mask(conn);
|
||||
|
||||
translate_keysyms();
|
||||
grab_all_keys(conn, false);
|
||||
|
||||
int randr_base;
|
||||
if (force_xinerama) {
|
||||
xinerama_init();
|
||||
} else {
|
||||
DLOG("Checking for XRandR...\n");
|
||||
randr_init(&randr_base);
|
||||
|
||||
#if 0
|
||||
xcb_event_set_handler(&evenths,
|
||||
randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY,
|
||||
handle_screen_change,
|
||||
NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!tree_restore())
|
||||
tree_init();
|
||||
tree_render();
|
||||
|
||||
/* proof-of-concept for assignments */
|
||||
Con *ws = workspace_get("3");
|
||||
|
||||
Match *current_swallow = scalloc(sizeof(Match));
|
||||
TAILQ_INSERT_TAIL(&(ws->swallow_head), current_swallow, matches);
|
||||
|
||||
current_swallow->insert_where = M_ACTIVE;
|
||||
current_swallow->class = strdup("xterm");
|
||||
|
||||
struct ev_loop *loop = ev_loop_new(0);
|
||||
if (loop == NULL)
|
||||
die("Could not initialize libev. Bad LIBEV_FLAGS?\n");
|
||||
|
||||
/* Create the UNIX domain socket for IPC */
|
||||
if (config.ipc_socket_path != NULL) {
|
||||
int ipc_socket = ipc_create_socket(config.ipc_socket_path);
|
||||
if (ipc_socket == -1) {
|
||||
ELOG("Could not create the IPC socket, IPC disabled\n");
|
||||
} else {
|
||||
struct ev_io *ipc_io = scalloc(sizeof(struct ev_io));
|
||||
ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ);
|
||||
ev_io_start(loop, ipc_io);
|
||||
}
|
||||
}
|
||||
|
||||
struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io));
|
||||
struct ev_check *xcb_check = scalloc(sizeof(struct ev_check));
|
||||
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
|
||||
|
||||
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
|
||||
ev_io_start(loop, xcb_watcher);
|
||||
|
||||
ev_check_init(xcb_check, xcb_check_cb);
|
||||
ev_check_start(loop, xcb_check);
|
||||
|
||||
ev_prepare_init(xcb_prepare, xcb_prepare_cb);
|
||||
ev_prepare_start(loop, xcb_prepare);
|
||||
|
||||
xcb_flush(conn);
|
||||
|
||||
manage_existing_windows(root);
|
||||
|
||||
ev_loop(loop, 0);
|
||||
}
|
47
src/randr.c
47
src/randr.c
|
@ -12,30 +12,11 @@
|
|||
* (take your time to read it completely, it answers all questions).
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/randr.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "i3.h"
|
||||
#include "data.h"
|
||||
#include "table.h"
|
||||
#include "util.h"
|
||||
#include "layout.h"
|
||||
#include "xcb.h"
|
||||
#include "config.h"
|
||||
#include "workspace.h"
|
||||
#include "log.h"
|
||||
#include "ewmh.h"
|
||||
#include "ipc.h"
|
||||
#include "client.h"
|
||||
#include "all.h"
|
||||
|
||||
/* While a clean namespace is usually a pretty good thing, we really need
|
||||
* to use shorter names than the whole xcb_randr_* default names. */
|
||||
|
@ -159,6 +140,7 @@ Output *get_output_most(direction_t direction, Output *current) {
|
|||
return candidate;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Initializes the specified output, assigning the specified workspace to it.
|
||||
*
|
||||
|
@ -207,6 +189,7 @@ void initialize_output(xcb_connection_t *conn, Output *output, Workspace *worksp
|
|||
workspace_assign_to(ws, output, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Disables RandR support by creating exactly one output with the size of the
|
||||
|
@ -245,8 +228,6 @@ void disable_randr(xcb_connection_t *conn) {
|
|||
*/
|
||||
static void output_change_mode(xcb_connection_t *conn, Output *output) {
|
||||
i3Font *font = load_font(conn, config.font);
|
||||
Workspace *ws;
|
||||
Client *client;
|
||||
|
||||
DLOG("Output mode changed, reconfiguring bar, updating workspaces\n");
|
||||
Rect bar_rect = {output->rect.x,
|
||||
|
@ -256,6 +237,7 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) {
|
|||
|
||||
xcb_set_window_rect(conn, output->bar, bar_rect);
|
||||
|
||||
#if 0
|
||||
/* go through all workspaces and set force_reconfigure */
|
||||
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
||||
if (ws->output != output)
|
||||
|
@ -290,6 +272,7 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) {
|
|||
xcb_set_window_rect(conn, client->child, r);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -368,8 +351,7 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
|
|||
* (Re-)queries the outputs via RandR and stores them in the list of outputs.
|
||||
*
|
||||
*/
|
||||
void randr_query_outputs(xcb_connection_t *conn) {
|
||||
Workspace *ws;
|
||||
void randr_query_outputs() {
|
||||
Output *output, *other, *first;
|
||||
xcb_randr_get_screen_resources_current_cookie_t rcookie;
|
||||
resources_reply *res;
|
||||
|
@ -460,8 +442,9 @@ void randr_query_outputs(xcb_connection_t *conn) {
|
|||
if ((first = get_first_output()) == NULL)
|
||||
die("No usable outputs available\n");
|
||||
|
||||
bool needs_init = (first->current_workspace == NULL);
|
||||
//bool needs_init = (first->current_workspace == NULL);
|
||||
|
||||
#if 0
|
||||
TAILQ_FOREACH(ws, workspaces, workspaces) {
|
||||
if (ws->output != output)
|
||||
continue;
|
||||
|
@ -469,7 +452,7 @@ void randr_query_outputs(xcb_connection_t *conn) {
|
|||
workspace_assign_to(ws, first, true);
|
||||
if (!needs_init)
|
||||
continue;
|
||||
initialize_output(conn, first, ws);
|
||||
//initialize_output(conn, first, ws);
|
||||
needs_init = false;
|
||||
}
|
||||
|
||||
|
@ -479,7 +462,9 @@ void randr_query_outputs(xcb_connection_t *conn) {
|
|||
SLIST_REMOVE_HEAD(&(output->dock_clients), dock_clients);
|
||||
SLIST_INSERT_HEAD(&(first->dock_clients), dock, dock_clients);
|
||||
}
|
||||
output->current_workspace = NULL;
|
||||
|
||||
#endif
|
||||
//output->current_workspace = NULL;
|
||||
output->to_be_disabled = false;
|
||||
} else if (output->changed) {
|
||||
output_change_mode(conn, output);
|
||||
|
@ -492,8 +477,9 @@ void randr_query_outputs(xcb_connection_t *conn) {
|
|||
disable_randr(conn);
|
||||
}
|
||||
|
||||
ewmh_update_workarea();
|
||||
//ewmh_update_workarea();
|
||||
|
||||
#if 0
|
||||
/* Just go through each active output and associate one workspace */
|
||||
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||
if (!output->active || output->current_workspace != NULL)
|
||||
|
@ -501,9 +487,10 @@ void randr_query_outputs(xcb_connection_t *conn) {
|
|||
ws = get_first_workspace_for_output(output);
|
||||
initialize_output(conn, output, ws);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* render_layout flushes */
|
||||
render_layout(conn);
|
||||
tree_render();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -511,7 +498,7 @@ void randr_query_outputs(xcb_connection_t *conn) {
|
|||
* XRandR information to setup workspaces for each screen.
|
||||
*
|
||||
*/
|
||||
void initialize_randr(xcb_connection_t *conn, int *event_base) {
|
||||
void randr_init(int *event_base) {
|
||||
const xcb_query_extension_reply_t *extreply;
|
||||
|
||||
extreply = xcb_get_extension_data(conn, &xcb_randr_id);
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*/
|
||||
|
||||
#include "all.h"
|
||||
|
||||
/*
|
||||
* "Renders" the given container (and its children), meaning that all rects are
|
||||
* updated correctly. Note that this function does not call any xcb_*
|
||||
* functions, so the changes are completely done in memory only (and
|
||||
* side-effect free). As soon as you call x_push_changes(), the changes will be
|
||||
* updated in X11.
|
||||
*
|
||||
*/
|
||||
void render_con(Con *con) {
|
||||
printf("currently rendering node %p / %s / layout %d\n",
|
||||
con, con->name, con->layout);
|
||||
int children = 0;
|
||||
Con *child;
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes)
|
||||
children++;
|
||||
printf("children: %d, orientation = %d\n", children, con->orientation);
|
||||
|
||||
/* Copy container rect, subtract container border */
|
||||
/* This is the actually usable space inside this container for clients */
|
||||
Rect rect = con->rect;
|
||||
rect.x += 2;
|
||||
rect.y += 2;
|
||||
rect.width -= 2 * 2;
|
||||
rect.height -= 2 * 2;
|
||||
|
||||
int x = rect.x;
|
||||
int y = rect.y;
|
||||
|
||||
int i = 0;
|
||||
|
||||
printf("mapped = true\n");
|
||||
con->mapped = true;
|
||||
|
||||
/* Check for fullscreen nodes */
|
||||
Con *fullscreen = con_get_fullscreen_con(con);
|
||||
if (fullscreen) {
|
||||
LOG("got fs node: %p\n", fullscreen);
|
||||
fullscreen->rect = rect;
|
||||
render_con(fullscreen);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||
|
||||
/* default layout */
|
||||
if (con->layout == L_DEFAULT) {
|
||||
double percentage = 1.0 / children;
|
||||
if (child->percent > 0.0)
|
||||
percentage = child->percent;
|
||||
printf("child %p / %s requests percentage %f\n",
|
||||
child, child->name, percentage);
|
||||
|
||||
if (con->orientation == HORIZ) {
|
||||
child->rect.x = x;
|
||||
child->rect.y = y;
|
||||
child->rect.width = percentage * rect.width;
|
||||
child->rect.height = rect.height;
|
||||
x += child->rect.width;
|
||||
} else {
|
||||
child->rect.x = x;
|
||||
child->rect.y = y;
|
||||
child->rect.width = rect.width;
|
||||
child->rect.height = percentage * rect.height;
|
||||
y += child->rect.height;
|
||||
}
|
||||
|
||||
/* first we have the decoration, if this is a leaf node */
|
||||
if (con_is_leaf(child)) {
|
||||
printf("that child is a leaf node, subtracting deco\n");
|
||||
/* TODO: make a function for relative coords? */
|
||||
child->deco_rect.x = child->rect.x - con->rect.x;
|
||||
child->deco_rect.y = child->rect.y - con->rect.y;
|
||||
|
||||
child->rect.y += 17;
|
||||
child->rect.height -= 17;
|
||||
|
||||
child->deco_rect.width = child->rect.width;
|
||||
child->deco_rect.height = 17;
|
||||
}
|
||||
}
|
||||
|
||||
if (con->layout == L_STACKED) {
|
||||
printf("stacked con\n");
|
||||
child->rect.x = x;
|
||||
child->rect.y = y;
|
||||
child->rect.width = rect.width;
|
||||
child->rect.height = rect.height;
|
||||
|
||||
child->rect.y += (17 * children);
|
||||
child->rect.height -= (17 * children);
|
||||
|
||||
child->deco_rect.x = x - con->rect.x;
|
||||
child->deco_rect.y = y - con->rect.y + (i * 17);
|
||||
child->deco_rect.width = child->rect.width;
|
||||
child->deco_rect.height = 17;
|
||||
}
|
||||
|
||||
printf("child at (%d, %d) with (%d x %d)\n",
|
||||
child->rect.x, child->rect.y, child->rect.width, child->rect.height);
|
||||
printf("x now %d, y now %d\n", x, y);
|
||||
if (child->window) {
|
||||
/* depending on the border style, the rect of the child window
|
||||
* needs to be smaller */
|
||||
Rect *inset = &(child->window_rect);
|
||||
*inset = (Rect){0, 0, child->rect.width, child->rect.height};
|
||||
/* TODO: different border styles */
|
||||
inset->x += 2;
|
||||
inset->width -= 2 * 2;
|
||||
inset->height -= 2;
|
||||
}
|
||||
x_raise_con(child);
|
||||
render_con(child);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* in a stacking container, we ensure the focused client is raised */
|
||||
if (con->layout == L_STACKED) {
|
||||
Con *foc = TAILQ_FIRST(&(con->focus_head));
|
||||
x_raise_con(foc);
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
|
||||
LOG("render floating:\n");
|
||||
LOG("floating child at (%d,%d) with %d x %d\n", child->rect.x, child->rect.y, child->rect.width, child->rect.height);
|
||||
x_raise_con(child);
|
||||
render_con(child);
|
||||
}
|
||||
|
||||
printf("-- level up\n");
|
||||
}
|
406
src/table.c
406
src/table.c
|
@ -1,406 +0,0 @@
|
|||
/*
|
||||
* vim:ts=8:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
*
|
||||
* © 2009 Michael Stapelberg and contributors
|
||||
*
|
||||
* See file LICENSE for license information.
|
||||
*
|
||||
* table.c: Functions/macros for easy modifying/accessing of _the_ table (defining our
|
||||
* layout).
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "data.h"
|
||||
#include "table.h"
|
||||
#include "util.h"
|
||||
#include "i3.h"
|
||||
#include "layout.h"
|
||||
#include "config.h"
|
||||
#include "workspace.h"
|
||||
#include "log.h"
|
||||
|
||||
int current_workspace = 0;
|
||||
int num_workspaces = 1;
|
||||
struct workspaces_head *workspaces;
|
||||
/* Convenience pointer to the current workspace */
|
||||
Workspace *c_ws;
|
||||
int current_col = 0;
|
||||
int current_row = 0;
|
||||
|
||||
/*
|
||||
* Initialize table
|
||||
*
|
||||
*/
|
||||
void init_table() {
|
||||
workspaces = scalloc(sizeof(struct workspaces_head));
|
||||
TAILQ_INIT(workspaces);
|
||||
|
||||
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, bool skip_layout_switch) {
|
||||
Container *new;
|
||||
new = *container = scalloc(sizeof(Container));
|
||||
CIRCLEQ_INIT(&(new->clients));
|
||||
new->colspan = 1;
|
||||
new->rowspan = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add one row to the table
|
||||
*
|
||||
*/
|
||||
void expand_table_rows(Workspace *workspace) {
|
||||
workspace->rows++;
|
||||
|
||||
workspace->height_factor = realloc(workspace->height_factor, sizeof(float) * workspace->rows);
|
||||
workspace->height_factor[workspace->rows-1] = 0;
|
||||
|
||||
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, 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds one row at the head of the table
|
||||
*
|
||||
*/
|
||||
void expand_table_rows_at_head(Workspace *workspace) {
|
||||
workspace->rows++;
|
||||
|
||||
workspace->height_factor = realloc(workspace->height_factor, sizeof(float) * workspace->rows);
|
||||
|
||||
DLOG("rows = %d\n", workspace->rows);
|
||||
for (int rows = (workspace->rows - 1); rows >= 1; rows--) {
|
||||
DLOG("Moving height_factor %d (%f) to %d\n", rows-1, workspace->height_factor[rows-1], rows);
|
||||
workspace->height_factor[rows] = workspace->height_factor[rows-1];
|
||||
}
|
||||
|
||||
workspace->height_factor[0] = 0;
|
||||
|
||||
for (int cols = 0; cols < workspace->cols; cols++)
|
||||
workspace->table[cols] = realloc(workspace->table[cols], sizeof(Container*) * workspace->rows);
|
||||
|
||||
/* Move the other rows */
|
||||
for (int cols = 0; cols < workspace->cols; cols++)
|
||||
for (int rows = workspace->rows - 1; rows > 0; rows--) {
|
||||
DLOG("Moving row %d to %d\n", rows-1, rows);
|
||||
workspace->table[cols][rows] = workspace->table[cols][rows-1];
|
||||
workspace->table[cols][rows]->row = rows;
|
||||
}
|
||||
|
||||
for (int cols = 0; cols < workspace->cols; cols++)
|
||||
new_container(workspace, &(workspace->table[cols][0]), cols, 0, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add one column to the table
|
||||
*
|
||||
*/
|
||||
void expand_table_cols(Workspace *workspace) {
|
||||
workspace->cols++;
|
||||
|
||||
workspace->width_factor = realloc(workspace->width_factor, sizeof(float) * workspace->cols);
|
||||
workspace->width_factor[workspace->cols-1] = 0;
|
||||
|
||||
workspace->table = realloc(workspace->table, sizeof(Container**) * workspace->cols);
|
||||
workspace->table[workspace->cols-1] = scalloc(sizeof(Container*) * workspace->rows);
|
||||
|
||||
for (int c = 0; c < workspace->rows; 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Inserts one column at the table’s head
|
||||
*
|
||||
*/
|
||||
void expand_table_cols_at_head(Workspace *workspace) {
|
||||
workspace->cols++;
|
||||
|
||||
workspace->width_factor = realloc(workspace->width_factor, sizeof(float) * workspace->cols);
|
||||
|
||||
DLOG("cols = %d\n", workspace->cols);
|
||||
for (int cols = (workspace->cols - 1); cols >= 1; cols--) {
|
||||
DLOG("Moving width_factor %d (%f) to %d\n", cols-1, workspace->width_factor[cols-1], cols);
|
||||
workspace->width_factor[cols] = workspace->width_factor[cols-1];
|
||||
}
|
||||
|
||||
workspace->width_factor[0] = 0;
|
||||
|
||||
workspace->table = realloc(workspace->table, sizeof(Container**) * workspace->cols);
|
||||
workspace->table[workspace->cols-1] = scalloc(sizeof(Container*) * workspace->rows);
|
||||
|
||||
/* Move the other columns */
|
||||
for (int rows = 0; rows < workspace->rows; rows++)
|
||||
for (int cols = workspace->cols - 1; cols > 0; cols--) {
|
||||
DLOG("Moving col %d to %d\n", cols-1, cols);
|
||||
workspace->table[cols][rows] = workspace->table[cols-1][rows];
|
||||
workspace->table[cols][rows]->col = cols;
|
||||
}
|
||||
|
||||
for (int rows = 0; rows < workspace->rows; rows++)
|
||||
new_container(workspace, &(workspace->table[0][rows]), 0, rows, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shrinks the table by one column.
|
||||
*
|
||||
* The containers themselves are freed in move_columns_from() or move_rows_from(). Therefore, this
|
||||
* function may only be called from move_*() or after making sure that the containers are freed
|
||||
* properly.
|
||||
*
|
||||
*/
|
||||
static void shrink_table_cols(Workspace *workspace) {
|
||||
float free_space = workspace->width_factor[workspace->cols-1];
|
||||
|
||||
workspace->cols--;
|
||||
|
||||
/* Shrink the width_factor array */
|
||||
workspace->width_factor = realloc(workspace->width_factor, sizeof(float) * workspace->cols);
|
||||
|
||||
/* Free the container-pointers */
|
||||
free(workspace->table[workspace->cols]);
|
||||
|
||||
/* Re-allocate the table */
|
||||
workspace->table = realloc(workspace->table, sizeof(Container**) * workspace->cols);
|
||||
|
||||
/* Distribute the free space */
|
||||
if (free_space == 0)
|
||||
return;
|
||||
|
||||
for (int cols = (workspace->cols-1); cols >= 0; cols--) {
|
||||
if (workspace->width_factor[cols] == 0)
|
||||
continue;
|
||||
|
||||
DLOG("Added free space (%f) to %d (had %f)\n", free_space, cols,
|
||||
workspace->width_factor[cols]);
|
||||
workspace->width_factor[cols] += free_space;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* See shrink_table_cols()
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
DLOG("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
|
||||
*
|
||||
*/
|
||||
bool cell_exists(Workspace *ws, int col, int row) {
|
||||
return (col >= 0 && col < ws->cols) &&
|
||||
(row >= 0 && row < ws->rows);
|
||||
}
|
||||
|
||||
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 || old_container->mode == MODE_TABBED)
|
||||
leave_stack_mode(conn, old_container);
|
||||
|
||||
free(old_container);
|
||||
}
|
||||
|
||||
static void move_columns_from(xcb_connection_t *conn, Workspace *workspace, int cols) {
|
||||
DLOG("firstly freeing \n");
|
||||
|
||||
/* Free the columns which are cleaned up */
|
||||
for (int rows = 0; rows < workspace->rows; rows++)
|
||||
free_container(conn, workspace, cols-1, rows);
|
||||
|
||||
for (; cols < workspace->cols; cols++)
|
||||
for (int rows = 0; rows < workspace->rows; rows++) {
|
||||
DLOG("at col = %d, row = %d\n", cols, rows);
|
||||
Container *new_container = workspace->table[cols][rows];
|
||||
|
||||
DLOG("moving cols = %d to cols -1 = %d\n", cols, cols-1);
|
||||
workspace->table[cols-1][rows] = new_container;
|
||||
|
||||
new_container->row = rows;
|
||||
new_container->col = cols-1;
|
||||
}
|
||||
}
|
||||
|
||||
static void move_rows_from(xcb_connection_t *conn, Workspace *workspace, int rows) {
|
||||
for (int cols = 0; cols < workspace->cols; cols++)
|
||||
free_container(conn, workspace, cols, rows-1);
|
||||
|
||||
for (; rows < workspace->rows; rows++)
|
||||
for (int cols = 0; cols < workspace->cols; cols++) {
|
||||
Container *new_container = workspace->table[cols][rows];
|
||||
|
||||
DLOG("moving rows = %d to rows -1 = %d\n", rows, rows - 1);
|
||||
workspace->table[cols][rows-1] = new_container;
|
||||
|
||||
new_container->row = rows-1;
|
||||
new_container->col = cols;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Prints the table’s contents in human-readable form for debugging
|
||||
*
|
||||
*/
|
||||
void dump_table(xcb_connection_t *conn, Workspace *workspace) {
|
||||
DLOG("dump_table()\n");
|
||||
FOR_TABLE(workspace) {
|
||||
Container *con = workspace->table[cols][rows];
|
||||
DLOG("----\n");
|
||||
DLOG("at col=%d, row=%d\n", cols, rows);
|
||||
DLOG("currently_focused = %p\n", con->currently_focused);
|
||||
Client *loop;
|
||||
CIRCLEQ_FOREACH(loop, &(con->clients), clients) {
|
||||
DLOG("got client %08x / %s\n", loop->child, loop->name);
|
||||
}
|
||||
DLOG("----\n");
|
||||
}
|
||||
DLOG("done\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Shrinks the table by "compacting" it, that is, removing completely empty rows/columns
|
||||
*
|
||||
*/
|
||||
void cleanup_table(xcb_connection_t *conn, Workspace *workspace) {
|
||||
DLOG("cleanup_table()\n");
|
||||
|
||||
/* Check for empty columns if we got more than one column */
|
||||
for (int cols = 0; (workspace->cols > 1) && (cols < workspace->cols);) {
|
||||
bool completely_empty = true;
|
||||
for (int rows = 0; rows < workspace->rows; rows++)
|
||||
if (workspace->table[cols][rows]->currently_focused != NULL) {
|
||||
completely_empty = false;
|
||||
break;
|
||||
}
|
||||
if (completely_empty) {
|
||||
DLOG("Removing completely empty column %d\n", cols);
|
||||
if (cols < (workspace->cols - 1))
|
||||
move_columns_from(conn, workspace, cols+1);
|
||||
else {
|
||||
for (int rows = 0; rows < workspace->rows; rows++)
|
||||
free_container(conn, workspace, cols, rows);
|
||||
}
|
||||
shrink_table_cols(workspace);
|
||||
|
||||
if (workspace->current_col >= workspace->cols)
|
||||
workspace->current_col = workspace->cols - 1;
|
||||
} else cols++;
|
||||
}
|
||||
|
||||
/* Check for empty rows if we got more than one row */
|
||||
for (int rows = 0; (workspace->rows > 1) && (rows < workspace->rows);) {
|
||||
bool completely_empty = true;
|
||||
DLOG("Checking row %d\n", rows);
|
||||
for (int cols = 0; cols < workspace->cols; cols++)
|
||||
if (workspace->table[cols][rows]->currently_focused != NULL) {
|
||||
completely_empty = false;
|
||||
break;
|
||||
}
|
||||
if (completely_empty) {
|
||||
DLOG("Removing completely empty row %d\n", rows);
|
||||
if (rows < (workspace->rows - 1))
|
||||
move_rows_from(conn, workspace, rows+1);
|
||||
else {
|
||||
for (int cols = 0; cols < workspace->cols; cols++)
|
||||
free_container(conn, workspace, cols, rows);
|
||||
}
|
||||
shrink_table_rows(workspace);
|
||||
|
||||
if (workspace->current_row >= workspace->rows)
|
||||
workspace->current_row = workspace->rows - 1;
|
||||
} else rows++;
|
||||
}
|
||||
|
||||
/* Boundary checking for current_col and current_row */
|
||||
if (current_col >= c_ws->cols)
|
||||
current_col = c_ws->cols-1;
|
||||
|
||||
if (current_row >= c_ws->rows)
|
||||
current_row = c_ws->rows-1;
|
||||
|
||||
if (CUR_CELL->currently_focused != NULL)
|
||||
set_focus(conn, CUR_CELL->currently_focused, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fixes col/rowspan (makes sure there are no overlapping windows, obeys borders).
|
||||
*
|
||||
*/
|
||||
void fix_colrowspan(xcb_connection_t *conn, Workspace *workspace) {
|
||||
DLOG("Fixing col/rowspan\n");
|
||||
|
||||
FOR_TABLE(workspace) {
|
||||
Container *con = workspace->table[cols][rows];
|
||||
if (con->colspan > 1) {
|
||||
DLOG("gots one with colspan %d (at %d c, %d r)\n", con->colspan, cols, rows);
|
||||
while (con->colspan > 1 &&
|
||||
(!cell_exists(workspace, cols + (con->colspan-1), rows) &&
|
||||
workspace->table[cols + (con->colspan - 1)][rows]->currently_focused != NULL))
|
||||
con->colspan--;
|
||||
DLOG("fixed it to %d\n", con->colspan);
|
||||
}
|
||||
if (con->rowspan > 1) {
|
||||
DLOG("gots one with rowspan %d (at %d c, %d r)\n", con->rowspan, cols, rows);
|
||||
while (con->rowspan > 1 &&
|
||||
(!cell_exists(workspace, cols, rows + (con->rowspan - 1)) &&
|
||||
workspace->table[cols][rows + (con->rowspan - 1)]->currently_focused != NULL))
|
||||
con->rowspan--;
|
||||
DLOG("fixed it to %d\n", con->rowspan);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*/
|
||||
|
||||
#include "all.h"
|
||||
|
||||
struct Con *croot;
|
||||
struct Con *focused;
|
||||
|
||||
struct all_cons_head all_cons = TAILQ_HEAD_INITIALIZER(all_cons);
|
||||
|
||||
/*
|
||||
* Sets input focus to the given container. Will be updated in X11 in the next
|
||||
* run of x_push_changes().
|
||||
*
|
||||
*/
|
||||
void con_focus(Con *con) {
|
||||
assert(con != NULL);
|
||||
|
||||
/* 1: set focused-pointer to the new con */
|
||||
/* 2: exchange the position of the container in focus stack of the parent all the way up */
|
||||
TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
|
||||
TAILQ_INSERT_HEAD(&(con->parent->focus_head), con, focused);
|
||||
if (con->parent->parent != NULL)
|
||||
con_focus(con->parent);
|
||||
|
||||
focused = con;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads tree from ~/.i3/_restart.json
|
||||
*
|
||||
*/
|
||||
bool tree_restore() {
|
||||
char *globbed = glob_path("~/.i3/_restart.json");
|
||||
|
||||
if (!path_exists(globbed)) {
|
||||
LOG("%s does not exist, not restoring tree\n", globbed);
|
||||
free(globbed);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO: refactor the following */
|
||||
croot = con_new(NULL);
|
||||
focused = croot;
|
||||
|
||||
tree_append_json(globbed);
|
||||
char *old_restart = glob_path("~/.i3/_restart.json.old");
|
||||
unlink(old_restart);
|
||||
rename(globbed, old_restart);
|
||||
free(globbed);
|
||||
free(old_restart);
|
||||
|
||||
printf("appended tree, using new root\n");
|
||||
croot = TAILQ_FIRST(&(croot->nodes_head));
|
||||
printf("new root = %p\n", croot);
|
||||
Con *out = TAILQ_FIRST(&(croot->nodes_head));
|
||||
printf("out = %p\n", out);
|
||||
Con *ws = TAILQ_FIRST(&(out->nodes_head));
|
||||
printf("ws = %p\n", ws);
|
||||
con_focus(ws);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes the tree by creating the root node, adding all RandR outputs
|
||||
* to the tree (that means randr_init() has to be called before) and
|
||||
* assigning a workspace to each RandR output.
|
||||
*
|
||||
*/
|
||||
void tree_init() {
|
||||
Output *output;
|
||||
|
||||
croot = con_new(NULL);
|
||||
croot->name = "root";
|
||||
croot->type = CT_ROOT;
|
||||
|
||||
Con *ws;
|
||||
/* add the outputs */
|
||||
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||
if (!output->active)
|
||||
continue;
|
||||
|
||||
Con *oc = con_new(croot);
|
||||
oc->name = strdup(output->name);
|
||||
oc->type = CT_OUTPUT;
|
||||
oc->rect = output->rect;
|
||||
|
||||
/* add a workspace to this output */
|
||||
ws = con_new(oc);
|
||||
ws->name = strdup("1");
|
||||
ws->fullscreen_mode = CF_OUTPUT;
|
||||
}
|
||||
|
||||
con_focus(ws);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens an empty container in the current container
|
||||
*
|
||||
*/
|
||||
Con *tree_open_con(Con *con) {
|
||||
if (con == NULL) {
|
||||
/* every focusable Con has a parent (outputs have parent root) */
|
||||
con = focused->parent;
|
||||
/* If the parent is an output, we are on a workspace. In this case,
|
||||
* the new container needs to be opened as a leaf of the workspace. */
|
||||
if (con->type == CT_OUTPUT)
|
||||
con = focused;
|
||||
}
|
||||
|
||||
assert(con != NULL);
|
||||
|
||||
/* 3: re-calculate child->percent for each child */
|
||||
con_fix_percent(con, WINDOW_ADD);
|
||||
|
||||
/* 4: add a new container leaf to this con */
|
||||
Con *new = con_new(con);
|
||||
con_focus(new);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes the given container including all children
|
||||
*
|
||||
*/
|
||||
void tree_close(Con *con) {
|
||||
/* TODO: check floating clients and adjust old_parent if necessary */
|
||||
|
||||
/* Get the container which is next focused */
|
||||
Con *next;
|
||||
if (con->type == CT_FLOATING_CON) {
|
||||
next = TAILQ_NEXT(con, floating_windows);
|
||||
if (next == TAILQ_END(&(con->parent->floating_head)))
|
||||
next = con->parent;
|
||||
} else {
|
||||
next = TAILQ_NEXT(con, focused);
|
||||
if (next == TAILQ_END(&(con->parent->nodes_head)))
|
||||
next = con->parent;
|
||||
}
|
||||
|
||||
LOG("closing %p\n", con);
|
||||
Con *child;
|
||||
/* We cannot use TAILQ_FOREACH because the children get deleted
|
||||
* in their parent’s nodes_head */
|
||||
while (!TAILQ_EMPTY(&(con->nodes_head))) {
|
||||
child = TAILQ_FIRST(&(con->nodes_head));
|
||||
tree_close(child);
|
||||
}
|
||||
|
||||
/* kill the X11 part of this container */
|
||||
x_con_kill(con);
|
||||
|
||||
con_detach(con);
|
||||
con_fix_percent(con->parent, WINDOW_REMOVE);
|
||||
|
||||
if (con->window != NULL) {
|
||||
x_window_kill(con->window->id);
|
||||
free(con->window);
|
||||
}
|
||||
free(con->name);
|
||||
TAILQ_REMOVE(&all_cons, con, all_cons);
|
||||
free(con);
|
||||
|
||||
/* TODO: check if the container (or one of its children) was focused */
|
||||
con_focus(next);
|
||||
}
|
||||
|
||||
void tree_close_con() {
|
||||
assert(focused != NULL);
|
||||
if (focused->parent->type == CT_OUTPUT) {
|
||||
LOG("Cannot close workspace\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Kill con */
|
||||
tree_close(focused);
|
||||
}
|
||||
|
||||
/*
|
||||
* Splits (horizontally or vertically) the given container by creating a new
|
||||
* container which contains the old one and the future ones.
|
||||
*
|
||||
*/
|
||||
void tree_split(Con *con, orientation_t orientation) {
|
||||
/* 2: replace it with a new Con */
|
||||
Con *new = con_new(NULL);
|
||||
Con *parent = con->parent;
|
||||
TAILQ_REPLACE(&(parent->nodes_head), con, new, nodes);
|
||||
TAILQ_REPLACE(&(parent->focus_head), con, new, focused);
|
||||
new->parent = parent;
|
||||
new->orientation = orientation;
|
||||
|
||||
/* 3: add it as a child to the new Con */
|
||||
con_attach(con, new);
|
||||
}
|
||||
|
||||
void level_up() {
|
||||
/* We can focus up to the workspace, but not any higher in the tree */
|
||||
if (focused->parent->type != CT_CON) {
|
||||
printf("cannot go up\n");
|
||||
return;
|
||||
}
|
||||
con_focus(focused->parent);
|
||||
}
|
||||
|
||||
void level_down() {
|
||||
/* Go down the focus stack of the current node */
|
||||
Con *next = TAILQ_FIRST(&(focused->focus_head));
|
||||
if (next == TAILQ_END(&(focused->focus_head))) {
|
||||
printf("cannot go down\n");
|
||||
return;
|
||||
}
|
||||
con_focus(next);
|
||||
}
|
||||
|
||||
static void mark_unmapped(Con *con) {
|
||||
Con *current;
|
||||
|
||||
con->mapped = false;
|
||||
TAILQ_FOREACH(current, &(con->nodes_head), nodes)
|
||||
mark_unmapped(current);
|
||||
}
|
||||
|
||||
void tree_render() {
|
||||
if (croot == NULL)
|
||||
return;
|
||||
|
||||
printf("-- BEGIN RENDERING --\n");
|
||||
/* Reset map state for all nodes in tree */
|
||||
/* TODO: a nicer method to walk all nodes would be good, maybe? */
|
||||
mark_unmapped(croot);
|
||||
croot->mapped = true;
|
||||
|
||||
/* We start rendering at an output */
|
||||
Con *output;
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
printf("output %p / %s\n", output, output->name);
|
||||
render_con(output);
|
||||
}
|
||||
x_push_changes(croot);
|
||||
printf("-- END RENDERING --\n");
|
||||
}
|
||||
|
||||
void tree_next(char way, orientation_t orientation) {
|
||||
/* 1: get the first parent with the same orientation */
|
||||
Con *parent = focused->parent;
|
||||
while (parent->orientation != orientation) {
|
||||
LOG("need to go one level further up\n");
|
||||
/* if the current parent is an output, we are at a workspace
|
||||
* and the orientation still does not match */
|
||||
if (parent->parent->type == CT_OUTPUT)
|
||||
return;
|
||||
parent = parent->parent;
|
||||
}
|
||||
Con *current = TAILQ_FIRST(&(parent->focus_head));
|
||||
assert(current != TAILQ_END(&(parent->focus_head)));
|
||||
|
||||
/* 2: chose next (or previous) */
|
||||
Con *next;
|
||||
if (way == 'n') {
|
||||
next = TAILQ_NEXT(current, nodes);
|
||||
/* if we are at the end of the list, we need to wrap */
|
||||
if (next == TAILQ_END(&(parent->nodes_head)))
|
||||
next = TAILQ_FIRST(&(parent->nodes_head));
|
||||
} else {
|
||||
next = TAILQ_PREV(current, nodes_head, nodes);
|
||||
/* if we are at the end of the list, we need to wrap */
|
||||
if (next == TAILQ_END(&(parent->nodes_head)))
|
||||
next = TAILQ_LAST(&(parent->nodes_head), nodes_head);
|
||||
}
|
||||
|
||||
/* 3: focus choice comes in here. at the moment we will go down
|
||||
* until we find a window */
|
||||
/* TODO: check for window, atm we only go down as far as possible */
|
||||
while (TAILQ_FIRST(&(next->focus_head)) != TAILQ_END(&(next->focus_head)))
|
||||
next = TAILQ_FIRST(&(next->focus_head));
|
||||
|
||||
con_focus(next);
|
||||
}
|
||||
|
||||
void tree_move(char way, orientation_t orientation) {
|
||||
/* 1: get the first parent with the same orientation */
|
||||
Con *parent = focused->parent;
|
||||
bool level_changed = false;
|
||||
while (parent->orientation != orientation) {
|
||||
LOG("need to go one level further up\n");
|
||||
/* if the current parent is an output, we are at a workspace
|
||||
* and the orientation still does not match */
|
||||
if (parent->parent->type == CT_OUTPUT)
|
||||
return;
|
||||
parent = parent->parent;
|
||||
level_changed = true;
|
||||
}
|
||||
Con *current = TAILQ_FIRST(&(parent->focus_head));
|
||||
assert(current != TAILQ_END(&(parent->focus_head)));
|
||||
|
||||
/* 2: chose next (or previous) */
|
||||
Con *next = current;
|
||||
if (way == 'n') {
|
||||
LOG("i would insert it after %p / %s\n", next, next->name);
|
||||
if (!level_changed) {
|
||||
next = TAILQ_NEXT(next, nodes);
|
||||
if (next == TAILQ_END(&(next->parent->nodes_head))) {
|
||||
LOG("cannot move further to the right\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
con_detach(focused);
|
||||
focused->parent = next->parent;
|
||||
|
||||
TAILQ_INSERT_AFTER(&(next->parent->nodes_head), next, focused, nodes);
|
||||
TAILQ_INSERT_HEAD(&(next->parent->focus_head), focused, focused);
|
||||
/* TODO: don’t influence focus handling? */
|
||||
} else {
|
||||
LOG("i would insert it before %p / %s\n", current, current->name);
|
||||
if (!level_changed) {
|
||||
next = TAILQ_PREV(next, nodes_head, nodes);
|
||||
if (next == TAILQ_END(&(next->parent->nodes_head))) {
|
||||
LOG("cannot move further\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
con_detach(focused);
|
||||
focused->parent = next->parent;
|
||||
|
||||
TAILQ_INSERT_BEFORE(next, focused, nodes);
|
||||
TAILQ_INSERT_HEAD(&(next->parent->focus_head), focused, focused);
|
||||
/* TODO: don’t influence focus handling? */
|
||||
}
|
||||
}
|
86
src/util.c
86
src/util.c
|
@ -10,34 +10,18 @@
|
|||
* util.c: Utility functions, which can be useful everywhere.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <iconv.h>
|
||||
#if defined(__OpenBSD__)
|
||||
#include <sys/cdefs.h>
|
||||
#endif
|
||||
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "i3.h"
|
||||
#include "data.h"
|
||||
#include "table.h"
|
||||
#include "layout.h"
|
||||
#include "util.h"
|
||||
#include "xcb.h"
|
||||
#include "client.h"
|
||||
#include "log.h"
|
||||
#include "ewmh.h"
|
||||
#include "manage.h"
|
||||
#include "workspace.h"
|
||||
#include "ipc.h"
|
||||
#include "all.h"
|
||||
|
||||
static iconv_t conversion_descriptor = 0;
|
||||
//static iconv_t conversion_descriptor = 0;
|
||||
struct keyvalue_table_head by_parent = TAILQ_HEAD_INITIALIZER(by_parent);
|
||||
struct keyvalue_table_head by_child = TAILQ_HEAD_INITIALIZER(by_child);
|
||||
|
||||
|
@ -77,12 +61,20 @@ void *scalloc(size_t size) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void *srealloc(void *ptr, size_t size) {
|
||||
void *result = realloc(ptr, size);
|
||||
exit_if_null(result, "Error: out memory (realloc(%zd))\n", size);
|
||||
return result;
|
||||
}
|
||||
|
||||
char *sstrdup(const char *str) {
|
||||
char *result = strdup(str);
|
||||
exit_if_null(result, "Error: out of memory (strdup())\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
/*
|
||||
* The table_* functions emulate the behaviour of libxcb-wm, which in libxcb 0.3.4 suddenly
|
||||
* vanished. Great.
|
||||
|
@ -120,7 +112,7 @@ void *table_get(struct keyvalue_table_head *head, uint32_t key) {
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Starts the given application by passing it through a shell. We use double fork
|
||||
* to avoid zombie processes. As the started application’s parent exits (immediately),
|
||||
|
@ -132,6 +124,7 @@ void *table_get(struct keyvalue_table_head *head, uint32_t key) {
|
|||
*
|
||||
*/
|
||||
void start_application(const char *command) {
|
||||
LOG("executing: %s\n", command);
|
||||
if (fork() == 0) {
|
||||
/* Child process */
|
||||
if (fork() == 0) {
|
||||
|
@ -165,6 +158,7 @@ void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie, char *err_mes
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Converts the given string to UCS-2 big endian for use with
|
||||
* xcb_image_text_16(). The amount of real glyphs is stored in real_strlen,
|
||||
|
@ -482,6 +476,7 @@ done:
|
|||
FREE(to_title_ucs);
|
||||
return matching;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Goes through the list of arguments (for exec()) and checks if the given argument
|
||||
|
@ -506,15 +501,58 @@ static char **append_argument(char **original, char *argument) {
|
|||
return result;
|
||||
}
|
||||
|
||||
#define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__)
|
||||
#define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str))
|
||||
|
||||
void store_restart_layout() {
|
||||
yajl_gen gen = yajl_gen_alloc(NULL, NULL);
|
||||
|
||||
dump_node(gen, croot, true);
|
||||
|
||||
const unsigned char *payload;
|
||||
unsigned int length;
|
||||
y(get_buf, &payload, &length);
|
||||
|
||||
char *globbed = glob_path("~/.i3/_restart.json");
|
||||
int fd = open(globbed, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
||||
free(globbed);
|
||||
if (fd == -1) {
|
||||
perror("open()");
|
||||
return;
|
||||
}
|
||||
|
||||
int written = 0;
|
||||
while (written < length) {
|
||||
int n = write(fd, payload + written, length - written);
|
||||
/* TODO: correct error-handling */
|
||||
if (n == -1) {
|
||||
perror("write()");
|
||||
return;
|
||||
}
|
||||
if (n == 0) {
|
||||
printf("write == 0?\n");
|
||||
return;
|
||||
}
|
||||
written += n;
|
||||
printf("written: %d of %d\n", written, length);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
printf("layout: %.*s\n", length, payload);
|
||||
|
||||
y(free);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restart i3 in-place
|
||||
* appends -a to argument list to disable autostart
|
||||
*
|
||||
*/
|
||||
void i3_restart() {
|
||||
restore_geometry(global_conn);
|
||||
store_restart_layout();
|
||||
restore_geometry();
|
||||
|
||||
ipc_shutdown();
|
||||
//ipc_shutdown();
|
||||
|
||||
LOG("restarting \"%s\"...\n", start_argv[0]);
|
||||
/* make sure -a is in the argument list or append it */
|
||||
|
@ -524,6 +562,8 @@ void i3_restart() {
|
|||
/* not reached */
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
|
||||
/*
|
||||
|
@ -559,4 +599,4 @@ void *memmem(const void *l, size_t l_len, const void *s, size_t s_len) {
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,25 +10,11 @@
|
|||
* workspace.c: Functions for modifying workspaces
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "data.h"
|
||||
#include "i3.h"
|
||||
#include "config.h"
|
||||
#include "xcb.h"
|
||||
#include "table.h"
|
||||
#include "randr.h"
|
||||
#include "layout.h"
|
||||
#include "workspace.h"
|
||||
#include "client.h"
|
||||
#include "log.h"
|
||||
#include "ewmh.h"
|
||||
#include "ipc.h"
|
||||
#include "all.h"
|
||||
|
||||
extern Con *focused;
|
||||
|
||||
/*
|
||||
* Returns a pointer to the workspace with the given number (starting at 0),
|
||||
|
@ -36,38 +22,40 @@
|
|||
* 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;
|
||||
Con *workspace_get(const char *num) {
|
||||
Con *output, *workspace = NULL, *current;
|
||||
|
||||
/* If we are still there, we could not find the requested workspace. */
|
||||
int last_ws = TAILQ_LAST(workspaces, workspaces_head)->num;
|
||||
/* TODO: could that look like this in the future?
|
||||
GET_MATCHING_NODE(workspace, croot, strcasecmp(current->name, num) != 0);
|
||||
*/
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||
TAILQ_FOREACH(current, &(output->nodes_head), nodes) {
|
||||
if (strcasecmp(current->name, num) != 0)
|
||||
continue;
|
||||
|
||||
DLOG("We need to initialize that one, last ws = %d\n", last_ws);
|
||||
workspace = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int c = last_ws; c < number; c++) {
|
||||
DLOG("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("should switch to ws %s\n", num);
|
||||
if (workspace == NULL) {
|
||||
LOG("need to create this one\n");
|
||||
output = con_get_output(focused);
|
||||
LOG("got output %p\n", output);
|
||||
workspace = con_new(output);
|
||||
workspace->name = strdup(num);
|
||||
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
||||
}
|
||||
DLOG("done\n");
|
||||
|
||||
ewmh_update_workarea();
|
||||
//ewmh_update_workarea();
|
||||
|
||||
return ws;
|
||||
return workspace;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
/*
|
||||
* Sets the name (or just its number) for the given workspace. This has to
|
||||
* be called for every workspace as the rendering function
|
||||
|
@ -105,22 +93,27 @@ void workspace_set_name(Workspace *ws, const char *name) {
|
|||
bool workspace_is_visible(Workspace *ws) {
|
||||
return (ws->output != NULL && ws->output->current_workspace == ws);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Switches to the given workspace
|
||||
*
|
||||
*/
|
||||
void workspace_show(xcb_connection_t *conn, int workspace) {
|
||||
bool need_warp = false;
|
||||
xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
|
||||
/* t_ws (to workspace) is just a convenience pointer to the workspace we’re switching to */
|
||||
Workspace *t_ws = workspace_get(workspace-1);
|
||||
void workspace_show(const char *num) {
|
||||
Con *workspace, *current;
|
||||
|
||||
DLOG("show_workspace(%d)\n", workspace);
|
||||
workspace = workspace_get(num);
|
||||
workspace->fullscreen_mode = CF_OUTPUT;
|
||||
/* disable fullscreen */
|
||||
TAILQ_FOREACH(current, &(workspace->parent->nodes_head), nodes)
|
||||
current->fullscreen_mode = CF_NONE;
|
||||
|
||||
/* Store current_row/current_col */
|
||||
c_ws->current_row = current_row;
|
||||
c_ws->current_col = current_col;
|
||||
LOG("switching to %p\n", workspace);
|
||||
con_focus(workspace);
|
||||
workspace->fullscreen_mode = CF_OUTPUT;
|
||||
LOG("focused now = %p / %s\n", focused, focused->name);
|
||||
#if 0
|
||||
|
||||
/* Check if the workspace has not been used yet */
|
||||
workspace_initialize(t_ws, c_ws->output, false);
|
||||
|
@ -209,8 +202,10 @@ void workspace_show(xcb_connection_t *conn, int workspace) {
|
|||
client_warp_pointer_into(conn, last_focused);
|
||||
xcb_flush(conn);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Assigns the given workspace to the given output by correctly updating its
|
||||
* state and reconfiguring all the clients on this workspace.
|
||||
|
@ -475,3 +470,4 @@ int workspace_height(Workspace *ws) {
|
|||
|
||||
return height;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*/
|
||||
|
||||
#include "all.h"
|
||||
|
||||
/* Stores the X11 window ID of the currently focused window */
|
||||
static xcb_window_t focused_id = XCB_NONE;
|
||||
|
||||
/*
|
||||
* Describes the X11 state we may modify (map state, position, window stack).
|
||||
* There is one entry per container. The state represents the current situation
|
||||
* as X11 sees it (with the exception of the order in the state_head CIRCLEQ,
|
||||
* which represents the order that will be pushed to X11, while old_state_head
|
||||
* represents the current order). It will be updated in x_push_changes().
|
||||
*
|
||||
*/
|
||||
typedef struct con_state {
|
||||
xcb_window_t id;
|
||||
bool mapped;
|
||||
Rect rect;
|
||||
Rect window_rect;
|
||||
|
||||
bool initial;
|
||||
|
||||
CIRCLEQ_ENTRY(con_state) state;
|
||||
CIRCLEQ_ENTRY(con_state) old_state;
|
||||
} con_state;
|
||||
|
||||
CIRCLEQ_HEAD(state_head, con_state) state_head =
|
||||
CIRCLEQ_HEAD_INITIALIZER(state_head);
|
||||
|
||||
CIRCLEQ_HEAD(old_state_head, con_state) old_state_head =
|
||||
CIRCLEQ_HEAD_INITIALIZER(old_state_head);
|
||||
|
||||
/*
|
||||
* Returns the container state for the given frame. This function always
|
||||
* returns a container state (otherwise, there is a bug in the code and the
|
||||
* container state of a container for which x_con_init() was not called was
|
||||
* requested).
|
||||
*
|
||||
*/
|
||||
static con_state *state_for_frame(xcb_window_t window) {
|
||||
con_state *state;
|
||||
CIRCLEQ_FOREACH(state, &state_head, state)
|
||||
if (state->id == window)
|
||||
return state;
|
||||
|
||||
/* TODO: better error handling? */
|
||||
ELOG("No state found\n");
|
||||
assert(true);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes the X11 part for the given container. Called exactly once for
|
||||
* every container from con_new().
|
||||
*
|
||||
*/
|
||||
void x_con_init(Con *con) {
|
||||
/* TODO: maybe create the window when rendering first? we could then even
|
||||
* get the initial geometry right */
|
||||
|
||||
uint32_t mask = 0;
|
||||
uint32_t values[2];
|
||||
|
||||
/* our own frames should not be managed */
|
||||
mask |= XCB_CW_OVERRIDE_REDIRECT;
|
||||
values[0] = 1;
|
||||
|
||||
/* We want to know when… */
|
||||
mask |= XCB_CW_EVENT_MASK;
|
||||
values[1] = FRAME_EVENT_MASK;
|
||||
|
||||
Rect dims = { -15, -15, 10, 10 };
|
||||
con->frame = create_window(conn, dims, XCB_WINDOW_CLASS_INPUT_OUTPUT, -1, false, mask, values);
|
||||
con->gc = xcb_generate_id(conn);
|
||||
xcb_create_gc(conn, con->gc, con->frame, 0, 0);
|
||||
|
||||
struct con_state *state = scalloc(sizeof(struct con_state));
|
||||
state->id = con->frame;
|
||||
state->mapped = false;
|
||||
state->initial = true;
|
||||
CIRCLEQ_INSERT_HEAD(&state_head, state, state);
|
||||
CIRCLEQ_INSERT_HEAD(&old_state_head, state, old_state);
|
||||
LOG("adding new state for window id 0x%08x\n", state->id);
|
||||
}
|
||||
|
||||
void x_con_kill(Con *con) {
|
||||
con_state *state;
|
||||
|
||||
xcb_destroy_window(conn, con->frame);
|
||||
state = state_for_frame(con->frame);
|
||||
CIRCLEQ_REMOVE(&state_head, state, state);
|
||||
CIRCLEQ_REMOVE(&old_state_head, state, old_state);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the client supports the given protocol atom (like WM_DELETE_WINDOW)
|
||||
*
|
||||
*/
|
||||
static bool window_supports_protocol(xcb_window_t window, xcb_atom_t atom) {
|
||||
xcb_get_property_cookie_t cookie;
|
||||
xcb_get_wm_protocols_reply_t protocols;
|
||||
bool result = false;
|
||||
|
||||
cookie = xcb_get_wm_protocols_unchecked(conn, window, atoms[WM_PROTOCOLS]);
|
||||
if (xcb_get_wm_protocols_reply(conn, cookie, &protocols, NULL) != 1)
|
||||
return false;
|
||||
|
||||
/* Check if the client’s protocols have the requested atom set */
|
||||
for (uint32_t i = 0; i < protocols.atoms_len; i++)
|
||||
if (protocols.atoms[i] == atom)
|
||||
result = true;
|
||||
|
||||
xcb_get_wm_protocols_reply_wipe(&protocols);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void x_window_kill(xcb_window_t window) {
|
||||
/* if this window does not support WM_DELETE_WINDOW, we kill it the hard way */
|
||||
if (!window_supports_protocol(window, atoms[WM_DELETE_WINDOW])) {
|
||||
LOG("Killing window the hard way\n");
|
||||
xcb_kill_client(conn, window);
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_client_message_event_t ev;
|
||||
|
||||
memset(&ev, 0, sizeof(xcb_client_message_event_t));
|
||||
|
||||
ev.response_type = XCB_CLIENT_MESSAGE;
|
||||
ev.window = window;
|
||||
ev.type = atoms[WM_PROTOCOLS];
|
||||
ev.format = 32;
|
||||
ev.data.data32[0] = atoms[WM_DELETE_WINDOW];
|
||||
ev.data.data32[1] = XCB_CURRENT_TIME;
|
||||
|
||||
LOG("Sending WM_DELETE to the client\n");
|
||||
xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char*)&ev);
|
||||
xcb_flush(conn);
|
||||
}
|
||||
|
||||
void x_draw_decoration(Con *con) {
|
||||
Con *parent;
|
||||
|
||||
parent = con->parent;
|
||||
|
||||
if (con == focused)
|
||||
xcb_change_gc_single(conn, parent->gc, XCB_GC_FOREGROUND, get_colorpixel("#FF0000"));
|
||||
else xcb_change_gc_single(conn, parent->gc, XCB_GC_FOREGROUND, get_colorpixel("#0C0C0C"));
|
||||
xcb_rectangle_t drect = { con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height };
|
||||
xcb_poly_fill_rectangle(conn, parent->frame, parent->gc, 1, &drect);
|
||||
|
||||
if (con->window == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (con->window->class == NULL) {
|
||||
LOG("not rendering decoration, not yet known\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LOG("should render text %s onto %p / %s\n", con->window->class, parent, parent->name);
|
||||
|
||||
xcb_change_gc_single(conn, parent->gc, XCB_GC_FOREGROUND, get_colorpixel("#FFFFFF"));
|
||||
xcb_image_text_8(
|
||||
conn,
|
||||
strlen(con->window->class),
|
||||
parent->frame,
|
||||
parent->gc,
|
||||
con->deco_rect.x,
|
||||
con->deco_rect.y + 14,
|
||||
con->window->class
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function pushes the properties of each node of the layout tree to
|
||||
* X11 if they have changed (like the map state, position of the window, …).
|
||||
* It recursively traverses all children of the given node.
|
||||
*
|
||||
*/
|
||||
static void x_push_node(Con *con) {
|
||||
Con *current;
|
||||
con_state *state;
|
||||
|
||||
LOG("Pushing changes for node %p / %s\n", con, con->name);
|
||||
state = state_for_frame(con->frame);
|
||||
|
||||
/* map/unmap if map state changed */
|
||||
if (state->mapped != con->mapped) {
|
||||
if (!con->mapped) {
|
||||
LOG("unmapping container\n");
|
||||
xcb_unmap_window(conn, con->frame);
|
||||
} else {
|
||||
if (state->initial && con->window != NULL) {
|
||||
LOG("mapping child window\n");
|
||||
xcb_map_window(conn, con->window->id);
|
||||
}
|
||||
LOG("mapping container\n");
|
||||
xcb_map_window(conn, con->frame);
|
||||
}
|
||||
state->mapped = con->mapped;
|
||||
}
|
||||
|
||||
/* set new position if rect changed */
|
||||
if (memcmp(&(state->rect), &(con->rect), sizeof(Rect)) != 0) {
|
||||
LOG("setting rect (%d, %d, %d, %d)\n", con->rect.x, con->rect.y, con->rect.width, con->rect.height);
|
||||
xcb_set_window_rect(conn, con->frame, con->rect);
|
||||
memcpy(&(state->rect), &(con->rect), sizeof(Rect));
|
||||
}
|
||||
|
||||
/* dito, but for child windows */
|
||||
if (memcmp(&(state->window_rect), &(con->window_rect), sizeof(Rect)) != 0) {
|
||||
LOG("setting window rect (%d, %d, %d, %d)\n",
|
||||
con->window_rect.x, con->window_rect.y, con->window_rect.width, con->window_rect.height);
|
||||
xcb_set_window_rect(conn, con->window->id, con->window_rect);
|
||||
memcpy(&(state->rect), &(con->rect), sizeof(Rect));
|
||||
}
|
||||
|
||||
/* handle all children and floating windows of this node */
|
||||
TAILQ_FOREACH(current, &(con->nodes_head), nodes)
|
||||
x_push_node(current);
|
||||
|
||||
TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
|
||||
x_push_node(current);
|
||||
|
||||
if (con->type != CT_ROOT && con->type != CT_OUTPUT)
|
||||
x_draw_decoration(con);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pushes all changes (state of each node, see x_push_node() and the window
|
||||
* stack) to X11.
|
||||
*
|
||||
*/
|
||||
void x_push_changes(Con *con) {
|
||||
con_state *state;
|
||||
|
||||
LOG("\n\n PUSHING CHANGES\n\n");
|
||||
x_push_node(con);
|
||||
|
||||
LOG("-- PUSHING FOCUS STACK --\n");
|
||||
/* X11 correctly represents the stack if we push it from bottom to top */
|
||||
CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
|
||||
LOG("stack: 0x%08x\n", state->id);
|
||||
con_state *prev = CIRCLEQ_PREV(state, state);
|
||||
con_state *old_prev = CIRCLEQ_PREV(state, old_state);
|
||||
if ((state->initial || prev != old_prev) && prev != CIRCLEQ_END(&state_head)) {
|
||||
state->initial = false;
|
||||
LOG("Stacking 0x%08x above 0x%08x\n", prev->id, state->id);
|
||||
uint32_t mask = 0;
|
||||
mask |= XCB_CONFIG_WINDOW_SIBLING;
|
||||
mask |= XCB_CONFIG_WINDOW_STACK_MODE;
|
||||
uint32_t values[] = {state->id, XCB_STACK_MODE_ABOVE};
|
||||
|
||||
xcb_configure_window(conn, prev->id, mask, values);
|
||||
}
|
||||
}
|
||||
|
||||
xcb_window_t to_focus = focused->frame;
|
||||
if (focused->window != NULL)
|
||||
to_focus = focused->window->id;
|
||||
|
||||
if (focused_id != to_focus) {
|
||||
LOG("Updating focus\n");
|
||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME);
|
||||
}
|
||||
|
||||
xcb_flush(conn);
|
||||
LOG("\n\n ENDING CHANGES\n\n");
|
||||
|
||||
/* save the current stack as old stack */
|
||||
CIRCLEQ_FOREACH(state, &state_head, state) {
|
||||
CIRCLEQ_REMOVE(&old_state_head, state, old_state);
|
||||
CIRCLEQ_INSERT_TAIL(&old_state_head, state, old_state);
|
||||
}
|
||||
CIRCLEQ_FOREACH(state, &old_state_head, old_state) {
|
||||
LOG("old stack: 0x%08x\n", state->id);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Raises the specified container in the internal stack of X windows. The
|
||||
* next call to x_push_changes() will make the change visible in X11.
|
||||
*
|
||||
*/
|
||||
void x_raise_con(Con *con) {
|
||||
con_state *state;
|
||||
LOG("raising in new stack: %p / %s\n", con, con->name);
|
||||
state = state_for_frame(con->frame);
|
||||
|
||||
LOG("found state entry, moving to top\n");
|
||||
CIRCLEQ_REMOVE(&state_head, state, state);
|
||||
CIRCLEQ_INSERT_HEAD(&state_head, state, state);
|
||||
}
|
16
src/xcb.c
16
src/xcb.c
|
@ -10,18 +10,8 @@
|
|||
* xcb.c: Helper functions for easier usage of XCB
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
|
||||
#include "i3.h"
|
||||
#include "util.h"
|
||||
#include "xcb.h"
|
||||
#include "log.h"
|
||||
#include "all.h"
|
||||
|
||||
TAILQ_HEAD(cached_fonts_head, Font) cached_fonts = TAILQ_HEAD_INITIALIZER(cached_fonts);
|
||||
unsigned int xcb_numlock_mask;
|
||||
|
@ -74,7 +64,7 @@ i3Font *load_font(xcb_connection_t *conn, const char *pattern) {
|
|||
* This has to be done by the caller.
|
||||
*
|
||||
*/
|
||||
uint32_t get_colorpixel(xcb_connection_t *conn, char *hex) {
|
||||
uint32_t get_colorpixel(char *hex) {
|
||||
char strgroups[3][3] = {{hex[1], hex[2], '\0'},
|
||||
{hex[3], hex[4], '\0'},
|
||||
{hex[5], hex[6], '\0'}};
|
||||
|
@ -182,6 +172,7 @@ void fake_configure_notify(xcb_connection_t *conn, Rect r, xcb_window_t window)
|
|||
xcb_flush(conn);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Generates a configure_notify_event with absolute coordinates (relative to the X root
|
||||
* window, not to the client’s frame) for the given client.
|
||||
|
@ -197,6 +188,7 @@ void fake_absolute_configure_notify(xcb_connection_t *conn, Client *client) {
|
|||
|
||||
fake_configure_notify(conn, absolute, client->child);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Finds out which modifier mask is the one for numlock, as the user may change this.
|
||||
|
|
150
src/xinerama.c
150
src/xinerama.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* vim:ts=8:expandtab
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
*
|
||||
|
@ -12,20 +12,10 @@
|
|||
* driver which does not support RandR in 2010 *sigh*.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xinerama.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "data.h"
|
||||
#include "util.h"
|
||||
#include "xinerama.h"
|
||||
#include "workspace.h"
|
||||
#include "log.h"
|
||||
#include "randr.h"
|
||||
#include "all.h"
|
||||
|
||||
static int num_screens;
|
||||
|
||||
|
@ -34,12 +24,12 @@ static int num_screens;
|
|||
*
|
||||
*/
|
||||
static Output *get_screen_at(int x, int y) {
|
||||
Output *output;
|
||||
TAILQ_FOREACH(output, &outputs, outputs)
|
||||
if (output->rect.x == x && output->rect.y == y)
|
||||
return output;
|
||||
Output *output;
|
||||
TAILQ_FOREACH(output, &outputs, outputs)
|
||||
if (output->rect.x == x && output->rect.y == y)
|
||||
return output;
|
||||
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -48,52 +38,52 @@ static Output *get_screen_at(int x, int y) {
|
|||
*
|
||||
*/
|
||||
static void query_screens(xcb_connection_t *conn) {
|
||||
xcb_xinerama_query_screens_reply_t *reply;
|
||||
xcb_xinerama_screen_info_t *screen_info;
|
||||
xcb_xinerama_query_screens_reply_t *reply;
|
||||
xcb_xinerama_screen_info_t *screen_info;
|
||||
|
||||
reply = xcb_xinerama_query_screens_reply(conn, xcb_xinerama_query_screens_unchecked(conn), NULL);
|
||||
if (!reply) {
|
||||
ELOG("Couldn't get Xinerama screens\n");
|
||||
return;
|
||||
}
|
||||
screen_info = xcb_xinerama_query_screens_screen_info(reply);
|
||||
int screens = xcb_xinerama_query_screens_screen_info_length(reply);
|
||||
reply = xcb_xinerama_query_screens_reply(conn, xcb_xinerama_query_screens_unchecked(conn), NULL);
|
||||
if (!reply) {
|
||||
ELOG("Couldn't get Xinerama screens\n");
|
||||
return;
|
||||
}
|
||||
screen_info = xcb_xinerama_query_screens_screen_info(reply);
|
||||
int screens = xcb_xinerama_query_screens_screen_info_length(reply);
|
||||
|
||||
for (int screen = 0; screen < screens; screen++) {
|
||||
Output *s = get_screen_at(screen_info[screen].x_org, screen_info[screen].y_org);
|
||||
if (s != NULL) {
|
||||
DLOG("Re-used old Xinerama screen %p\n", s);
|
||||
/* This screen already exists. We use the littlest screen so that the user
|
||||
can always see the complete workspace */
|
||||
s->rect.width = min(s->rect.width, screen_info[screen].width);
|
||||
s->rect.height = min(s->rect.height, screen_info[screen].height);
|
||||
} else {
|
||||
s = scalloc(sizeof(Output));
|
||||
asprintf(&(s->name), "xinerama-%d", num_screens);
|
||||
DLOG("Created new Xinerama screen %s (%p)\n", s->name, s);
|
||||
s->active = true;
|
||||
s->rect.x = screen_info[screen].x_org;
|
||||
s->rect.y = screen_info[screen].y_org;
|
||||
s->rect.width = screen_info[screen].width;
|
||||
s->rect.height = screen_info[screen].height;
|
||||
/* We always treat the screen at 0x0 as the primary screen */
|
||||
if (s->rect.x == 0 && s->rect.y == 0)
|
||||
TAILQ_INSERT_HEAD(&outputs, s, outputs);
|
||||
else TAILQ_INSERT_TAIL(&outputs, s, outputs);
|
||||
num_screens++;
|
||||
}
|
||||
|
||||
DLOG("found Xinerama screen: %d x %d at %d x %d\n",
|
||||
screen_info[screen].width, screen_info[screen].height,
|
||||
screen_info[screen].x_org, screen_info[screen].y_org);
|
||||
for (int screen = 0; screen < screens; screen++) {
|
||||
Output *s = get_screen_at(screen_info[screen].x_org, screen_info[screen].y_org);
|
||||
if (s != NULL) {
|
||||
DLOG("Re-used old Xinerama screen %p\n", s);
|
||||
/* This screen already exists. We use the littlest screen so that the user
|
||||
can always see the complete workspace */
|
||||
s->rect.width = min(s->rect.width, screen_info[screen].width);
|
||||
s->rect.height = min(s->rect.height, screen_info[screen].height);
|
||||
} else {
|
||||
s = scalloc(sizeof(Output));
|
||||
asprintf(&(s->name), "xinerama-%d", num_screens);
|
||||
DLOG("Created new Xinerama screen %s (%p)\n", s->name, s);
|
||||
s->active = true;
|
||||
s->rect.x = screen_info[screen].x_org;
|
||||
s->rect.y = screen_info[screen].y_org;
|
||||
s->rect.width = screen_info[screen].width;
|
||||
s->rect.height = screen_info[screen].height;
|
||||
/* We always treat the screen at 0x0 as the primary screen */
|
||||
if (s->rect.x == 0 && s->rect.y == 0)
|
||||
TAILQ_INSERT_HEAD(&outputs, s, outputs);
|
||||
else TAILQ_INSERT_TAIL(&outputs, s, outputs);
|
||||
num_screens++;
|
||||
}
|
||||
|
||||
free(reply);
|
||||
DLOG("found Xinerama screen: %d x %d at %d x %d\n",
|
||||
screen_info[screen].width, screen_info[screen].height,
|
||||
screen_info[screen].x_org, screen_info[screen].y_org);
|
||||
}
|
||||
|
||||
if (num_screens == 0) {
|
||||
ELOG("No screens found. Please fix your setup. i3 will exit now.\n");
|
||||
exit(0);
|
||||
}
|
||||
free(reply);
|
||||
|
||||
if (num_screens == 0) {
|
||||
ELOG("No screens found. Please fix your setup. i3 will exit now.\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -101,28 +91,30 @@ static void query_screens(xcb_connection_t *conn) {
|
|||
* information to setup workspaces for each screen.
|
||||
*
|
||||
*/
|
||||
void initialize_xinerama(xcb_connection_t *conn) {
|
||||
if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) {
|
||||
DLOG("Xinerama extension not found, disabling.\n");
|
||||
void xinerama_init() {
|
||||
if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) {
|
||||
DLOG("Xinerama extension not found, disabling.\n");
|
||||
disable_randr(conn);
|
||||
} else {
|
||||
xcb_xinerama_is_active_reply_t *reply;
|
||||
reply = xcb_xinerama_is_active_reply(conn, xcb_xinerama_is_active(conn), NULL);
|
||||
|
||||
if (reply == NULL || !reply->state) {
|
||||
DLOG("Xinerama is not active (in your X-Server), disabling.\n");
|
||||
disable_randr(conn);
|
||||
} else {
|
||||
xcb_xinerama_is_active_reply_t *reply;
|
||||
reply = xcb_xinerama_is_active_reply(conn, xcb_xinerama_is_active(conn), NULL);
|
||||
} else
|
||||
query_screens(conn);
|
||||
|
||||
if (reply == NULL || !reply->state) {
|
||||
DLOG("Xinerama is not active (in your X-Server), disabling.\n");
|
||||
disable_randr(conn);
|
||||
} else
|
||||
query_screens(conn);
|
||||
FREE(reply);
|
||||
}
|
||||
|
||||
FREE(reply);
|
||||
}
|
||||
|
||||
Output *output;
|
||||
Workspace *ws;
|
||||
/* Just go through each active output and associate one workspace */
|
||||
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||
ws = get_first_workspace_for_output(output);
|
||||
initialize_output(conn, output, ws);
|
||||
}
|
||||
#if 0
|
||||
Output *output;
|
||||
Workspace *ws;
|
||||
/* Just go through each active output and associate one workspace */
|
||||
TAILQ_FOREACH(output, &outputs, outputs) {
|
||||
ws = get_first_workspace_for_output(output);
|
||||
initialize_output(conn, output, ws);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
test:
|
||||
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(1)" t/15*.t
|
||||
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(1)" t/16*.t
|
||||
|
||||
all: test
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
#!perl
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
|
||||
use Test::More tests => 8;
|
||||
use Test::Deep;
|
||||
use List::MoreUtils qw(all none);
|
||||
use Data::Dumper;
|
||||
use AnyEvent::I3;
|
||||
|
||||
my $i3 = i3("/tmp/nestedcons");
|
||||
|
||||
####################
|
||||
# Request tree
|
||||
####################
|
||||
|
||||
my $tree = $i3->get_workspaces->recv;
|
||||
# $VAR1 = {
|
||||
# 'fullscreen_mode' => 0,
|
||||
# 'nodes' => [
|
||||
# {
|
||||
# 'fullscreen_mode' => 0,
|
||||
# 'nodes' => [
|
||||
# {
|
||||
# 'fullscreen_mode' => 0,
|
||||
# 'nodes' => [],
|
||||
# 'window' => undef,
|
||||
# 'name' => '1',
|
||||
# 'orientation' => 0,
|
||||
# 'type' => 2
|
||||
# }
|
||||
# ],
|
||||
# 'window' => undef,
|
||||
# 'name' => 'LVDS1',
|
||||
# 'orientation' => 0,
|
||||
# 'type' => 1
|
||||
# }
|
||||
# ],
|
||||
# 'window' => undef,
|
||||
# 'name' => 'root',
|
||||
# 'orientation' => 0,
|
||||
# 'type' => 0
|
||||
# };
|
||||
|
||||
my $expected = {
|
||||
fullscreen_mode => 0,
|
||||
nodes => ignore(),
|
||||
window => undef,
|
||||
name => 'root',
|
||||
orientation => ignore(),
|
||||
type => 0,
|
||||
id => ignore(),
|
||||
};
|
||||
|
||||
cmp_deeply($tree, $expected, 'root node OK');
|
||||
|
||||
my @nodes = @{$tree->{nodes}};
|
||||
|
||||
ok(@nodes > 0, 'root node has at least one leaf');
|
||||
|
||||
ok((all { $_->{type} == 1 } @nodes), 'all nodes are of type CT_OUTPUT');
|
||||
ok((none { defined($_->{window}) } @nodes), 'no CT_OUTPUT contains a window');
|
||||
ok((all { @{$_->{nodes}} > 0 } @nodes), 'all nodes have at least one leaf (workspace)');
|
||||
my @workspaces;
|
||||
for my $ws (map { @{$_->{nodes}} } @nodes) {
|
||||
push @workspaces, $ws;
|
||||
}
|
||||
|
||||
ok((all { $_->{type} == 2 } @workspaces), 'all workspaces are of type CT_CON');
|
||||
ok((all { @{$_->{nodes}} == 0 } @workspaces), 'all workspaces are empty yet');
|
||||
ok((none { defined($_->{window}) } @workspaces), 'no CT_OUTPUT contains a window');
|
||||
|
||||
# TODO: get the focused container
|
||||
|
||||
$i3->command('open')->recv;
|
||||
|
||||
# TODO: get the focused container, check if it changed.
|
||||
# TODO: get the old focused container, check if there is a new child
|
||||
|
||||
diag(Dumper(\@workspaces));
|
||||
|
||||
diag(Dumper($tree));
|
||||
|
||||
|
||||
diag( "Testing i3, Perl $], $^X" );
|
Loading…
Reference in New Issue