]> git.sur5r.net Git - i3/i3/commitdiff
first step of the big refactoring ("tree" branch).
authorMichael Stapelberg <michael@stapelberg.de>
Sat, 27 Mar 2010 14:25:51 +0000 (15:25 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Tue, 13 Apr 2010 11:17:39 +0000 (13:17 +0200)
From here on, we can track changes. It made no sense to put the
development up to this point into git.

48 files changed:
Makefile
README.tree [new file with mode: 0644]
common.mk
i3-msg/main.c
include/all.h [new file with mode: 0644]
include/commands.h
include/con.h [new file with mode: 0644]
include/config.h
include/data.h
include/floating.h
include/handlers.h
include/i3.h
include/i3/ipc.h
include/ipc.h
include/load_layout.h [new file with mode: 0644]
include/manage.h
include/randr.h
include/render.h [new file with mode: 0644]
include/table.h [deleted file]
include/tree.h [new file with mode: 0644]
include/util.h
include/workspace.h
include/x.h [new file with mode: 0644]
include/xcb.h
include/xinerama.h
src/cfgparse.l
src/cfgparse.y
src/click.c
src/con.c [new file with mode: 0644]
src/config.c
src/floating.c
src/handlers.c
src/ipc.c
src/load_layout.c [new file with mode: 0644]
src/log.c
src/manage.c
src/nc.c [new file with mode: 0644]
src/randr.c
src/render.c [new file with mode: 0644]
src/table.c [deleted file]
src/tree.c [new file with mode: 0644]
src/util.c
src/workspace.c
src/x.c [new file with mode: 0644]
src/xcb.c
src/xinerama.c
testcases/Makefile
testcases/t/16-nestedcons.t [new file with mode: 0644]

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