# 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))
# 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
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"
--- /dev/null
+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.
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
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");
--- /dev/null
+/*
+ * 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
#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
--- /dev/null
+#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
} 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.
*
*
* 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,
uint32_t height;
} __attribute__((packed));
-/**
- * Defines a position in the table
- *
- */
-struct Cell {
- int row;
- int column;
-};
-
/**
* Used for the cache of colorpixels.
*
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;
* 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)
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
bool changed;
bool to_be_disabled;
- /** Current workspace selected on this virtual screen */
- Workspace *current_workspace;
-
/** x, y, width, height */
Rect rect;
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
#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),
* 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
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
*/
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
* 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);
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.
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
#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;
#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
#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.
#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"
*/
void ipc_shutdown();
+void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
+
#endif
--- /dev/null
+#ifndef _LOAD_LAYOUT_H
+#define _LOAD_LAYOUT_H
+
+void tree_append_json(const char *filename);
+
+#endif
* 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
* 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
uint32_t border_width);
#endif
+#endif
* 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
* 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.
--- /dev/null
+/*
+ * vim:ts=4:sw=4:expandtab
+ */
+
+#ifndef _RENDER_H
+#define _RENDER_H
+
+void render_con(Con *con);
+
+#endif
+++ /dev/null
-/*
- * 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
--- /dev/null
+/*
+ * 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
*/
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)
*/
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.
*/
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
*/
Client *get_matching_client(xcb_connection_t *conn,
const char *window_classtitle, Client *specific);
+#endif
/*
* Restart i3 in-place
#include <xcb/xcb.h>
#include "data.h"
+#include "tree.h"
#include "randr.h"
#ifndef _WORKSPACE_H
* 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
*/
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.
*
*/
int workspace_height(Workspace *ws);
-
+#endif
#endif
--- /dev/null
+/*
+ * 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
* 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,
*/
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
* Xinerama information to setup workspaces for each screen.
*
*/
-void initialize_xinerama(xcb_connection_t *conn);
+void xinerama_init();
#endif
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; }
* 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);
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
ws->table[0][0],
config.container_mode);
}
+#endif
}
| TOKNEWCONTAINER WHITESPACE TOKSTACKLIMIT WHITESPACE TOKSTACKLIMIT WHITESPACE NUMBER
{
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) {
con->stack_limit = config.container_stack_limit;
con->stack_limit_value = config.container_stack_limit_value;
}
+#endif
}
;
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
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
}
}
;
char *hex;
if (asprintf(&hex, "#%s", $<string>2) == -1)
die("asprintf()");
- $<number>$ = get_colorpixel(global_conn, hex);
+ $<number>$ = get_colorpixel(hex);
free(hex);
}
;
* 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;
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))
}
return resize_graphical_handler(conn, ws, first, second, orientation, event);
+#endif
}
--- /dev/null
+/*
+ * 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;
+ }
+}
* 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;
* 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);
}
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) {
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
}
}
}
/* Clear workspace names */
+#if 0
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces)
workspace_set_name(ws, NULL);
+#endif
}
SLIST_INIT(&modes);
/* 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");
REQUIRED_OPTION(font);
+#if 0
/* Set an empty name for every workspace which got no name */
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
workspace_set_name(ws, NULL);
}
+#endif
}
* 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;
/* 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
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 */
}
* 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
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
* 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 */
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;
}
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:
xcb_flush(conn);
}
+#if 0
/*
* Changes focus in the given direction for floating clients.
*
xcb_flush(conn);
}
+#endif
/*
- * vim:ts=8:expandtab
+ * vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
*
* 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
*
*/
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);
-
- /* 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;
-
- DLOG("(checked mode_switch, state %d)\n", state_filtered);
-
- /* 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;
- }
+ 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);
+
+ if (xkb_current_group == XkbGroup2Index)
+ state_filtered |= BIND_MODE_SWITCH;
+
+ DLOG("(checked mode_switch, state %d)\n", state_filtered);
+
+ /* 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;
}
+ }
- 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
return 1;
}
+#endif
* 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__)
* 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
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
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
};
/*
--- /dev/null
+#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);
+}
#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
* (or existing ones on restart).
*
*/
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include <xcb/xcb.h>
-#include <xcb/xcb_icccm.h>
-
-#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"
+
+#include "all.h"
+
+extern struct Con *focused;
+
/*
* 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;
/* 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);
* 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");
-
- 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);
+void restore_geometry() {
+ LOG("Restoring geometry\n");
+
+ 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);
* 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.
xcb_flush(conn);
}
+#endif
--- /dev/null
+/*
+ * 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);
+}
* (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. */
return candidate;
}
+#if 0
/*
* Initializes the specified output, assigning the specified workspace to it.
*
workspace_assign_to(ws, output, true);
}
}
+#endif
/*
* Disables RandR support by creating exactly one output with the size of the
*/
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,
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)
xcb_set_window_rect(conn, client->child, r);
}
}
+#endif
}
/*
* (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;
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;
workspace_assign_to(ws, first, true);
if (!needs_init)
continue;
- initialize_output(conn, first, ws);
+ //initialize_output(conn, first, ws);
needs_init = false;
}
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);
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)
ws = get_first_workspace_for_output(output);
initialize_output(conn, output, ws);
}
+#endif
/* render_layout flushes */
- render_layout(conn);
+ tree_render();
}
/*
* 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);
--- /dev/null
+/*
+ * 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");
+}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
--- /dev/null
+/*
+ * 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? */
+ }
+}
* 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 "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"
-
-static iconv_t conversion_descriptor = 0;
+#include <fcntl.h>
+
+#include "all.h"
+
+//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);
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.
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),
*
*/
void start_application(const char *command) {
+ LOG("executing: %s\n", command);
if (fork() == 0) {
/* Child process */
if (fork() == 0) {
}
}
+#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,
FREE(to_title_ucs);
return matching;
}
+#endif
/*
* Goes through the list of arguments (for exec()) and checks if the given 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 */
/* not reached */
}
+#if 0
+
#if defined(__OpenBSD__)
/*
}
#endif
-
+#endif
* 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),
* memory and initializing the data structures correctly).
*
*/
-Workspace *workspace_get(int number) {
- Workspace *ws = NULL;
- TAILQ_FOREACH(ws, workspaces, workspaces)
- if (ws->num == number)
- return ws;
-
- /* If we are still there, we could not find the requested workspace. */
- int last_ws = TAILQ_LAST(workspaces, workspaces_head)->num;
-
- DLOG("We need to initialize that one, last ws = %d\n", last_ws);
-
- for (int c = last_ws; c < number; c++) {
- DLOG("Creating new ws\n");
+Con *workspace_get(const char *num) {
+ Con *output, *workspace = NULL, *current;
+
+ /* 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;
- 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);
+ workspace = current;
+ break;
+ }
+ }
- 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
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);
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.
return height;
}
+#endif
--- /dev/null
+/*
+ * 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);
+}
* 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;
* 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'}};
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.
fake_configure_notify(conn, absolute, client->child);
}
+#endif
/*
* Finds out which modifier mask is the one for numlock, as the user may change this.
/*
- * vim:ts=8:expandtab
+ * vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
*
* 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;
*
*/
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;
}
/*
*
*/
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);
-
- 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++;
- }
+ 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);
- 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);
+ }
}
/*
* 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
}
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
--- /dev/null
+#!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" );