]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Sat, 4 Aug 2012 14:10:37 +0000 (16:10 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 4 Aug 2012 14:10:37 +0000 (16:10 +0200)
87 files changed:
.gitignore
Makefile
common.mk
debian/i3-wm.manpages
docs/Makefile
docs/docs.mk [new file with mode: 0644]
docs/hacking-howto
docs/ipc
docs/refcard.html
docs/userguide
generate-command-parser.pl
i3-config-wizard/Makefile
i3-config-wizard/cfgparse.y
i3-config-wizard/i3-config-wizard.mk [new file with mode: 0644]
i3-dump-log/Makefile
i3-dump-log/i3-dump-log.mk [new file with mode: 0644]
i3-input/Makefile
i3-input/i3-input.mk [new file with mode: 0644]
i3-migrate-config-to-v4
i3-msg/Makefile
i3-msg/i3-msg.mk [new file with mode: 0644]
i3-nagbar/Makefile
i3-nagbar/i3-nagbar.mk [new file with mode: 0644]
i3-sensible-terminal
i3.config
i3.config.keycodes
i3bar/Makefile
i3bar/doc/Makefile [deleted file]
i3bar/doc/i3bar.man [deleted file]
i3bar/i3bar.mk [new file with mode: 0644]
i3bar/src/xcb.c
include/all.h
include/commands.h
include/con.h
include/data.h
include/key_press.h [new file with mode: 0644]
include/log.h
include/regex.h
include/tree.h
libi3/Makefile
libi3/libi3.mk [new file with mode: 0644]
man/Makefile
man/i3-dump-log.man
man/i3-sensible-terminal.man
man/i3.man
man/i3bar.man [new file with mode: 0644]
man/man.mk [new file with mode: 0644]
parser-specs/commands.spec
src/Makefile [new file with mode: 0644]
src/cfgparse.y
src/click.c
src/commands.c
src/commands_parser.c
src/con.c
src/floating.c
src/handlers.c
src/i3.mk [new file with mode: 0644]
src/ipc.c
src/key_press.c [new file with mode: 0644]
src/load_layout.c
src/log.c
src/main.c
src/move.c
src/randr.c
src/regex.c
src/render.c
src/tree.c
src/util.c
src/workspace.c
testcases/complete-run.pl
testcases/lib/SocketActivation.pm
testcases/lib/TestWorker.pm
testcases/lib/i3test.pm
testcases/t/116-nestedcons.t
testcases/t/122-split.t
testcases/t/132-move-workspace.t
testcases/t/141-resize.t
testcases/t/145-flattening.t
testcases/t/156-fullscreen-focus.t
testcases/t/157-regress-fullscreen-level-up.t [deleted file]
testcases/t/159-socketpaths.t
testcases/t/166-assign.t
testcases/t/171-config-migrate.t
testcases/t/187-commands-parser.t
testcases/t/191-resize-levels.t [new file with mode: 0644]
testcases/t/192-layout.t [new file with mode: 0644]
testcases/t/504-move-workspace-to-output.t

index 705314b2a9488932bea9212a4a51e30232ebc2f8..3eac9270a91e887a8f3f10b0e579c9269b20b9de 100644 (file)
@@ -1,8 +1,6 @@
 *.o
 tags
-include/loglevels.h
 include/GENERATED_*.h
-loglevels.tmp
 *.swp
 *.gcda
 *.gcno
@@ -35,5 +33,5 @@ i3-nagbar/i3-nagbar
 i3-msg/i3-msg
 i3-config-wizard/i3-config-wizard
 i3-dump-log/i3-dump-log
-libi3/libi3.a
+libi3.a
 docs/*.pdf
index 065cdbcbe5a1568cfcf161a9415243897f7f1d41..f1104ead0458240893d14c72ad5535cdf7a74e0a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,151 +2,57 @@ TOPDIR=$(shell pwd)
 
 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:=$(FILES:.c=.o)
-HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h))
-CMDPARSE_HEADERS:=include/GENERATED_call.h include/GENERATED_enums.h include/GENERATED_tokens.h
+SUBDIRS:=
 
-# Recursively generate loglevels.h by explicitly calling make
-# We need this step because we need to ensure that loglevels.h will be
-# 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)
-else
-UNUSED:=$(shell $(MAKE) loglevels.h)
-endif
+ALL_TARGETS =
+INSTALL_TARGETS =
+CLEAN_TARGETS =
+DISTCLEAN_TARGETS =
 
-SUBDIRS:=i3-msg i3-input i3-nagbar i3-config-wizard i3bar i3-dump-log
+all: real-all
 
-# Depend on the specific file (.c for each .o) and on all headers
-src/%.o: src/%.c ${HEADERS}
-       echo "[i3] CC $<"
-       $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -c -o $@ $<
+include libi3/libi3.mk
+include src/i3.mk
+include i3-config-wizard/i3-config-wizard.mk
+include i3-msg/i3-msg.mk
+include i3-input/i3-input.mk
+include i3-nagbar/i3-nagbar.mk
+include i3bar/i3bar.mk
+include i3-dump-log/i3-dump-log.mk
+include docs/docs.mk
+include man/man.mk
 
-all: i3 subdirs
+real-all: $(ALL_TARGETS)
 
-i3: libi3/libi3.a src/cfgparse.y.o src/cfgparse.yy.o ${FILES}
-       echo "[i3] LINK i3"
-       $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
-
-libi3/%.a: libi3/*.c
-       $(MAKE) -C libi3
-
-subdirs:
-       for dir in $(SUBDIRS); do \
-               echo ""; \
-               echo "MAKE $$dir"; \
-               $(MAKE) -C $$dir; \
-       done
-
-loglevels.h:
-       echo "[i3] LOGLEVELS"
-       for file in $$(ls src/*.c src/*.y src/*.l | grep -v 'cfgparse.\(tab\|yy\).c'); \
-       do \
-               echo $$(basename $$file .c); \
-       done > loglevels.tmp
-       (echo "char *loglevels[] = {"; for file in $$(cat loglevels.tmp); \
-       do \
-               echo "\"$$file\", "; \
-       done; \
-       echo "};") > include/loglevels.h;
-
-# The GENERATED_* files are actually all created from a single pass, so all
-# files just depend on the first one.
-include/GENERATED_call.h: generate-command-parser.pl parser-specs/commands.spec
-       echo "[i3] Generating command parser"
-       (cd include; ../generate-command-parser.pl)
-include/GENERATED_enums.h: include/GENERATED_call.h
-include/GENERATED_tokens.h: include/GENERATED_call.h
-
-# This target compiles the command parser twice:
-# Once with -DTEST_PARSER, creating a stand-alone executable used for tests,
-# and once as an object file for i3.
-src/commands_parser.o: src/commands_parser.c ${HEADERS} ${CMDPARSE_HEADERS}
-       echo "[i3] CC $<"
-       $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DTEST_PARSER -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -o test.commands_parser $< $(LIBS)
-       $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -c -o $@ $<
-
-src/cfgparse.yy.o: src/cfgparse.l src/cfgparse.y.o ${HEADERS}
-       echo "[i3] LEX $<"
-       $(FLEX) -i -o$(@:.o=.c) $<
-       $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.l/ { print NR }' loglevels.tmp))" -c -o $@ $(@:.o=.c)
-
-
-src/cfgparse.y.o: src/cfgparse.y ${HEADERS}
-       echo "[i3] YACC $<"
-       $(BISON) --debug --verbose -b $(basename $< .y) -d $<
-       $(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.y/ { print NR }' loglevels.tmp))" -c -o $@ $(<:.y=.tab.c)
-
-
-install: all
-       echo "[i3] INSTALL"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(SYSCONFDIR)/i3
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/include/i3
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/xsessions
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/applications
-       $(INSTALL) -m 0755 i3 $(DESTDIR)$(PREFIX)/bin/
-       $(INSTALL) -m 0755 i3-migrate-config-to-v4 $(DESTDIR)$(PREFIX)/bin/
-       $(INSTALL) -m 0755 i3-sensible-editor $(DESTDIR)$(PREFIX)/bin/
-       $(INSTALL) -m 0755 i3-sensible-pager $(DESTDIR)$(PREFIX)/bin/
-       $(INSTALL) -m 0755 i3-sensible-terminal $(DESTDIR)$(PREFIX)/bin/
-       test -e $(DESTDIR)$(SYSCONFDIR)/i3/config || $(INSTALL) -m 0644 i3.config $(DESTDIR)$(SYSCONFDIR)/i3/config
-       test -e $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes || $(INSTALL) -m 0644 i3.config.keycodes $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes
-       $(INSTALL) -m 0644 i3.xsession.desktop $(DESTDIR)$(PREFIX)/share/xsessions/i3.desktop
-       $(INSTALL) -m 0644 i3.applications.desktop $(DESTDIR)$(PREFIX)/share/applications/i3.desktop
-       $(INSTALL) -m 0644 include/i3/ipc.h $(DESTDIR)$(PREFIX)/include/i3/
-       for dir in $(SUBDIRS); do \
-               $(MAKE) -C $$dir install; \
-       done
+install: $(INSTALL_TARGETS)
 
 dist: distclean
        [ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION}
        [ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2
        mkdir i3-${VERSION}
-       cp i3-migrate-config-to-v4 generate-command-parser.pl i3-sensible-* i3.config.keycodes DEPENDS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.xsession.desktop i3.applications.desktop pseudo-doc.doxygen Makefile i3-${VERSION}
+       cp i3-migrate-config-to-v4 generate-command-parser.pl i3-sensible-* i3.config.keycodes DEPENDS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.xsession.desktop i3.applications.desktop pseudo-doc.doxygen common.mk Makefile i3-${VERSION}
        cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man parser-specs i3-${VERSION}
        # Only copy toplevel documentation (important stuff)
        mkdir i3-${VERSION}/docs
        # Pre-generate documentation
-       $(MAKE) -C docs
-       $(MAKE) -C i3bar/doc
+       $(MAKE) docs
        # Cleanup τεχ output files
        find docs -regex ".*\.\(aux\|out\|log\|toc\|bm\|dvi\|log\)" -exec rm '{}' \;
        find docs -maxdepth 1 -type f ! \( -name "*.xcf" -or -name "*.svg" \) -exec cp '{}' i3-${VERSION}/docs \;
        # Only copy source code from i3-input
        mkdir i3-${VERSION}/i3-input
-       find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \;
-       sed -e 's/^GIT_VERSION:=\(.*\)/GIT_VERSION:=$(shell /bin/echo '${GIT_VERSION}' | sed 's/\\/\\\\/g')/g;s/^VERSION:=\(.*\)/VERSION:=${VERSION}/g' common.mk > i3-${VERSION}/common.mk
+       find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.mk" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \;
+       echo -n ${I3_VERSION} > i3-${VERSION}/VERSION
        # Pre-generate a manpage to allow distributors to skip this step and save some dependencies
-       $(MAKE) -C man
+       $(MAKE) mans
        cp man/*.1 i3-${VERSION}/man/
-       cp i3bar/doc/*.1 i3-${VERSION}/i3bar/doc/
        tar cfj i3-${VERSION}.tar.bz2 i3-${VERSION}
        rm -rf i3-${VERSION}
 
-clean:
-       rm -f src/*.o src/*.gcno src/cmdparse.* src/cfgparse.tab.{c,h} src/cfgparse.yy.c src/cfgparse.{output,dot} loglevels.tmp include/loglevels.h include/GENERATED_*
+clean: $(CLEAN_TARGETS)
        (which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true
-       $(MAKE) -C libi3 clean
-       $(MAKE) -C docs clean
-       $(MAKE) -C man clean
-       for dir in $(SUBDIRS); do \
-               echo ""; \
-               echo "CLEAN $$dir"; \
-               $(MAKE) TOPDIR=$(TOPDIR) -C $$dir distclean; \
-       done
 
-distclean: clean
-       rm -f i3
-       for dir in $(SUBDIRS); do \
-               echo ""; \
-               echo "DISTCLEAN $$dir"; \
-               $(MAKE) TOPDIR=$(TOPDIR) -C $$dir distclean; \
-       done
+distclean: clean $(DISTCLEAN_TARGETS)
 
 coverage:
        rm -f /tmp/i3-coverage.info
index 439490596d38ff0b910c6b34896d400fea577af8..f202166df5b1f20b576a23fcbc3fd8ce1577ab12 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -14,9 +14,42 @@ ifndef SYSCONFDIR
     SYSCONFDIR=$(PREFIX)/etc
   endif
 endif
-# The escaping is absurd, but we need to escape for shell, sed, make, define
-GIT_VERSION:="$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch $(shell [ -f $(TOPDIR)/.git/HEAD ] && sed 's/ref: refs\/heads\/\(.*\)/\\\\\\"\1\\\\\\"/g' $(TOPDIR)/.git/HEAD || echo 'unknown'))"
-VERSION:=$(shell git describe --tags --abbrev=0)
+
+I3_VERSION := '$(shell [ -f $(TOPDIR)/VERSION ] && cat $(TOPDIR)/VERSION)'
+ifeq ('',$(I3_VERSION))
+VERSION := $(shell git describe --tags --abbrev=0)
+I3_VERSION := '$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch \"$(shell git describe --tags --always --all | sed s:heads/::)\")'
+else
+VERSION := ${I3_VERSION}
+endif
+
+
+## Generic flags
+
+# Default CFLAGS that users should be able to override
+ifeq ($(DEBUG),1)
+# Extended debugging flags, macros shall be available in gcc
+CFLAGS ?= -pipe -gdwarf-2 -g3
+else
+CFLAGS ?= -pipe -O2 -freorder-blocks-and-partition
+endif
+
+# Default LDFLAGS that users should be able to override
+LDFLAGS ?= $(as_needed_LDFLAG)
+
+# Common CFLAGS for all i3 related binaries
+I3_CFLAGS  = -std=c99
+I3_CFLAGS += -Wall
+# unused-function, unused-label, unused-variable are turned on by -Wall
+# We don’t want unused-parameter because of the use of many callbacks
+I3_CFLAGS += -Wunused-value
+I3_CFLAGS += -Iinclude
+
+I3_CPPFLAGS  = -DI3_VERSION=\"${I3_VERSION}\"
+I3_CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\"
+
+
+## Libraries flags
 
 ifeq ($(shell which pkg-config 2>/dev/null 1>/dev/null || echo 1),1)
 $(error "pkg-config was not found")
@@ -35,79 +68,84 @@ endif
 cflags_for_lib = $(shell pkg-config --silence-errors --cflags $(1) 2>/dev/null)
 ldflags_for_lib = $(shell pkg-config --exists 2>/dev/null $(1) && pkg-config --libs $(1) 2>/dev/null || echo -l$(2))
 
-CFLAGS += -std=c99
-CFLAGS += -pipe
-CFLAGS += -Wall
-# unused-function, unused-label, unused-variable are turned on by -Wall
-# We don’t want unused-parameter because of the use of many callbacks
-CFLAGS += -Wunused-value
-CFLAGS += -Iinclude
-CFLAGS += $(call cflags_for_lib, xcb-keysyms)
+# XCB common stuff
+XCB_CFLAGS  := $(call cflags_for_lib, xcb)
+XCB_CFLAGS  += $(call cflags_for_lib, xcb-event)
+XCB_LIBS    := $(call ldflags_for_lib, xcb,xcb)
+XCB_LIBS    += $(call ldflags_for_lib, xcb-event,xcb-event)
 ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1)
-CPPFLAGS += -DXCB_COMPAT
-CFLAGS += $(call cflags_for_lib, xcb-atom)
-CFLAGS += $(call cflags_for_lib, xcb-aux)
+XCB_CFLAGS  += $(call cflags_for_lib, xcb-atom)
+XCB_CFLAGS  += $(call cflags_for_lib, xcb-aux)
+XCB_LIBS    += $(call ldflags_for_lib, xcb-atom,xcb-atom)
+XCB_LIBS    += $(call ldflags_for_lib, xcb-aux,xcb-aux)
 else
-CFLAGS += $(call cflags_for_lib, xcb-util)
+XCB_CFLAGS  += $(call cflags_for_lib, xcb-util)
+XCB_LIBS    += $(call ldflags_for_lib, xcb-util)
 endif
-CFLAGS += $(call cflags_for_lib, xcb-icccm)
-CFLAGS += $(call cflags_for_lib, xcb-xinerama)
-CFLAGS += $(call cflags_for_lib, xcb-randr)
-CFLAGS += $(call cflags_for_lib, xcb)
-CFLAGS += $(call cflags_for_lib, xcursor)
-CFLAGS += $(call cflags_for_lib, x11)
-CFLAGS += $(call cflags_for_lib, yajl)
-CFLAGS += $(call cflags_for_lib, libev)
-CFLAGS += $(call cflags_for_lib, libpcre)
-CFLAGS += $(call cflags_for_lib, libstartup-notification-1.0)
-CPPFLAGS += -DI3_VERSION=\"${GIT_VERSION}\"
-CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\"
 
+# XCB keyboard stuff
+XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms)
+XCB_KBD_LIBS   := $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms)
+
+# XCB WM stuff
+XCB_WM_CFLAGS := $(call cflags_for_lib, xcb-icccm)
+XCB_WM_CFLAGS += $(call cflags_for_lib, xcb-xinerama)
+XCB_WM_CFLAGS += $(call cflags_for_lib, xcb-randr)
+XCB_WM_LIBS   := $(call ldflags_for_lib, xcb-icccm,xcb-icccm)
+XCB_WM_LIBS   += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama)
+XCB_WM_LIBS   += $(call ldflags_for_lib, xcb-randr,xcb-randr)
+
+# Xlib
+X11_CFLAGS := $(call cflags_for_lib, x11)
+X11_LIBS   := $(call ldflags_for_lib, x11,X11)
+
+# Xcursor
+XCURSOR_CFLAGS := $(call cflags_for_lib, xcursor)
+XCURSOR_LIBS   := $(call ldflags_for_lib, xcursor,Xcursor)
+
+# yajl
+YAJL_CFLAGS := $(call cflags_for_lib, yajl)
+# Fallback for libyajl 1 which did not include yajl_version.h. We need
+# YAJL_MAJOR from that file to decide which code path should be used.
+YAJL_CFLAGS += -idirafter $(TOPDIR)/yajl-fallback
+YAJL_LIBS   := $(call ldflags_for_lib, yajl,yajl)
+
+#libev
+LIBEV_CFLAGS := $(call cflags_for_lib, libev)
+LIBEV_LIBS   := $(call ldflags_for_lib, libev,ev)
+
+# libpcre
+PCRE_CFLAGS := $(call cflags_for_lib, libpcre)
 ifeq ($(shell pkg-config --atleast-version=8.10 libpcre 2>/dev/null && echo 1),1)
-CPPFLAGS += -DPCRE_HAS_UCP=1
+I3_CPPFLAGS += -DPCRE_HAS_UCP=1
 endif
+PCRE_LIBS   := $(call ldflags_for_lib, libpcre,pcre)
 
-LIBS += -lm
-# Darwin (Mac OS X) doesn’t have librt
-ifneq ($(UNAME),Darwin)
-LIBS += -lrt
-endif
-LIBS += -L $(TOPDIR)/libi3 -li3
-LIBS += $(call ldflags_for_lib, xcb-event,xcb-event)
-LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms)
-ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1)
-LIBS += $(call ldflags_for_lib, xcb-atom,xcb-atom)
-LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux)
-else
-LIBS += $(call ldflags_for_lib, xcb-util)
-endif
-LIBS += $(call ldflags_for_lib, xcb-icccm,xcb-icccm)
-LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama)
-LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
-LIBS += $(call ldflags_for_lib, xcb,xcb)
-LIBS += $(call ldflags_for_lib, xcursor,Xcursor)
-LIBS += $(call ldflags_for_lib, x11,X11)
-LIBS += $(call ldflags_for_lib, yajl,yajl)
-LIBS += $(call ldflags_for_lib, libev,ev)
-LIBS += $(call ldflags_for_lib, libpcre,pcre)
-LIBS += $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1)
+# startup-notification
+LIBSN_CFLAGS := $(call cflags_for_lib, libstartup-notification-1.0)
+LIBSN_LIBS   := $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1)
+
+# libi3
+LIBS = -L$(TOPDIR) -li3
+
+## Platform-specific flags
 
 # Please test if -Wl,--as-needed works on your platform and send me a patch.
 # it is known not to work on Darwin (Mac OS X)
 ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
-LDFLAGS += -Wl,--as-needed
+as_needed_LDFLAG = -Wl,--as-needed
 endif
 
 ifeq ($(UNAME),NetBSD)
 # We need -idirafter instead of -I to prefer the system’s iconv over GNU libiconv
-CFLAGS += -idirafter /usr/pkg/include
-LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib
+I3_CFLAGS += -idirafter /usr/pkg/include
+I3_LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib
 endif
 
 ifeq ($(UNAME),OpenBSD)
-CFLAGS += -I${X11BASE}/include
+I3_CFLAGS += -I${X11BASE}/include
 LIBS += -liconv
-LDFLAGS += -L${X11BASE}/lib
+I3_LDFLAGS += -L${X11BASE}/lib
 endif
 
 ifeq ($(UNAME),FreeBSD)
@@ -116,33 +154,31 @@ endif
 
 ifeq ($(UNAME),Darwin)
 LIBS += -liconv
+else
+# Darwin (Mac OS X) doesn’t have librt
+LIBS += -lrt
 endif
 
-# Fallback for libyajl 1 which did not include yajl_version.h. We need
-# YAJL_MAJOR from that file to decide which code path should be used.
-CFLAGS += -idirafter $(TOPDIR)/yajl-fallback
-
 ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
-CPPFLAGS += -D_GNU_SOURCE
+I3_CPPFLAGS += -D_GNU_SOURCE
 endif
 
-ifeq ($(DEBUG),1)
-# Extended debugging flags, macros shall be available in gcc
-CFLAGS += -gdwarf-2
-CFLAGS += -g3
-else
-CFLAGS += -O2
-CFLAGS += -freorder-blocks-and-partition
-endif
 
 ifeq ($(COVERAGE),1)
-CFLAGS += -fprofile-arcs -ftest-coverage
+I3_CFLAGS += -fprofile-arcs -ftest-coverage
 LIBS += -lgcov
 endif
 
+V ?= 0
+ifeq ($(V),0)
 # Don’t print command lines which are run
 .SILENT:
 
+# echo-ing vars
+V_ASCIIDOC = echo ASCIIDOC $@;
+V_A2X = echo A2X $@;
+endif
+
 # Always remake the following targets
 .PHONY: install clean dist distclean
 
index 4df7233c697e8ff16c374451c4c160807bb67e16..a1b05bd3554dc2c33d7270bfc069dd3269759ce2 100644 (file)
@@ -8,4 +8,4 @@ man/i3-migrate-config-to-v4.1
 man/i3-sensible-pager.1
 man/i3-sensible-editor.1
 man/i3-sensible-terminal.1
-i3bar/doc/i3bar.1
+man/i3bar.1
index fc41236f2acf5276ea1957f38bc8aa5b54e1bf40..d6e670c9de1441eddf97d4f033f3d264c6c0603b 100644 (file)
@@ -1,36 +1,7 @@
-# To pass additional parameters for asciidoc
-ASCIIDOC=asciidoc
-
-ASCIIDOC_TARGETS:=hacking-howto.html debugging.html debugging-release-version.html userguide.html ipc.html multi-monitor.html wsbar.html testsuite.html i3bar-protocol.html
-
-all: ${ASCIIDOC_TARGETS}
-
-hacking-howto.html: hacking-howto
-       $(ASCIIDOC) -a toc -n $<
-
-i3bar-protocol.html: i3bar-protocol
-       $(ASCIIDOC) -a toc -n $<
-
-debugging.html: debugging
-       $(ASCIIDOC) -n $<
-
-debugging-release-version.html: debugging-release-version
-       $(ASCIIDOC) -n $<
-
-userguide.html: userguide
-       $(ASCIIDOC) -a toc -n $<
-
-testsuite.html: testsuite
-       $(ASCIIDOC) -a toc -n $<
-
-ipc.html: ipc
-       $(ASCIIDOC) -a toc -n $<
-
-multi-monitor.html: multi-monitor
-       $(ASCIIDOC) -a toc -n $<
-
-wsbar.html: wsbar
-       $(ASCIIDOC) -a toc -n $<
+all:
+       $(MAKE) -C .. docs
 
 clean:
-       rm -f ${ASCIIDOC_TARGETS}
+       $(MAKE) -C .. clean-docs
+
+.PHONY: all clean
diff --git a/docs/docs.mk b/docs/docs.mk
new file mode 100644 (file)
index 0000000..773c832
--- /dev/null
@@ -0,0 +1,35 @@
+DISTCLEAN_TARGETS += clean-docs
+
+# To pass additional parameters for asciidoc
+ASCIIDOC = asciidoc
+
+ASCIIDOC_NOTOC_TARGETS = \
+       docs/debugging.html \
+       docs/debugging-release-version.html
+
+ASCIIDOC_TOC_TARGETS = \
+       docs/hacking-howto.html \
+       docs/userguide.html \
+       docs/ipc.html \
+       docs/multi-monitor.html \
+       docs/wsbar.html \
+       docs/testsuite.html \
+       docs/i3bar-protocol.html
+
+ASCIIDOC_TARGETS = \
+       $(ASCIIDOC_TOC_TARGETS) \
+       $(ASCIIDOC_NOTOC_TARGETS)
+
+ASCIIDOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -n $(ASCIIDOC_FLAGS) -o $@ $<
+ASCIIDOC_TOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -a toc -n $(ASCIIDOC_FLAGS) -o $@ $<
+
+docs: $(ASCIIDOC_TARGETS)
+
+$(ASCIIDOC_TOC_TARGETS): docs/%.html: docs/%
+       $(ASCIIDOC_TOC_CALL)
+
+$(ASCIIDOC_NOTOC_TARGETS): docs/%.html: docs/%
+       $(ASCIIDOC_CALL)
+
+clean-docs:
+       rm -f $(ASCIIDOC_TARGETS)
index 73ae96335991d5312e1b61b5a0c265be8a16926f..7f2c35e65d0b6ba06727ffb37159a0632264f874 100644 (file)
@@ -141,7 +141,7 @@ src/load_layout.c::
 Contains code for loading layouts from JSON files.
 
 src/log.c::
-Handles the setting of loglevels, contains the logging functions.
+Contains the logging functions.
 
 src/main.c::
 Initializes the window manager.
index 83ab7218714df269cc01ad55302c47fe8b334a31..525e9968421e3fd83071939e228e233cd66d3a7c 100644 (file)
--- a/docs/ipc
+++ b/docs/ipc
@@ -1,7 +1,7 @@
 IPC interface (interprocess communication)
 ==========================================
 Michael Stapelberg <michael@i3wm.org>
-February 2012
+August 2012
 
 This document describes how to interface with i3 from a separate process. This
 is useful for example to remote-control i3 (to write test cases for example) or
@@ -270,12 +270,15 @@ border (string)::
        Can be either "normal", "none" or "1pixel", dependending on the
        container’s border style.
 layout (string)::
-       Can be either "default", "stacked", "tabbed", "dockarea" or "output".
+       Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or
+       "output".
        Other values might be possible in the future, should we add new
        layouts.
 orientation (string)::
        Can be either "none" (for non-split containers), "horizontal" or
        "vertical".
+       THIS FIELD IS OBSOLETE. It is still present, but your code should not
+       use it. Instead, rely on the layout field.
 percent (float)::
        The percentage which this container takes in its parent. A value of
        +null+ means that the percent property does not make sense for this
@@ -296,6 +299,11 @@ window_rect (map)::
 geometry (map)::
        The original geometry the window specified when i3 mapped it. Used when
        switching a window to floating mode, for example.
+window (integer)::
+       The X11 window ID of the *actual client window* inside this container.
+       This field is set to null for split containers or otherwise empty
+       containers. This ID corresponds to what xwininfo(1) and other
+       X11-related tools display (usually in hex).
 urgent (bool)::
        Whether this container (window or workspace) has the urgency hint set.
 focused (bool)::
@@ -621,7 +629,7 @@ C::
 Ruby::
        http://github.com/badboy/i3-ipc
 Perl::
-       http://search.cpan.org/search?query=AnyEvent::I3
+       https://metacpan.org/module/AnyEvent::I3
 Python::
-       https://github.com/whitelynx/i3ipc
-       https://github.com/ziberna/i3-py (includes higher-level features)
+       https://github.com/whitelynx/i3ipc
+       https://github.com/ziberna/i3-py (includes higher-level features)
index a4427f4f261380b85377cd8e8e29a8de2c735733..7156da368e72721b443f424365dd6026765fd348 100644 (file)
                </p>
        </header>
 
+
        <section>
        <h2>Basics</h2>
        <table>
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
                        <td>open new terminal
-
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>j</kbd>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>j</kbd>
                        <td>focus left
 
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>k</kbd>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>k</kbd>
                        <td>focus down
 
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>l</kbd>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>l</kbd>
                        <td>focus up
 
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>;</kbd>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>;</kbd>
                        <td>focus right
+               <tr>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
+                       <td>toggle focus mode
        </table>
        </section>
 
-
        <section>
-       <h2>Changing the container layout</h2>
+       <h2>Moving windows</h2>
        <table>
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>e</kbd>
-                       <td>default
-
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>j</kbd>
+                       <td>move window left
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>s</kbd>
-                       <td>stacking
-
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>k</kbd>
+                       <td>move window down
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>w</kbd>
-                       <td>tabbed
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>l</kbd>
+                       <td>move window up
+               <tr>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>;</kbd>
+                       <td>move window right
        </table>
        </section>
 
 </div><div>
 
        <section>
-       <h2>Fullscreen mode</h2>
+       <h2>Modifying windows</h2>
        <table>
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>f</kbd>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>f</kbd>
                        <td>toggle fullscreen
+               <tr>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>v</kbd>
+                       <td>split a window vertically
+               <tr>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>h</kbd>
+                       <td>split a window horizontally
+               <tr>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>r</kbd>
+                       <td>resize mode
        </table>
+       <p class="ref">Look at the “Resizing containers / windows” section of the user guide.</p>
        </section>
 
-
        <section>
-       <h2>Opening other applications</h2>
+       <h2>Changing the container layout</h2>
        <table>
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>d</kbd>
-                       <td>open application (with dmenu)
-       </table>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>e</kbd>
+                       <td>default
 
+               <tr>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>s</kbd>
+                       <td>stacking
 
-       <section>
-       <h2>Closing windows</h2>
-       <table>
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd> </kbd>+ <kbd>q</kbd>
-                       <td>kill a window
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>w</kbd>
+                       <td>tabbed
        </table>
        </section>
 
-
        <section>
-       <h2>Using workspaces</h2>
+       <h2>Floating</h2>
        <table>
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>1</kbd>–<kbd>9</kbd>
-                       <td>switch to another workspace
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd></kbd>
+                       <td>toggle floating
+               <tr>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
+                       <td>drag floating
        </table>
        </section>
 
 
        <section>
-       <h2>Moving windows to workspaces</h2>
+       <h2>Using workspaces</h2>
        <table>
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>1</kbd>–<kbd>9</kbd>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>0</kbd>-<kbd>9</kbd>
+                       <td>switch to another workspace
+               <tr>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>0</kbd>-<kbd>9</kbd>
                        <td>move a window to another workspace
        </table>
        </section>
 </div><div>
 
        <section>
-       <h2>Resizing</h2>
-       <p class="ref">Look at “Resizing containers / windows” section of the user guide.</p>
-       </section>
-
-
-       <section>
-       <h2>Restart / Exit</h2>
+       <h2>Opening applications / Closing windows</h2>
        <table>
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>r</kbd>
-                       <td>restart i3 inplace
-
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>d</kbd>
+                       <td>open application launcher (dmenu)
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>e</kbd>
-
-       </section><td>exit i3
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>q</kbd>
+                       <td>kill a window
        </table>
-
+       </section>
 
        <section>
-       <h2>Floating</h2>
+       <h2>Restart / Exit</h2>
        <table>
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd></kbd>
-                       <td>toggle floating
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>c</kbd>
+                        <td>reload the configuration file
+               <tr>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>r</kbd>
+                       <td>restart i3 inplace
 
                <tr>
-                       <td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd>
-                       <td>drag floating
-       </table>
+                       <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>e</kbd>
+                        <td>exit i3
        </section>
+       </table>
+
 
        <!-- footer -->
        <p id="copyright">
                <br />
                All rights reserved
                <br />
-               Designed by Zeus Panchenko
+               Designed by Zeus Panchenko, updated by Moritz Bandemer
        </p>
        <p id="licence">
                Permission is granted to copy, distribute and/or modify this document provided
index 853fc5e622b5c755ba4cef0d004c203d631621b3..64063831ec38d665b1ceef45ba5ee2018e57701d 100644 (file)
@@ -1,7 +1,7 @@
 i3 User’s Guide
 ===============
-Michael Stapelberg <michael+i3@stapelberg.de>
-April 2012
+Michael Stapelberg <michael@i3wm.org>
+August 2012
 
 This document contains all the information you need to configure and use the i3
 window manager. If it does not, please contact us on IRC (preferred) or post your
@@ -68,9 +68,11 @@ To split a window vertically, press +mod+v+. To split it horizontally, press
 
 A split container can have one of the following layouts:
 
-default::
+splith/splitv::
 Windows are sized so that every window gets an equal amount of space in the
-container.
+container. splith distributes the windows horizontally (windows are right next
+to each other), splitv distributes them vertically (windows are on top of each
+other).
 stacking::
 Only the focused window in the container is displayed. You get a list of
 windows at the top of the container.
@@ -78,8 +80,8 @@ tabbed::
 The same principle as +stacking+, but the list of windows at the top is only
 a single line which is vertically split.
 
-To switch modes, press +mod+e+ for default, +mod+s+ for stacking and
-+mod+w+ for tabbed.
+To switch modes, press +mod+e+ for splith/splitv (it toggles), +mod+s+ for
+stacking and +mod+w+ for tabbed.
 
 image:modes.png[Container modes]
 
@@ -196,20 +198,21 @@ image::tree-shot4.png["shot4",title="Two terminals on standard workspace"]
 
 It is only natural to use so-called +Split Containers+ in order to build a
 layout when using a tree as data structure. In i3, every +Container+ has an
-orientation (horizontal, vertical or unspecified). So, in our example with the
-workspace, the default orientation of the workspace +Container+ is horizontal
-(most monitors are widescreen nowadays). If you change the orientation to
-vertical (+mod+v+ in the default config) and *then* open two terminals, i3 will
-configure your windows like this:
+orientation (horizontal, vertical or unspecified) and the orientation depends
+on the layout the container is in (vertical for splitv and stacking, horizontal
+for splith and tabbed). So, in our example with the workspace, the default
+layout of the workspace +Container+ is splith (most monitors are widescreen
+nowadays). If you change the layout to splitv (+mod+l+ in the default config)
+and *then* open two terminals, i3 will configure your windows like this:
 
 image::tree-shot2.png["shot2",title="Vertical Workspace Orientation"]
 
-An interesting new feature of the tree branch is the ability to split anything:
-Let’s assume you have two terminals on a workspace (with horizontal
-orientation), focus is on the right terminal. Now you want to open another
-terminal window below the current one. If you would just open a new terminal
-window, it would show up to the right due to the horizontal workspace
-orientation. Instead, press +mod+v+ to create a +Vertical Split Container+ (to
+An interesting new feature of i3 since version 4 is the ability to split anything:
+Let’s assume you have two terminals on a workspace (with splith layout, that is
+horizontal orientation), focus is on the right terminal. Now you want to open
+another terminal window below the current one. If you would just open a new
+terminal window, it would show up to the right due to the splith layout.
+Instead, press +mod+v+ to split the container with the splitv layout (to
 open a +Horizontal Split Container+, use +mod+h+). Now you can open a new
 terminal and it will open below the current one:
 
@@ -1190,13 +1193,15 @@ cursor for 60 seconds.
 === Splitting containers
 
 The split command makes the current window a split container. Split containers
-can contain multiple windows. Every split container has an orientation, it is
-either split horizontally (a new window gets placed to the right of the current
-one) or vertically (a new window gets placed below the current one).
+can contain multiple windows. Depending on the layout of the split container,
+new windows get placed to the right of the current one (splith) or new windows
+get placed below the current one (splitv).
 
 If you apply this command to a split container with the same orientation,
 nothing will happen. If you use a different orientation, the split container’s
-orientation will be changed (if it does not have more than one window).
+orientation will be changed (if it does not have more than one window). Use
++layout toggle split+ to change the layout of any split container from splitv
+to splith or vice-versa.
 
 *Syntax*:
 ---------------------------
@@ -1211,19 +1216,32 @@ bindsym mod+h split horizontal
 
 === Manipulating layout
 
-Use +layout default+, +layout stacking+ or +layout tabbed+ to change the
-current container layout to default, stacking or tabbed layout, respectively.
+Use +layout toggle split+, +layout stacking+ or +layout tabbed+ to change the
+current container layout to splith/splitv, stacking or tabbed layout,
+respectively.
 
 To make the current window (!) fullscreen, use +fullscreen+, to make
 it floating (or tiling again) use +floating enable+ respectively +floating disable+
 (or +floating toggle+):
 
+*Syntax*:
+--------------
+layout <tabbed|stacking>
+layout toggle [split|all]
+--------------
+
 *Examples*:
 --------------
 bindsym mod+s layout stacking
-bindsym mod+l layout default
+bindsym mod+l layout toggle split
 bindsym mod+w layout tabbed
 
+# Toggle between stacking/tabbed/split:
+bindsym mod+x layout toggle
+
+# Toggle between stacking/tabbed/splith/splitv:
+bindsym mod+x layout toggle all
+
 # Toggle fullscreen
 bindsym mod+f fullscreen
 
@@ -1313,8 +1331,9 @@ You can also switch to the next and previous workspace with the commands
 workspace 1, 3, 4 and 9 and you want to cycle through them with a single key
 combination. To restrict those to the current output, use +workspace
 next_on_output+ and +workspace prev_on_output+. Similarly, you can use +move
-container to workspace next+ and +move container to workspace prev+ to move a
-container to the next/previous workspace.
+container to workspace next+, +move container to workspace prev+ to move a
+container to the next/previous workspace and +move container to workspace current+
+(the last one makes sense only when used with criteria).
 
 [[back_and_forth]]
 To switch back to the previously focused workspace, use +workspace
@@ -1330,6 +1349,18 @@ you can use the +move workspace to output+ command followed by the name of the
 target output. You may also use +left+, +right+, +up+, +down+ instead of the
 xrandr output name to move to the next output in the specified direction.
 
+*Syntax*:
+-----------------------------------
+workspace <next|prev|next_on_output|prev_on_output>
+workspace back_and_forth
+workspace <name>
+workspace number <number>
+
+move [window|container] [to] workspace <name>
+move [window|container] [to] workspace number <number>
+move [window|container] [to] workspace <prev|next|current>
+-----------------------------------
+
 *Examples*:
 -------------------------
 bindsym mod+1 workspace 1
@@ -1345,6 +1376,9 @@ bindsym mod+b workspace back_and_forth
 
 # move the whole workspace to the next output
 bindsym mod+x move workspace to output right
+
+# move firefox to current workspace
+bindsym mod+F1 [class="Firefox"] move workspace current
 -------------------------
 
 ==== Named workspaces
index 993c64fcaa570dcc73d229ef4bc6130223078369..01cbe462ec64d05c7a51c2d80da8e7b710c5820c 100755 (executable)
@@ -152,7 +152,7 @@ for my $state (@keys) {
         $cmd =~ s/[^(]+\(//;
         $cmd =~ s/\)$//;
         $cmd = ", $cmd" if length($cmd) > 0;
-        say $callfh qq|           printf("$fmt\\n"$cmd);|;
+        say $callfh qq|           fprintf(stderr, "$fmt\\n"$cmd);|;
         say $callfh '#endif';
         say $callfh "             state = $next_state;";
         say $callfh "             break;";
index 75d4684f8bcd56f919a3957f54b23646ed398706..d5ac18c665df6fecd4a4c172575d6e5578312ba1 100644 (file)
@@ -1,47 +1,10 @@
-# Default value so one can compile i3-input standalone
-TOPDIR=..
+all:
+       $(MAKE) -C .. i3-config-wizard/i3-config-wizard
 
-include $(TOPDIR)/common.mk
-
-# Depend on the object files of all source-files in src/*.c and on all header files
-AUTOGENERATED:=cfgparse.tab.c cfgparse.yy.c
-FILES:=$(patsubst %.c,%.o,$(filter-out $(AUTOGENERATED),$(wildcard *.c)))
-HEADERS:=$(wildcard *.h)
-
-CPPFLAGS += -I$(TOPDIR)/include
-
-# Depend on the specific file (.c for each .o) and on all headers
-%.o: %.c ${HEADERS}
-       echo "[i3-config-wizard] CC $<"
-       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
-
-all: i3-config-wizard
-
-i3-config-wizard: $(TOPDIR)/libi3/libi3.a cfgparse.y.o cfgparse.yy.o ${FILES}
-       echo "[i3-config-wizard] LINK i3-config-wizard"
-       $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
-
-$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c
-       $(MAKE) -C $(TOPDIR)/libi3
-
-cfgparse.yy.o: cfgparse.l cfgparse.y.o ${HEADERS}
-       echo "[i3-config-wizard] LEX $<"
-       $(FLEX) -i -o$(@:.o=.c) $<
-       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(@:.o=.c)
-
-cfgparse.y.o: cfgparse.y ${HEADERS}
-       echo "[i3-config-wizard] YACC $<"
-       $(BISON) --debug --verbose -b $(basename $< .y) -d $<
-       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(<:.y=.tab.c)
-
-
-install: all
-       echo "[i3-config-wizard] INSTALL"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-config-wizard $(DESTDIR)$(PREFIX)/bin/
+install:
+       $(MAKE) -C .. install-i3-config-wizard
 
 clean:
-       rm -f *.o cfgparse.tab.{c,h} cfgparse.output cfgparse.yy.c
+       $(MAKE) -C .. clean-i3-config-wizard
 
-distclean: clean
-       rm -f i3-config-wizard
+.PHONY: all install clean
index e5a86868e9139f477f9d772e037ac1763f205936..17c3953c449a6c470810ed76fbea6dd5a61d71ab 100644 (file)
@@ -39,6 +39,8 @@ extern FILE *yyin;
 YY_BUFFER_STATE yy_scan_string(const char *);
 
 static struct context *context;
+static xcb_connection_t *conn;
+static xcb_key_symbols_t *keysyms;
 
 /* We don’t need yydebug for now, as we got decent error messages using
  * yyerror(). Should you ever want to extend the parser, it might be handy
@@ -67,6 +69,13 @@ int yywrap() {
 char *rewrite_binding(const char *bindingline) {
     char *result = NULL;
 
+    conn = xcb_connect(NULL, NULL);
+    if (conn == NULL || xcb_connection_has_error(conn)) {
+        fprintf(stderr, "Cannot open display\n");
+        exit(1);
+    }
+    keysyms = xcb_key_symbols_alloc(conn);
+
     context = calloc(sizeof(struct context), 1);
 
     yy_scan_string(bindingline);
@@ -81,6 +90,8 @@ char *rewrite_binding(const char *bindingline) {
     if (context->line_copy)
         free(context->line_copy);
     free(context);
+    xcb_key_symbols_free(keysyms);
+    xcb_disconnect(conn);
 
     return result;
 }
@@ -101,6 +112,28 @@ static char *modifier_to_string(int modifiers) {
     else return strdup("");
 }
 
+/*
+ * Returns true if sym is bound to any key except for 'except_keycode' on the
+ * first four layers (normal, shift, mode_switch, mode_switch + shift).
+ *
+ */
+static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) {
+    xcb_keycode_t i,
+                  min_keycode = xcb_get_setup(conn)->min_keycode,
+                  max_keycode = xcb_get_setup(conn)->max_keycode;
+
+    for (i = min_keycode; i && i <= max_keycode; i++) {
+        if (i == except_keycode)
+            continue;
+        for (int level = 0; level < 4; level++) {
+            if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym)
+                continue;
+            return true;
+        }
+    }
+    return false;
+}
+
 %}
 
 %error-verbose
@@ -139,11 +172,22 @@ bindcode:
              * different key than the upper-case one (unlikely for letters, but
              * more likely for special characters). */
             level = 1;
+
+            /* Try to use the keysym on the first level (lower-case). In case
+             * this doesn’t make it ambiguous (think of a keyboard layout
+             * having '1' on two different keys, but '!' only on keycode 10),
+             * we’ll stick with the keysym of the first level.
+             *
+             * This reduces a lot of confusion for users who switch keyboard
+             * layouts from qwerty to qwertz or other slight variations of
+             * qwerty (yes, that happens quite often). */
+            KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, 0);
+            if (!keysym_used_on_other_key(sym, $4))
+                level = 0;
         }
         KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, level);
         char *str = XKeysymToString(sym);
         char *modifiers = modifier_to_string($<number>3);
-        // TODO: modifier to string
         sasprintf(&(context->result), "bindsym %s%s %s\n", modifiers, str, $<string>6);
         free(modifiers);
     }
diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk
new file mode 100644 (file)
index 0000000..bce679d
--- /dev/null
@@ -0,0 +1,37 @@
+ALL_TARGETS += i3-config-wizard/i3-config-wizard
+INSTALL_TARGETS += install-i3-config-wizard
+CLEAN_TARGETS += clean-i3-config-wizard
+
+i3_config_wizard_SOURCES_GENERATED  = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c
+i3_config_wizard_SOURCES           := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c))
+i3_config_wizard_HEADERS           := $(wildcard i3-config-wizard/*.h)
+i3_config_wizard_CFLAGS             = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS)
+i3_config_wizard_LIBS               = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS)
+
+i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_config_wizard_SOURCES:.c=.o)
+
+
+i3-config-wizard/%.o: i3-config-wizard/%.c $(i3_config_wizard_HEADERS)
+       echo "[i3-config-wizard] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
+
+i3-config-wizard/cfgparse.yy.c: i3-config-wizard/cfgparse.l i3-config-wizard/cfgparse.tab.o $(i3_config_wizard_HEADERS)
+       echo "[i3-config-wizard] LEX $<"
+       $(FLEX) -i -o $@ $<
+
+i3-config-wizard/cfgparse.tab.c: i3-config-wizard/cfgparse.y $(i3_config_wizard_HEADERS)
+       echo "[i3-config-wizard] YACC $<"
+       $(BISON) --debug --verbose -b $(basename $< .y) -d $<
+
+i3-config-wizard/i3-config-wizard: libi3.a $(i3_config_wizard_OBJECTS)
+       echo "[i3-config-wizard] Link i3-config-wizard"
+       $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_config_wizard_LIBS) $(LIBS)
+
+install-i3-config-wizard: i3-config-wizard/i3-config-wizard
+       echo "[i3-config-wizard] Install"
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
+       $(INSTALL) -m 0755 i3-config-wizard/i3-config-wizard $(DESTDIR)$(PREFIX)/bin/
+
+clean-i3-config-wizard:
+       echo "[i3-config-wizard] Clean"
+       rm -f $(i3_config_wizard_OBJECTS) $(i3_config_wizard_SOURCES_GENERATED) i3-config-wizard/i3-config-wizard i3-config-wizard/cfgparse.{output,dot}
index 18076e519113d9772b325652b20153b3ff127dc8..2b9e4fe939b44cf0131db7d19733c6e1be5ef7a5 100644 (file)
@@ -1,32 +1,10 @@
-# Default value so one can compile i3-dump-log standalone
-TOPDIR=..
+all:
+       $(MAKE) -C .. i3-dump-log/i3-dump-log
 
-include $(TOPDIR)/common.mk
-
-CFLAGS += -I$(TOPDIR)/include
-
-# Depend on the object files of all source-files in src/*.c and on all header files
-FILES=$(patsubst %.c,%.o,$(wildcard *.c))
-HEADERS=$(wildcard *.h)
-
-# Depend on the specific file (.c for each .o) and on all headers
-%.o: %.c ${HEADERS}
-       echo "[i3-dump-log] CC $<"
-       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
-
-all: i3-dump-log
-
-i3-dump-log: ${FILES}
-       echo "[i3-dump-log] LINK i3-dump-log"
-       $(CC) $(LDFLAGS) -o i3-dump-log ${FILES} $(LIBS)
-
-install: all
-       echo "[i3-dump-log] INSTALL"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-dump-log $(DESTDIR)$(PREFIX)/bin/
+install:
+       $(MAKE) -C .. install-i3-dump-log
 
 clean:
-       rm -f *.o
+       $(MAKE) -C .. clean-i3-dump-log
 
-distclean: clean
-       rm -f i3-dump-log
+.PHONY: all install clean
diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk
new file mode 100644 (file)
index 0000000..17e53a6
--- /dev/null
@@ -0,0 +1,28 @@
+ALL_TARGETS += i3-dump-log/i3-dump-log
+INSTALL_TARGETS += install-i3-dump-log
+CLEAN_TARGETS += clean-i3-dump-log
+
+i3_dump_log_SOURCES := $(wildcard i3-dump-log/*.c)
+i3_dump_log_HEADERS := $(wildcard i3-dump-log/*.h)
+i3_dump_log_CFLAGS   = $(XCB_CFLAGS)
+i3_dump_log_LIBS     = $(XCB_LIBS)
+
+i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o)
+
+
+i3-dump-log/%.o: i3-dump-log/%.c $(i3_dump_log_HEADERS)
+       echo "[i3-dump-log] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
+
+i3-dump-log/i3-dump-log: libi3.a $(i3_dump_log_OBJECTS)
+       echo "[i3-dump-log] Link i3-dump-log"
+       $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_dump_log_LIBS)
+
+install-i3-dump-log: i3-dump-log/i3-dump-log
+       echo "[i3-dump-log] Install"
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
+       $(INSTALL) -m 0755 i3-dump-log/i3-dump-log $(DESTDIR)$(PREFIX)/bin/
+
+clean-i3-dump-log:
+       echo "[i3-dump-log] Clean"
+       rm -f $(i3_dump_log_OBJECTS) i3-dump-log/i3-dump-log
index 493df784d7e1aacfe5c549fa6dd60dbbf8c09240..e1c8eae5603f9eb5cabaf8494fa5aa19d41b7808 100644 (file)
@@ -1,35 +1,10 @@
-# Default value so one can compile i3-input standalone
-TOPDIR=..
+all:
+       $(MAKE) -C .. i3-input/i3-input
 
-include $(TOPDIR)/common.mk
-
-CPPFLAGS += -I$(TOPDIR)/include
-
-# Depend on the object files of all source-files in src/*.c and on all header files
-FILES=$(patsubst %.c,%.o,$(wildcard *.c))
-HEADERS=$(wildcard *.h)
-
-# Depend on the specific file (.c for each .o) and on all headers
-%.o: %.c ${HEADERS}
-       echo "[i3-input] CC $<"
-       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
-
-all: i3-input
-
-i3-input: $(TOPDIR)/libi3/libi3.a ${FILES}
-       echo "[i3-input] LINK i3-input"
-       $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
-
-$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c
-       $(MAKE) -C $(TOPDIR)/libi3
-
-install: all
-       echo "[i3-input] INSTALL"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-input $(DESTDIR)$(PREFIX)/bin/
+install:
+       $(MAKE) -C .. install-i3-input
 
 clean:
-       rm -f *.o
+       $(MAKE) -C .. clean-i3-input
 
-distclean: clean
-       rm -f i3-input
+.PHONY: all install clean
diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk
new file mode 100644 (file)
index 0000000..9fb4b19
--- /dev/null
@@ -0,0 +1,28 @@
+ALL_TARGETS += i3-input/i3-input
+INSTALL_TARGETS += install-i3-input
+CLEAN_TARGETS += clean-i3-input
+
+i3_input_SOURCES := $(wildcard i3-input/*.c)
+i3_input_HEADERS := $(wildcard i3-input/*.h)
+i3_input_CFLAGS   = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS)
+i3_input_LIBS     = $(XCB_LIBS) $(XCB_KBD_LIBS)
+
+i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o)
+
+
+i3-input/%.o: i3-input/%.c $(i3_input_HEADERS)
+       echo "[i3-input] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_input_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
+
+i3-input/i3-input: libi3.a $(i3_input_OBJECTS)
+       echo "[i3-input] Link i3-input"
+       $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_input_LIBS) $(LIBS)
+
+install-i3-input: i3-input/i3-input
+       echo "[i3-input] Install"
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
+       $(INSTALL) -m 0755 i3-input/i3-input $(DESTDIR)$(PREFIX)/bin/
+
+clean-i3-input:
+       echo "[i3-input] Clean"
+       rm -f $(i3_input_OBJECTS) i3-input/i3-input
index c8ff41c933bf368efad8677c7983aea2d2fafaff..ae5bf4deb02811e715885dc622c741dcf114a6cd 100755 (executable)
@@ -202,7 +202,7 @@ sub convert_command {
     # simple replacements
     my @replace = (
         qr/^s/ => 'layout stacking',
-        qr/^d/ => 'layout default',
+        qr/^d/ => 'layout toggle split',
         qr/^T/ => 'layout tabbed',
         qr/^f($|[^go])/ => 'fullscreen',
         qr/^fg/ => 'fullscreen global',
index 617df93279b774de32383538528183353cf391d0..fedb31e7902a1611f4d0f43f843dd3c6b1b25826 100644 (file)
@@ -1,32 +1,10 @@
-# Default value so one can compile i3-msg standalone
-TOPDIR=..
+all:
+       $(MAKE) -C .. i3-msg/i3-msg
 
-include $(TOPDIR)/common.mk
-
-CFLAGS += -I$(TOPDIR)/include
-
-# Depend on the object files of all source-files in src/*.c and on all header files
-FILES=$(patsubst %.c,%.o,$(wildcard *.c))
-HEADERS=$(wildcard *.h)
-
-# Depend on the specific file (.c for each .o) and on all headers
-%.o: %.c ${HEADERS}
-       echo "[i3-msg] CC $<"
-       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
-
-all: i3-msg
-
-i3-msg: ${FILES}
-       echo "[i3-msg] LINK i3-msg"
-       $(CC) $(LDFLAGS) -o i3-msg ${FILES} $(LIBS)
-
-install: all
-       echo "[i3-msg] INSTALL"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-msg $(DESTDIR)$(PREFIX)/bin/
+install:
+       $(MAKE) -C .. install-i3-msg
 
 clean:
-       rm -f *.o
+       $(MAKE) -C .. clean-i3-msg
 
-distclean: clean
-       rm -f i3-msg
+.PHONY: all install clean
diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk
new file mode 100644 (file)
index 0000000..01d5fc7
--- /dev/null
@@ -0,0 +1,28 @@
+ALL_TARGETS += i3-msg/i3-msg
+INSTALL_TARGETS += install-i3-msg
+CLEAN_TARGETS += clean-i3-msg
+
+i3_msg_SOURCES := $(wildcard i3-msg/*.c)
+i3_msg_HEADERS := $(wildcard i3-msg/*.h)
+i3_msg_CFLAGS   = $(XCB_CFLAGS)
+i3_msg_LIBS     = $(XCB_LIBS)
+
+i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o)
+
+
+i3-msg/%.o: i3-msg/%.c $(i3_msg_HEADERS)
+       echo "[i3-msg] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_msg_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
+
+i3-msg/i3-msg: libi3.a $(i3_msg_OBJECTS)
+       echo "[i3-msg] Link i3-msg"
+       $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_msg_LIBS)
+
+install-i3-msg: i3-msg/i3-msg
+       echo "[i3-msg] Install"
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
+       $(INSTALL) -m 0755 i3-msg/i3-msg $(DESTDIR)$(PREFIX)/bin/
+
+clean-i3-msg:
+       echo "[i3-msg] Clean"
+       rm -f $(i3_msg_OBJECTS) i3-msg/i3-msg
index 05a5b911c3f40224d13c33f61012b6f8fa0638ee..12748e218e477b564bcdd9e3c3166316f7ed77ff 100644 (file)
@@ -1,35 +1,10 @@
-# Default value so one can compile i3-nagbar standalone
-TOPDIR=..
+all:
+       $(MAKE) -C .. i3-nagbar/i3-nagbar
 
-include $(TOPDIR)/common.mk
-
-CPPFLAGS += -I$(TOPDIR)/include
-
-# Depend on the object files of all source-files in src/*.c and on all header files
-FILES=$(patsubst %.c,%.o,$(wildcard *.c))
-HEADERS=$(wildcard *.h)
-
-# Depend on the specific file (.c for each .o) and on all headers
-%.o: %.c ${HEADERS}
-       echo "[i3-nagbar] CC $<"
-       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
-
-all: i3-nagbar
-
-i3-nagbar: $(TOPDIR)/libi3/libi3.a ${FILES}
-       echo "[i3-nagbar] LINK i3-nagbar"
-       $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
-
-$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c
-       $(MAKE) -C $(TOPDIR)/libi3
-
-install: all
-       echo "[i3-nagbar] INSTALL"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-nagbar $(DESTDIR)$(PREFIX)/bin/
+install:
+       $(MAKE) -C .. install-i3-nagbar
 
 clean:
-       rm -f *.o
+       $(MAKE) -C .. clean-i3-nagbar
 
-distclean: clean
-       rm -f i3-nagbar
+.PHONY: all install clean
diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk
new file mode 100644 (file)
index 0000000..f54d838
--- /dev/null
@@ -0,0 +1,28 @@
+ALL_TARGETS += i3-nagbar/i3-nagbar
+INSTALL_TARGETS += install-i3-nagbar
+CLEAN_TARGETS += clean-i3-nagbar
+
+i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c)
+i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h)
+i3_nagbar_CFLAGS   = $(XCB_CFLAGS)
+i3_nagbar_LIBS     = $(XCB_LIBS)
+
+i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o)
+
+
+i3-nagbar/%.o: i3-nagbar/%.c $(i3_nagbar_HEADERS)
+       echo "[i3-nagbar] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
+
+i3-nagbar/i3-nagbar: libi3.a $(i3_nagbar_OBJECTS)
+       echo "[i3-nagbar] Link i3-nagbar"
+       $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_nagbar_LIBS) $(LIBS)
+
+install-i3-nagbar: i3-nagbar/i3-nagbar
+       echo "[i3-nagbar] Install"
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
+       $(INSTALL) -m 0755 i3-nagbar/i3-nagbar $(DESTDIR)$(PREFIX)/bin/
+
+clean-i3-nagbar:
+       echo "[i3-nagbar] Clean"
+       rm -f $(i3_nagbar_OBJECTS) i3-nagbar/i3-nagbar
index a9975740864c07c51e2c18e1095f8eb43511ea65..fddefae10048f0f546e4b0404e32caeae0c7d1f4 100755 (executable)
@@ -8,7 +8,7 @@
 # Distributions/packagers should enhance this script with a
 # distribution-specific mechanism to find the preferred terminal emulator. On
 # Debian, there is the x-terminal-emulator symlink for example.
-for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm; do
+for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
     if which $terminal > /dev/null 2>&1; then
         exec $terminal "$@"
     fi
index 1a457fcab01d57c1634bd59f906bb421cd7945b8..d29155ed328df2a1fda77f3c459dd10f6e1cc541 100644 (file)
--- a/i3.config
+++ b/i3.config
@@ -57,10 +57,10 @@ bindsym Mod1+v split v
 # enter fullscreen mode for the focused container
 bindsym Mod1+f fullscreen
 
-# change container layout (stacked, tabbed, default)
+# change container layout (stacked, tabbed, toggle split)
 bindsym Mod1+s layout stacking
 bindsym Mod1+w layout tabbed
-bindsym Mod1+e layout default
+bindsym Mod1+e layout toggle split
 
 # toggle tiling / floating
 bindsym Mod1+Shift+space floating toggle
@@ -103,7 +103,7 @@ bindsym Mod1+Shift+c reload
 # restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
 bindsym Mod1+Shift+r restart
 # exit i3 (logs you out of your X session)
-bindsym Mod1+Shift+e exit
+bindsym Mod1+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'"
 
 # resize window (you can also use the mouse for that)
 mode "resize" {
index e360fff9c2a2987ba9dcfb2a8a538eba9dee6c98..1bff8890c83654e635e30bdb8d2f9a8e560dc653 100644 (file)
@@ -58,10 +58,10 @@ bindcode $mod+55 split v
 # enter fullscreen mode for the focused container
 bindcode $mod+41 fullscreen
 
-# change container layout (stacked, tabbed, default)
+# change container layout (stacked, tabbed, toggle split)
 bindcode $mod+39 layout stacking
 bindcode $mod+25 layout tabbed
-bindcode $mod+26 layout default
+bindcode $mod+26 layout toggle split
 
 # toggle tiling / floating
 bindcode $mod+Shift+65 floating toggle
@@ -104,7 +104,7 @@ bindcode $mod+Shift+54 reload
 # restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
 bindcode $mod+Shift+27 restart
 # exit i3 (logs you out of your X session)
-bindcode $mod+Shift+26 exit
+bindcode $mod+Shift+26 exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'"
 
 # resize window (you can also use the mouse for that)
 mode "resize" {
index 79d0e7cd4e0e58f43d97bb1db4064503a5785ad9..cd23cd819cfe66711303b95c91e32e74f6bcc726 100644 (file)
@@ -1,42 +1,10 @@
-TOPDIR=..
+all:
+       $(MAKE) -C .. i3bar/i3bar
 
-include $(TOPDIR)/common.mk
-
-FILES:=$(wildcard src/*.c)
-FILES:=$(FILES:.c=.o)
-HEADERS:=$(wildcard include/*.h)
-
-CPPFLAGS += -I$(TOPDIR)/include
-
-all: i3bar doc
-
-i3bar: $(TOPDIR)/libi3/libi3.a ${FILES}
-       echo "[i3bar] LINK"
-       $(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
-
-$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c
-       $(MAKE) -C $(TOPDIR)/libi3
-
-doc:
-       echo ""
-       echo "[i3bar] SUBDIR doc"
-       $(MAKE) -C doc
-
-src/%.o: src/%.c ${HEADERS}
-       echo "[i3bar] CC $<"
-       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
-
-install: all
-       echo "[i3bar] INSTALL"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin
+install:
+       $(MAKE) -C .. install-i3bar
 
 clean:
-       rm -f src/*.o
-       $(MAKE) -C doc clean
-
-distclean: clean
-       rm -f i3bar
-       $(MAKE) -C doc distclean
+       $(MAKE) -C .. clean-i3bar
 
-.PHONY: install clean distclean doc
+.PHONY: all install clean
diff --git a/i3bar/doc/Makefile b/i3bar/doc/Makefile
deleted file mode 100644 (file)
index 6956675..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-all: i3bar.1
-
-i3bar.1: i3bar.man
-       echo "A2X i3bar"
-       a2x --no-xmllint -f manpage i3bar.man
-clean:
-       rm -f i3bar.xml i3bar.1 i3bar.html
-
-distclean: clean
diff --git a/i3bar/doc/i3bar.man b/i3bar/doc/i3bar.man
deleted file mode 100644 (file)
index dcf3022..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-i3bar(1)
-========
-Axel Wagner <mail+i3bar@merovius.de>
-v4.1, October 2011
-
-== NAME
-
-i3bar - xcb-based status- and workspace-bar
-
-== SYNOPSIS
-
-*i3bar* [*-s* 'sock_path'] [*-b* 'bar_id'] [*-v*] [*-h*]
-
-== WARNING
-
-i3bar will automatically be invoked by i3 for every 'bar' configuration block.
-
-Starting it manually is usually not what you want to do.
-
-You have been warned!
-
-== OPTIONS
-
-*-s, --socket* 'sock_path'::
-Overwrites the path to the i3 IPC socket.
-
-*-b, --bar_id* 'bar_id'::
-Specifies the bar ID for which to get the configuration from i3.
-
-*-v, --version*::
-Display version number and exit.
-
-*-h, --help*::
-Display a short help-message and exit
-
-== DESCRIPTION
-
-*i3bar* displays a bar at the bottom (or top) of your monitor(s) containing
-workspace switching buttons and a statusline generated by i3status(1) or
-similar. It is automatically invoked (and configured through) i3.
-
-i3bar does not support any color or other markups, so stdin should be plain
-utf8, one line at a time. If you use *i3status*(1), you therefore should
-specify 'output_format = none' in the general section of its config file.
-
-== ENVIRONMENT
-
-=== I3SOCK
-
-Used as a fallback for the i3 IPC socket path if neither the commandline
-contains an argument nor the I3_SOCKET_PATH property is set on the X11 root
-window.
-
-== EXAMPLES
-
-Nothing to see here, move along. As stated above, you should not run i3bar manually.
-
-Instead, see the i3 documentation, especially the User’s Guide.
-
-== SEE ALSO
-
-+i3status(1)+ or +conky(1)+ for programs generating a statusline.
-
-+dzen2(1)+ or +xmobar(1)+ for similar programs to i3bar.
-
-== AUTHORS
-
-Axel Wagner and contributors
diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk
new file mode 100644 (file)
index 0000000..cf3bf34
--- /dev/null
@@ -0,0 +1,28 @@
+ALL_TARGETS += i3bar/i3bar
+INSTALL_TARGETS += install-i3bar
+CLEAN_TARGETS += clean-i3bar
+
+i3bar_SOURCES := $(wildcard i3bar/src/*.c)
+i3bar_HEADERS := $(wildcard i3bar/include/*.h)
+i3bar_CFLAGS   = $(XCB_CFLAGS) $(X11_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
+i3bar_LIBS     = $(XCB_LIBS) $(X11_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS)
+
+i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)
+
+
+i3bar/src/%.o: i3bar/src/%.c $(i3bar_HEADERS)
+       echo "[i3bar] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3bar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $<
+
+i3bar/i3bar: libi3.a $(i3bar_OBJECTS)
+       echo "[i3bar] Link i3bar"
+       $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3bar_LIBS) $(LIBS)
+
+install-i3bar: i3bar/i3bar
+       echo "[i3bar] Install"
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
+       $(INSTALL) -m 0755 i3bar/i3bar $(DESTDIR)$(PREFIX)/bin/
+
+clean-i3bar:
+       echo "[i3bar] Clean"
+       rm -f $(i3bar_OBJECTS) i3bar/i3bar
index 289d7d9ed96a5e759bafe8784c24edc7c14df154..419a47238fda317f784f57e7af5aff048128c924 100644 (file)
@@ -1464,6 +1464,9 @@ void draw_bars() {
         }
 
         i3_ws *ws_walk;
+        static char *last_urgent_ws = NULL;
+        bool has_urgent = false, walks_away = true;
+
         TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
             DLOG("Drawing Button for WS %s at x = %d, len = %d\n", ws_walk->name, i, ws_walk->name_width);
             uint32_t fg_color = colors.inactive_ws_fg;
@@ -1478,6 +1481,8 @@ void draw_bars() {
                     fg_color = colors.focus_ws_fg;
                     bg_color = colors.focus_ws_bg;
                     border_color = colors.focus_ws_border;
+                    if (last_urgent_ws && strcmp(ws_walk->name, last_urgent_ws) == 0)
+                        walks_away = false;
                 }
             }
             if (ws_walk->urgent) {
@@ -1485,6 +1490,11 @@ void draw_bars() {
                 fg_color = colors.urgent_ws_fg;
                 bg_color = colors.urgent_ws_bg;
                 border_color = colors.urgent_ws_border;
+                has_urgent = true;
+                if (!ws_walk->focused) {
+                    FREE(last_urgent_ws);
+                    last_urgent_ws = sstrdup(ws_walk->name);
+                }
                 /* The urgent-hint should get noticed, so we unhide the bars shortly */
                 unhide_bars();
             }
@@ -1517,6 +1527,11 @@ void draw_bars() {
             i += 10 + ws_walk->name_width + 1;
         }
 
+        if (!has_urgent && !mod_pressed && walks_away) {
+            FREE(last_urgent_ws);
+            hide_bars();
+        }
+
         i = 0;
     }
 
index 11eaaba49d7f5a48dda5ca4729e6b3ccb4f5a375..c6b0351f87d9c39e9f2024af3083e632c95a0b5f 100644 (file)
@@ -54,6 +54,7 @@
 #include "i3.h"
 #include "x.h"
 #include "click.h"
+#include "key_press.h"
 #include "floating.h"
 #include "config.h"
 #include "handlers.h"
index 85057d1919e9d31a171598d115d5491eabb07263..37ee98d9c6d797886b1500b5110b4da7300605c7 100644 (file)
@@ -200,11 +200,17 @@ void cmd_fullscreen(I3_CMD, char *fullscreen_mode);
 void cmd_move_direction(I3_CMD, char *direction, char *move_px);
 
 /**
- * Implementation of 'layout default|stacked|stacking|tabbed'.
+ * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
  *
  */
 void cmd_layout(I3_CMD, char *layout_str);
 
+/**
+ * Implementation of 'layout toggle [all|split]'.
+ *
+ */
+void cmd_layout_toggle(I3_CMD, char *toggle_mode);
+
 /**
  * Implementaiton of 'exit'.
  *
index b14c477e53de6fb864b8d1dc06e29c1b36510fd3..1965da7cbfe2a1fa5700ebd613bb1f212adaa95c 100644 (file)
@@ -248,6 +248,15 @@ void con_set_border_style(Con *con, int border_style);
  */
 void con_set_layout(Con *con, int layout);
 
+/**
+ * This function toggles the layout of a given container. toggle_mode can be
+ * either 'default' (toggle only between stacked/tabbed/last_split_layout),
+ * 'split' (toggle only between splitv/splith) or 'all' (toggle between all
+ * layouts).
+ *
+ */
+void con_toggle_layout(Con *con, const char *toggle_mode);
+
 /**
  * Determines the minimum size of the given con by looking at its children (for
  * split/stacked/tabbed cons). Will be called when resizing floating cons
@@ -255,4 +264,27 @@ void con_set_layout(Con *con, int layout);
  */
 Rect con_minimum_size(Con *con);
 
+/**
+ * Returns true if changing the focus to con would be allowed considering
+ * the fullscreen focus constraints. Specifically, if a fullscreen container or
+ * any of its descendants is focused, this function returns true if and only if
+ * focusing con would mean that focus would still be visible on screen, i.e.,
+ * the newly focused container would not be obscured by a fullscreen container.
+ *
+ * In the simplest case, if a fullscreen container or any of its descendants is
+ * fullscreen, this functions returns true if con is the fullscreen container
+ * itself or any of its descendants, as this means focus wouldn't escape the
+ * boundaries of the fullscreen container.
+ *
+ * In case the fullscreen container is of type CF_OUTPUT, this function returns
+ * true if con is on a different workspace, as focus wouldn't be obscured by
+ * the fullscreen container that is constrained to a different workspace.
+ *
+ * Note that this same logic can be applied to moving containers. If a
+ * container can be focused under the fullscreen focus constraints, it can also
+ * become a parent or sibling to the currently focused container.
+ *
+ */
+bool con_fullscreen_permits_focusing(Con *con);
+
 #endif
index f4ed9a3ef598250fbb5a0f26471337c36a68f27d..d28f1756e46e274128347b8ef9db15ca25a23265 100644 (file)
@@ -423,6 +423,8 @@ struct Assignment {
  */
 struct Con {
     bool mapped;
+    /** whether this is a split container or not */
+    bool split;
     enum {
         CT_ROOT = 0,
         CT_OUTPUT = 1,
@@ -431,7 +433,6 @@ struct Con {
         CT_WORKSPACE = 4,
         CT_DOCKAREA = 5
     } type;
-    orientation_t orientation;
     struct Con *parent;
 
     struct Rect rect;
@@ -496,7 +497,15 @@ struct Con {
     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, L_DOCKAREA = 3, L_OUTPUT = 4 } layout;
+    enum {
+        L_DEFAULT = 0,
+        L_STACKED = 1,
+        L_TABBED = 2,
+        L_DOCKAREA = 3,
+        L_OUTPUT = 4,
+        L_SPLITV = 5,
+        L_SPLITH = 6
+    } layout, last_split_layout;
     border_style_t border_style;
     /** 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
diff --git a/include/key_press.h b/include/key_press.h
new file mode 100644 (file)
index 0000000..4d469ba
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * key_press.c: key press handler
+ *
+ */
+#ifndef _KEY_PRESS_H
+#define _KEY_PRESS_H
+
+/**
+ * There was a key press. We compare this key code with our bindings table and pass
+ * the bound action to parse_command().
+ *
+ */
+void handle_key_press(xcb_key_press_event_t *event);
+
+/**
+ * Kills the commanderror i3-nagbar process, if any.
+ *
+ * Called when reloading/restarting, since the user probably fixed his wrong
+ * keybindings.
+ *
+ * If wait_for_it is set (restarting), this function will waitpid(), otherwise,
+ * ev is assumed to handle it (reloading).
+ *
+ */
+void kill_commanderror_nagbar(bool wait_for_it);
+
+#endif
index e5e20dc12c00611aff2f36781e04e00647e78e62..11555ab77a6aa4dad62dde897bd29ab0a66068ce 100644 (file)
@@ -4,7 +4,7 @@
  * i3 - an improved dynamic tiling window manager
  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
  *
- * log.c: Setting of loglevels, logging functions.
+ * log.c: Logging functions.
  *
  */
 #ifndef _LOG_H
@@ -17,9 +17,8 @@
    is, delete the preceding comma */
 #define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__)
 #define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__)
-#define DLOG(fmt, ...) debuglog(LOGLEVEL, "%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
 
-extern char *loglevels[];
 extern char *errorfilename;
 extern char *shmlogname;
 extern int shmlog_size;
@@ -32,10 +31,10 @@ extern int shmlog_size;
 void init_logging(void);
 
 /**
- * Enables the given loglevel.
+ * Set debug logging.
  *
  */
-void add_loglevel(const char *level);
+void set_debug_logging(const bool _debug_logging);
 
 /**
  * Set verbosity of i3. If verbose is set to true, informative messages will
@@ -47,10 +46,10 @@ void set_verbosity(bool _verbose);
 
 /**
  * Logs the given message to stdout while prefixing the current time to it,
- * but only if the corresponding debug loglevel was activated.
+ * but only if debug logging was activated.
  *
  */
-void debuglog(uint64_t lev, char *fmt, ...);
+void debuglog(char *fmt, ...);
 
 /**
  * Logs the given message to stdout while prefixing the current time to it.
index d55bb6cba19869af8d4cc16eb9f650752a7436db..fe1e9f9537f47dcb13d029618763362c2048d054 100644 (file)
@@ -31,7 +31,7 @@ void regex_free(struct regex *regex);
 /**
  * Checks if the given regular expression matches the given input and returns
  * true if it does. In either case, it logs the outcome using LOG(), so it will
- * be visible without any debug loglevel.
+ * be visible without debug logging.
  *
  */
 bool regex_matches(struct regex *regex, const char *input);
index b9159e3b5b134851ae759ebb0935aadcdcd47230..8816b19a640a55d0fa84300bdd59295c56c24943 100644 (file)
@@ -39,16 +39,16 @@ Con *tree_open_con(Con *con, i3Window *window);
 void tree_split(Con *con, orientation_t orientation);
 
 /**
- * Moves focus one level up.
+ * Moves focus one level up. Returns true if focus changed.
  *
  */
-void level_up(void);
+bool level_up(void);
 
 /**
- * Moves focus one level down.
+ * Moves focus one level down. Returns true if focus changed.
  *
  */
-void level_down(void);
+bool level_down(void);
 
 /**
  * Renders the tree, that is rendering all outputs using render_con() and
index e9efcf7bdc2a4a3bff62d80a07606baea03f137e..2c2f68ad7dc94338282d16f756b6258fdb23ded9 100644 (file)
@@ -1,26 +1,7 @@
-# Default value so one can compile i3-msg standalone
-TOPDIR=..
-
-include $(TOPDIR)/common.mk
-
-CFLAGS += -I$(TOPDIR)/include
-
-# Depend on the object files of all source-files in src/*.c and on all header files
-FILES=$(patsubst %.c,%.o,$(wildcard *.c))
-HEADERS=$(wildcard *.h)
-
-# Depend on the specific file (.c for each .o) and on all headers
-%.o: %.c ${HEADERS}
-       echo "[libi3] CC $<"
-       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
-
-all: libi3.a
-
-libi3.a: ${FILES}
-       echo "[libi3] AR libi3.a"
-       ar rcs libi3.a ${FILES}
+all:
+       $(MAKE) -C .. libi3.a
 
 clean:
-       rm -f *.o libi3.a
+       $(MAKE) -C .. clean-libi3
 
-distclean: clean
+.PHONY: all clean
diff --git a/libi3/libi3.mk b/libi3/libi3.mk
new file mode 100644 (file)
index 0000000..6f6bf50
--- /dev/null
@@ -0,0 +1,21 @@
+CLEAN_TARGETS += clean-libi3
+
+libi3_SOURCES := $(wildcard libi3/*.c)
+libi3_HEADERS := $(wildcard libi3/*.h)
+libi3_CFLAGS   =
+libi3_LIBS     =
+
+libi3_OBJECTS := $(libi3_SOURCES:.c=.o)
+
+
+libi3/%.o: libi3/%.c $(libi3_HEADERS)
+       echo "[libi3] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(libi3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
+
+libi3.a: $(libi3_OBJECTS)
+       echo "[libi3] AR libi3.a"
+       ar rcs $@ $^ $(libi3_LIBS)
+
+clean-libi3:
+       echo "[libi3] Clean"
+       rm -f $(libi3_OBJECTS) libi3.a
index ff08dc57d7921240bcf7ceaecba6d2e4c93a58aa..e4cee0cc3c5433e8ad7fb981670e2aebeebb13ed 100644 (file)
@@ -1,15 +1,7 @@
-A2M:=a2x -f manpage --asciidoc-opts="-f asciidoc.conf"
-
-all: i3.1 i3-msg.1 i3-input.1 i3-nagbar.1 i3-config-wizard.1 i3-migrate-config-to-v4.1 i3-sensible-editor.1 i3-sensible-pager.1 i3-sensible-terminal.1 i3-dump-log.1
-
-%.1: %.man asciidoc.conf
-       ${A2M} $<
+all:
+       $(MAKE) -C .. mans
 
 clean:
-       for file in $$(echo i3 i3-msg i3-input i3-nagbar i3-wsbar i3-config-wizard i3-migrate-config-to-v4 i3-sensible-editor i3-sensible-pager i3-sensible-terminal i3-dump-log); \
-       do \
-               rm -f $${file}.1 $${file}.html $${file}.xml; \
-       done
+       $(MAKE) -C .. clean-mans
 
-distclean: clean
-       rm -f *.1
+.PHONY: all clean
index 8e9094ff0078c285954eb03d38b628e2f58ce920..eb8ba2f76286c6d10697abdef79c4291371d36f0 100644 (file)
@@ -14,7 +14,7 @@ i3-dump-log [-s <socketpath>]
 == DESCRIPTION
 
 Debug versions of i3 automatically use 1% of your RAM (but 25 MiB max) to store
-full debug loglevel log output. This is extremely helpful for bugreports and
+full debug log output. This is extremely helpful for bugreports and
 figuring out what is going on, without permanently logging to a file.
 
 With i3-dump-log, you can dump the SHM log to stdout.
index 7e32aab4f0337d1580739e2fa6cd79965c56a47a..1d9f9ff8647c3b4bba8949cb52e47abc8f36abc2 100644 (file)
@@ -1,7 +1,7 @@
 i3-sensible-terminal(1)
 =======================
-Michael Stapelberg <michael+i3@stapelberg.de>
-v4.1, November 2011
+Michael Stapelberg <michael@i3wm.org>
+v4.2, August 2012
 
 == NAME
 
@@ -30,6 +30,7 @@ It tries to start one of the following (in that order):
 * xterm
 * gnome-terminal
 * roxterm
+* xfce4-terminal
 
 Please don’t complain about the order: If the user has any preference, he will
 have $TERMINAL set or modified his i3 configuration file.
index 9d34c71038a316e01601ebe7543bffb15e7038a7..c20e39da4da4a42e171915d9a7c0f07f24a13d1a 100644 (file)
@@ -9,7 +9,7 @@ i3 - an improved dynamic, tiling window manager
 
 == SYNOPSIS
 
-i3 [-a] [-c configfile] [-C] [-d <loglevel>] [-v] [-V]
+i3 [-a] [-c configfile] [-C] [-d all] [-v] [-V]
 
 == OPTIONS
 
@@ -22,8 +22,9 @@ Specifies an alternate configuration file path.
 -C::
 Check the configuration file for validity and exit.
 
--d::
-Specifies the debug loglevel. To see the most output, use -d all.
+-d all::
+Enables debug logging.
+The 'all' parameter is present for historical reasons.
 
 -v::
 Display version number (and date of the last commit).
diff --git a/man/i3bar.man b/man/i3bar.man
new file mode 100644 (file)
index 0000000..dcf3022
--- /dev/null
@@ -0,0 +1,68 @@
+i3bar(1)
+========
+Axel Wagner <mail+i3bar@merovius.de>
+v4.1, October 2011
+
+== NAME
+
+i3bar - xcb-based status- and workspace-bar
+
+== SYNOPSIS
+
+*i3bar* [*-s* 'sock_path'] [*-b* 'bar_id'] [*-v*] [*-h*]
+
+== WARNING
+
+i3bar will automatically be invoked by i3 for every 'bar' configuration block.
+
+Starting it manually is usually not what you want to do.
+
+You have been warned!
+
+== OPTIONS
+
+*-s, --socket* 'sock_path'::
+Overwrites the path to the i3 IPC socket.
+
+*-b, --bar_id* 'bar_id'::
+Specifies the bar ID for which to get the configuration from i3.
+
+*-v, --version*::
+Display version number and exit.
+
+*-h, --help*::
+Display a short help-message and exit
+
+== DESCRIPTION
+
+*i3bar* displays a bar at the bottom (or top) of your monitor(s) containing
+workspace switching buttons and a statusline generated by i3status(1) or
+similar. It is automatically invoked (and configured through) i3.
+
+i3bar does not support any color or other markups, so stdin should be plain
+utf8, one line at a time. If you use *i3status*(1), you therefore should
+specify 'output_format = none' in the general section of its config file.
+
+== ENVIRONMENT
+
+=== I3SOCK
+
+Used as a fallback for the i3 IPC socket path if neither the commandline
+contains an argument nor the I3_SOCKET_PATH property is set on the X11 root
+window.
+
+== EXAMPLES
+
+Nothing to see here, move along. As stated above, you should not run i3bar manually.
+
+Instead, see the i3 documentation, especially the User’s Guide.
+
+== SEE ALSO
+
++i3status(1)+ or +conky(1)+ for programs generating a statusline.
+
++dzen2(1)+ or +xmobar(1)+ for similar programs to i3bar.
+
+== AUTHORS
+
+Axel Wagner and contributors
diff --git a/man/man.mk b/man/man.mk
new file mode 100644 (file)
index 0000000..f999dc7
--- /dev/null
@@ -0,0 +1,32 @@
+DISTCLEAN_TARGETS += clean-mans
+
+A2X = a2x
+
+A2X_MAN_CALL = $(V_A2X)$(A2X) -f manpage --asciidoc-opts="-f man/asciidoc.conf" $(A2X_FLAGS) $<
+
+MANS_1 = \
+       man/i3.1 \
+       man/i3bar.1 \
+       man/i3-msg.1 \
+       man/i3-input.1 \
+       man/i3-nagbar.1 \
+       man/i3-config-wizard.1 \
+       man/i3-migrate-config-to-v4.1 \
+       man/i3-sensible-editor.1 \
+       man/i3-sensible-pager.1 \
+       man/i3-sensible-terminal.1 \
+       man/i3-dump-log.1
+
+MANS = \
+       $(MANS_1)
+
+mans: $(MANS)
+
+$(MANS_1): %.1: %.man man/asciidoc.conf
+       $(A2X_MAN_CALL)
+
+clean-mans:
+       for file in $(notdir $(MANS)); \
+       do \
+               rm -f man/$${file} man/$${file%.*}.html man/$${file%.*}.xml; \
+       done
index b0fb9e0118a4e0f462a10c762d6891864da8a97b..b4c9e005bd8ab16583bae3ef2dc8aeab8984f1f9 100644 (file)
@@ -66,10 +66,20 @@ state BORDER:
   border_style = 'normal', 'none', '1pixel', 'toggle'
       -> call cmd_border($border_style)
 
-# layout default|stacked|stacking|tabbed
+# layout default|stacked|stacking|tabbed|splitv|splith
+# layout toggle [split|all]
 state LAYOUT:
-  layout_mode = 'default', 'stacked', 'stacking', 'tabbed'
+  layout_mode = 'default', 'stacked', 'stacking', 'tabbed', 'splitv', 'splith'
       -> call cmd_layout($layout_mode)
+  'toggle'
+      -> LAYOUT_TOGGLE
+
+# layout toggle [split|all]
+state LAYOUT_TOGGLE:
+  end
+      -> call cmd_layout_toggle($toggle_mode)
+  toggle_mode = 'split', 'all'
+      -> call cmd_layout_toggle($toggle_mode)
 
 # append_layout <path>
 state APPEND_LAYOUT:
@@ -190,7 +200,7 @@ state RENAME_WORKSPACE_TO:
       -> call cmd_rename_workspace($old_name, $new_name)
 
 # move <direction> [<pixels> [px]]
-# move [window|container] [to] workspace <str>
+# move [window|container] [to] workspace [<str>|next|prev|current]
 # move [window|container] [to] output <str>
 # move [window|container] [to] scratchpad
 # move workspace to [output] <str>
@@ -231,7 +241,7 @@ state MOVE_DIRECTION_PX:
 state MOVE_WORKSPACE:
   'to'
       -> MOVE_WORKSPACE_TO_OUTPUT
-  workspace = 'next', 'prev', 'next_on_output', 'prev_on_output'
+  workspace = 'next', 'prev', 'next_on_output', 'prev_on_output', 'current'
       -> call cmd_move_con_to_workspace($workspace)
   'number'
       -> MOVE_WORKSPACE_NUMBER
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..6a37f56
--- /dev/null
@@ -0,0 +1,10 @@
+all:
+       $(MAKE) -C .. i3
+
+install:
+       $(MAKE) -C .. install-i3
+
+clean:
+       $(MAKE) -C .. clean-i3
+
+.PHONY: all install clean
index ab8be57c120d6555d73a4712c452f0cad890bc96..68a51996c05bdc896ee16fbfed52898e83246051 100644 (file)
@@ -19,6 +19,9 @@ static Barconfig current_bar;
  * store this in a separate variable because in the i3 config struct we just
  * store the i3Font. */
 static char *font_pattern;
+/* The path to the temporary script files used by i3-nagbar. We need to keep
+ * them around to delete the files in the i3-nagbar SIGCHLD handler. */
+static char *edit_script_path, *pager_script_path;
 
 typedef struct yy_buffer_state *YY_BUFFER_STATE;
 extern int yylex(struct context *context);
@@ -233,6 +236,12 @@ static char *migrate_config(char *input, off_t size) {
  */
 static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
     ev_child_stop(EV_A_ watcher);
+
+    if (unlink(edit_script_path) != 0)
+        warn("Could not delete temporary i3-nagbar script %s", edit_script_path);
+    if (unlink(pager_script_path) != 0)
+        warn("Could not delete temporary i3-nagbar script %s", pager_script_path);
+
     if (!WIFEXITED(watcher->rstatus)) {
         fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
         return;
@@ -264,6 +273,23 @@ static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
 }
 #endif
 
+/*
+ * Writes the given command as a shell script to path.
+ * Returns true unless something went wrong.
+ *
+ */
+static bool write_nagbar_script(const char *path, const char *command) {
+    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
+    if (fd == -1) {
+        warn("Could not create temporary script to store the nagbar command");
+        return false;
+    }
+    write(fd, "#!/bin/sh\n", strlen("#!/bin/sh\n"));
+    write(fd, command, strlen(command));
+    close(fd);
+    return true;
+}
+
 /*
  * Starts an i3-nagbar process which alerts the user that his configuration
  * file contains one or more errors. Also offers two buttons: One to launch an
@@ -276,6 +302,18 @@ static void start_configerror_nagbar(const char *config_path) {
         return;
 
     fprintf(stderr, "Starting i3-nagbar due to configuration errors\n");
+
+    /* We need to create a custom script containing our actual command
+     * since not every terminal emulator which is contained in
+     * i3-sensible-terminal supports -e with multiple arguments (and not
+     * all of them support -e with one quoted argument either).
+     *
+     * NB: The paths need to be unique, that is, don’t assume users close
+     * their nagbars at any point in time (and they still need to work).
+     * */
+    edit_script_path = get_process_filename("nagbar-cfgerror-edit");
+    pager_script_path = get_process_filename("nagbar-cfgerror-pager");
+
     configerror_pid = fork();
     if (configerror_pid == -1) {
         warn("Could not fork()");
@@ -284,10 +322,17 @@ static void start_configerror_nagbar(const char *config_path) {
 
     /* child */
     if (configerror_pid == 0) {
+        char *edit_command, *pager_command;
+        sasprintf(&edit_command, "i3-sensible-editor \"%s\" && i3-msg reload\n", config_path);
+        sasprintf(&pager_command, "i3-sensible-pager \"%s\"\n", errorfilename);
+        if (!write_nagbar_script(edit_script_path, edit_command) ||
+            !write_nagbar_script(pager_script_path, pager_command))
+            return;
+
         char *editaction,
              *pageraction;
-        sasprintf(&editaction, "i3-sensible-terminal -e sh -c \"i3-sensible-editor \\\"%s\\\" && i3-msg reload\"", config_path);
-        sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename);
+        sasprintf(&editaction, "i3-sensible-terminal -e \"%s\"", edit_script_path);
+        sasprintf(&pageraction, "i3-sensible-terminal -e \"%s\"", pager_script_path);
         char *argv[] = {
             NULL, /* will be replaced by the executable path */
             "-t",
index ca2a10371d20b1097a66a17f39277b3eb42eeb8b..9f0549fa336a77b43cf59bdeaee82ea6ada689d1 100644 (file)
@@ -35,13 +35,13 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press
     Con *resize_con = con;
     while (resize_con->type != CT_WORKSPACE &&
            resize_con->type != CT_FLOATING_CON &&
-           resize_con->parent->orientation != orientation)
+           con_orientation(resize_con->parent) != orientation)
         resize_con = resize_con->parent;
 
     DLOG("resize_con = %p\n", resize_con);
     if (resize_con->type != CT_WORKSPACE &&
         resize_con->type != CT_FLOATING_CON &&
-        resize_con->parent->orientation == orientation) {
+        con_orientation(resize_con->parent) == orientation) {
         first = resize_con;
         second = (way == 'n') ? TAILQ_NEXT(first, nodes) : TAILQ_PREV(first, nodes_head, nodes);
         if (second == TAILQ_END(&(first->nodes_head))) {
@@ -145,7 +145,7 @@ static bool tiling_resize(Con *con, xcb_button_press_event_t *event, const click
 
         if ((check_con->layout == L_STACKED ||
              check_con->layout == L_TABBED ||
-             check_con->orientation == HORIZ) &&
+             con_orientation(check_con) == HORIZ) &&
             con_num_children(check_con) > 1) {
             DLOG("Not handling this resize, this container has > 1 child.\n");
             return false;
index 66270f7599fd8e406bd1d5ba93e39d3a73373acf..44e6a23d4533f275b6ddffd15c96dd905c3899ea 100644 (file)
@@ -23,7 +23,7 @@
 } while (0)
 
 /** When the command did not include match criteria (!), we use the currently
- * focused command. Do not confuse this case with a command which included
+ * focused container. Do not confuse this case with a command which included
  * criteria but which did not match any windows. This macro has to be called in
  * every command.
  */
@@ -351,7 +351,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
 
 /*
  * Implementation of 'move [window|container] [to] workspace
- * next|prev|next_on_output|prev_on_output'.
+ * next|prev|next_on_output|prev_on_output|current'.
  *
  */
 void cmd_move_con_to_workspace(I3_CMD, char *which) {
@@ -359,6 +359,15 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
 
     DLOG("which=%s\n", which);
 
+    /* We have nothing to move:
+     *  when criteria was specified but didn't match any window or
+     *  when criteria wasn't specified and we don't have any window focused. */
+    if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
+        (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
+        ysuccess(false);
+        return;
+    }
+
     HANDLE_EMPTY_MATCH;
 
     /* get the workspace */
@@ -371,6 +380,8 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
         ws = workspace_next_on_output();
     else if (strcmp(which, "prev_on_output") == 0)
         ws = workspace_prev_on_output();
+    else if (strcmp(which, "current") == 0)
+        ws = con_get_workspace(focused);
     else {
         ELOG("BUG: called with which=%s\n", which);
         ysuccess(false);
@@ -400,9 +411,11 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
 
     owindow *current;
 
-    /* Error out early to not create a non-existing workspace (in
-     * workspace_get()) if we are not actually able to move anything. */
-    if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
+    /* We have nothing to move:
+     *  when criteria was specified but didn't match any window or
+     *  when criteria wasn't specified and we don't have any window focused. */
+    if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
+        (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
         ysuccess(false);
         return;
     }
@@ -430,9 +443,11 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
 void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
     owindow *current;
 
-    /* Error out early to not create a non-existing workspace (in
-     * workspace_get()) if we are not actually able to move anything. */
-    if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
+    /* We have nothing to move:
+     *  when criteria was specified but didn't match any window or
+     *  when criteria wasn't specified and we don't have any window focused. */
+    if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
+        (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
         ysuccess(false);
         return;
     }
@@ -504,6 +519,8 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
     LOG("tiling resize\n");
     /* get the appropriate current container (skip stacked/tabbed cons) */
     Con *current = focused;
+    Con *other = NULL;
+    double percentage = 0;
     while (current->parent->layout == L_STACKED ||
            current->parent->layout == L_TABBED)
         current = current->parent;
@@ -512,40 +529,50 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
     orientation_t search_orientation =
         (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT);
 
-    while (current->type != CT_WORKSPACE &&
-           current->type != CT_FLOATING_CON &&
-           current->parent->orientation != search_orientation)
-        current = current->parent;
+    do {
+        if (con_orientation(current->parent) != search_orientation) {
+            current = current->parent;
+            continue;
+        }
 
-    /* get the default percentage */
-    int children = con_num_children(current->parent);
-    Con *other;
-    LOG("ins. %d children\n", children);
-    double percentage = 1.0 / children;
-    LOG("default percentage = %f\n", percentage);
+        /* get the default percentage */
+        int children = con_num_children(current->parent);
+        LOG("ins. %d children\n", children);
+        percentage = 1.0 / children;
+        LOG("default percentage = %f\n", percentage);
 
-    orientation_t orientation = current->parent->orientation;
+        orientation_t orientation = con_orientation(current->parent);
 
-    if ((orientation == HORIZ &&
-         (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
-        (orientation == VERT &&
-         (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
-        LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
-            (orientation == HORIZ ? "horizontal" : "vertical"));
-        ysuccess(false);
-        return false;
-    }
+        if ((orientation == HORIZ &&
+             (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
+            (orientation == VERT &&
+             (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
+            LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
+                (orientation == HORIZ ? "horizontal" : "vertical"));
+            ysuccess(false);
+            return false;
+        }
 
-    if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) {
-        other = TAILQ_PREV(current, nodes_head, nodes);
-    } else {
-        other = TAILQ_NEXT(current, nodes);
-    }
-    if (other == TAILQ_END(workspaces)) {
-        LOG("No other container in this direction found, cannot resize.\n");
+        if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) {
+            other = TAILQ_PREV(current, nodes_head, nodes);
+        } else {
+            other = TAILQ_NEXT(current, nodes);
+        }
+        if (other == TAILQ_END(workspaces)) {
+            LOG("No other container in this direction found, trying to look further up in the tree...\n");
+            current = current->parent;
+            continue;
+        }
+        break;
+    } while (current->type != CT_WORKSPACE &&
+             current->type != CT_FLOATING_CON);
+
+    if (other == NULL) {
+        LOG("No other container in this direction found, trying to look further up in the tree...\n");
         ysuccess(false);
         return false;
     }
+
     LOG("other->percent = %f\n", other->percent);
     LOG("current->percent before = %f\n", current->percent);
     if (current->percent == 0.0)
@@ -585,7 +612,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
 
     while (current->type != CT_WORKSPACE &&
            current->type != CT_FLOATING_CON &&
-           current->parent->orientation != search_orientation)
+           con_orientation(current->parent) != search_orientation)
         current = current->parent;
 
     /* get the default percentage */
@@ -594,7 +621,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
     double percentage = 1.0 / children;
     LOG("default percentage = %f\n", percentage);
 
-    orientation_t orientation = current->parent->orientation;
+    orientation_t orientation = con_orientation(current->parent);
 
     if ((orientation == HORIZ &&
          strcmp(direction, "height") == 0) ||
@@ -1226,23 +1253,26 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) {
  *
  */
 void cmd_focus_level(I3_CMD, char *level) {
-    if (focused &&
-        focused->type != CT_WORKSPACE &&
-        focused->fullscreen_mode != CF_NONE) {
-        LOG("Cannot change focus while in fullscreen mode.\n");
-        ysuccess(false);
-        return;
-    }
-
     DLOG("level = %s\n", level);
+    bool success = false;
+
+    /* Focusing the parent can only be allowed if the newly
+     * focused container won't escape the fullscreen container. */
+    if (strcmp(level, "parent") == 0) {
+        if (focused && focused->parent) {
+            if (con_fullscreen_permits_focusing(focused->parent))
+                success = level_up();
+            else
+                LOG("Currently in fullscreen, not going up\n");
+        }
+    }
 
-    if (strcmp(level, "parent") == 0)
-        level_up();
-    else level_down();
+    /* Focusing a child should always be allowed. */
+    else success = level_down();
 
-    cmd_output->needs_tree_render = true;
+    cmd_output->needs_tree_render = success;
     // XXX: default reply for now, make this a better reply
-    ysuccess(true);
+    ysuccess(success);
 }
 
 /*
@@ -1275,13 +1305,9 @@ void cmd_focus(I3_CMD) {
         if (!ws)
             continue;
 
-        /* Don't allow the focus switch if the focused and current
-         * containers are in the same workspace. */
-        if (focused &&
-            focused->type != CT_WORKSPACE &&
-            focused->fullscreen_mode != CF_NONE &&
-            con_get_workspace(focused) == ws) {
-            LOG("Cannot change focus while in fullscreen mode (same workspace).\n");
+        /* Check the fullscreen focus constraints. */
+        if (!con_fullscreen_permits_focusing(current->con)) {
+            LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n");
             ysuccess(false);
             return;
         }
@@ -1376,17 +1402,27 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
 }
 
 /*
- * Implementation of 'layout default|stacked|stacking|tabbed'.
+ * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
  *
  */
 void cmd_layout(I3_CMD, char *layout_str) {
     if (strcmp(layout_str, "stacking") == 0)
         layout_str = "stacked";
-    DLOG("changing layout to %s\n", layout_str);
     owindow *current;
-    int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT :
-                  (strcmp(layout_str, "stacked") == 0 ? L_STACKED :
-                   L_TABBED));
+    int layout;
+    /* default is a special case which will be handled in con_set_layout(). */
+    if (strcmp(layout_str, "default") == 0)
+        layout = L_DEFAULT;
+    else if (strcmp(layout_str, "stacked") == 0)
+        layout = L_STACKED;
+    else if (strcmp(layout_str, "tabbed") == 0)
+        layout = L_TABBED;
+    else if (strcmp(layout_str, "splitv") == 0)
+        layout = L_SPLITV;
+    else if (strcmp(layout_str, "splith") == 0)
+        layout = L_SPLITH;
+
+    DLOG("changing layout to %s (%d)\n", layout_str, layout);
 
     /* check if the match is empty, not if the result is empty */
     if (match_is_empty(current_match))
@@ -1403,12 +1439,40 @@ void cmd_layout(I3_CMD, char *layout_str) {
     ysuccess(true);
 }
 
+/*
+ * Implementation of 'layout toggle [all|split]'.
+ *
+ */
+void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
+    owindow *current;
+
+    if (toggle_mode == NULL)
+        toggle_mode = "default";
+
+    DLOG("toggling layout (mode = %s)\n", toggle_mode);
+
+    /* check if the match is empty, not if the result is empty */
+    if (match_is_empty(current_match))
+        con_toggle_layout(focused->parent, toggle_mode);
+    else {
+        TAILQ_FOREACH(current, &owindows, owindows) {
+            DLOG("matching: %p / %s\n", current->con, current->con->name);
+            con_toggle_layout(current->con, toggle_mode);
+        }
+    }
+
+    cmd_output->needs_tree_render = true;
+    // XXX: default reply for now, make this a better reply
+    ysuccess(true);
+}
+
 /*
  * Implementaiton of 'exit'.
  *
  */
 void cmd_exit(I3_CMD) {
     LOG("Exiting due to user command.\n");
+    xcb_disconnect(conn);
     exit(0);
 
     /* unreached */
@@ -1421,6 +1485,7 @@ void cmd_exit(I3_CMD) {
 void cmd_reload(I3_CMD) {
     LOG("reloading\n");
     kill_configerror_nagbar(false);
+    kill_commanderror_nagbar(false);
     load_configuration(conn, NULL, true);
     x_set_i3_atoms();
     /* Send an IPC event just in case the ws names have changed */
@@ -1449,6 +1514,7 @@ void cmd_restart(I3_CMD) {
 void cmd_open(I3_CMD) {
     LOG("opening new container\n");
     Con *con = tree_open_con(NULL, NULL);
+    con->layout = L_SPLITH;
     con_focus(con);
 
     y(map_open);
index 73a14565ff3201ff0f8b741fcfdadf8ee51cd3a6..9af8e981227581422f3386619b790b9cf72b21fb 100644 (file)
@@ -389,9 +389,9 @@ struct CommandResult *parse_command(const char *input) {
                 position[(copywalk - input)] = (copywalk >= walk ? '^' : ' ');
             position[len] = '\0';
 
-            printf("%s\n", errormessage);
-            printf("Your command: %s\n", input);
-            printf("              %s\n", position);
+            ELOG("%s\n", errormessage);
+            ELOG("Your command: %s\n", input);
+            ELOG("              %s\n", position);
 
             /* Format this error message as a JSON reply. */
             y(map_open);
@@ -427,15 +427,23 @@ struct CommandResult *parse_command(const char *input) {
 
 /*
  * Logs the given message to stdout while prefixing the current time to it,
- * but only if the corresponding debug loglevel was activated.
+ * but only if debug logging was activated.
  * This is to be called by DLOG() which includes filename/linenumber
  *
  */
-void debuglog(uint64_t lev, char *fmt, ...) {
+void debuglog(char *fmt, ...) {
+    va_list args;
+
+    va_start(args, fmt);
+    fprintf(stdout, "# ");
+    vfprintf(stdout, fmt, args);
+    va_end(args);
+}
+
+void errorlog(char *fmt, ...) {
     va_list args;
 
     va_start(args, fmt);
-    fprintf(stderr, "# ");
     vfprintf(stderr, fmt, args);
     va_end(args);
 }
index c24a379f7abc186a1fd2dc5944da7a1e03992be0..cb4d080501607e0911249270c4fa01727c792407 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -217,8 +217,8 @@ bool con_accepts_window(Con *con) {
     if (con->type == CT_WORKSPACE)
         return false;
 
-    if (con->orientation != NO_ORIENTATION) {
-        DLOG("container %p does not accepts windows, orientation != NO_ORIENTATION\n", con);
+    if (con->split) {
+        DLOG("container %p does not accept windows, it is a split container.\n", con);
         return false;
     }
 
@@ -265,8 +265,11 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation) {
     while (con_orientation(parent) != orientation) {
         DLOG("Need to go one level further up\n");
         parent = parent->parent;
-        /* Abort when we reach a floating con */
-        if (parent && parent->type == CT_FLOATING_CON)
+        /* Abort when we reach a floating con, or an output con */
+        if (parent &&
+            (parent->type == CT_FLOATING_CON ||
+             parent->type == CT_OUTPUT ||
+             (parent->parent && parent->parent->type == CT_OUTPUT)))
             parent = NULL;
         if (parent == NULL)
             break;
@@ -576,11 +579,27 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
         return;
     }
 
+    /* Prevent moving if this would violate the fullscreen focus restrictions. */
+    if (!con_fullscreen_permits_focusing(workspace)) {
+        LOG("Cannot move out of a fullscreen container");
+        return;
+    }
+
     if (con_is_floating(con)) {
         DLOG("Using FLOATINGCON instead\n");
         con = con->parent;
     }
 
+    Con *source_ws = con_get_workspace(con);
+    if (workspace == source_ws) {
+        DLOG("Not moving, already there\n");
+        return;
+    }
+
+    /* Save the current workspace. So we can call workspace_show() by the end
+     * of this function. */
+    Con *current_ws = con_get_workspace(focused);
+
     Con *source_output = con_get_output(con),
         *dest_output = con_get_output(workspace);
 
@@ -663,8 +682,12 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
         /* Descend focus stack in case focus_next is a workspace which can
          * occur if we move to the same workspace.  Also show current workspace
          * to ensure it is focused. */
-        workspace_show(con_get_workspace(focus_next));
-        con_focus(con_descend_focused(focus_next));
+        workspace_show(current_ws);
+
+        /* Set focus only if con was on current workspace before moving.
+         * Otherwise we would give focus to some window on different workspace. */
+        if (source_ws == current_ws)
+            con_focus(con_descend_focused(focus_next));
     }
 
     CALL(parent, on_remove_child);
@@ -677,14 +700,32 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
  *
  */
 int con_orientation(Con *con) {
-    /* stacking containers behave like they are in vertical orientation */
-    if (con->layout == L_STACKED)
-        return VERT;
-
-    if (con->layout == L_TABBED)
-        return HORIZ;
-
-    return con->orientation;
+    switch (con->layout) {
+        case L_SPLITV:
+        /* stacking containers behave like they are in vertical orientation */
+        case L_STACKED:
+            return VERT;
+
+        case L_SPLITH:
+        /* tabbed containers behave like they are in vertical orientation */
+        case L_TABBED:
+            return HORIZ;
+
+        case L_DEFAULT:
+            DLOG("Someone called con_orientation() on a con with L_DEFAULT, this is a bug in the code.\n");
+            assert(false);
+            return HORIZ;
+
+        case L_DOCKAREA:
+        case L_OUTPUT:
+            DLOG("con_orientation() called on dockarea/output (%d) container %p\n", con->layout, con);
+            assert(false);
+            return HORIZ;
+
+        default:
+            DLOG("con_orientation() ran into default\n");
+            assert(false);
+    }
 }
 
 /*
@@ -997,23 +1038,16 @@ void con_set_layout(Con *con, int layout) {
         Con *new = con_new(NULL, NULL);
         new->parent = con;
 
-        /* 2: set the requested layout on the split con */
-        new->layout = layout;
-
-        /* 3: While the layout is irrelevant in stacked/tabbed mode, it needs
-         * to be set. Otherwise, this con will not be interpreted as a split
-         * container. */
-        if (config.default_orientation == NO_ORIENTATION) {
-            new->orientation = (con->rect.height > con->rect.width) ? VERT : HORIZ;
-        } else {
-            new->orientation = config.default_orientation;
-        }
+        /* 2: Set the requested layout on the split container and mark it as
+         * split. */
+        con_set_layout(new, layout);
+        new->split = true;
 
         Con *old_focused = TAILQ_FIRST(&(con->focus_head));
         if (old_focused == TAILQ_END(&(con->focus_head)))
             old_focused = NULL;
 
-        /* 4: move the existing cons of this workspace below the new con */
+        /* 3: move the existing cons of this workspace below the new con */
         DLOG("Moving cons\n");
         Con *child;
         while (!TAILQ_EMPTY(&(con->nodes_head))) {
@@ -1034,7 +1068,69 @@ void con_set_layout(Con *con, int layout) {
         return;
     }
 
-    con->layout = layout;
+    if (layout == L_DEFAULT) {
+        /* Special case: the layout formerly known as "default" (in combination
+         * with an orientation). Since we switched to splith/splitv layouts,
+         * using the "default" layout (which "only" should happen when using
+         * legacy configs) is using the last split layout (either splith or
+         * splitv) in order to still do the same thing.
+         *
+         * Starting from v4.6 though, we will nag users about using "layout
+         * default", and in v4.9 we will remove it entirely (with an
+         * appropriate i3-migrate-config mechanism). */
+        con->layout = con->last_split_layout;
+        /* In case last_split_layout was not initialized… */
+        if (con->layout == L_DEFAULT)
+            con->layout = L_SPLITH;
+    } else {
+        /* We fill in last_split_layout when switching to a different layout
+         * since there are many places in the code that don’t use
+         * con_set_layout(). */
+        if (con->layout == L_SPLITH || con->layout == L_SPLITV)
+            con->last_split_layout = con->layout;
+        con->layout = layout;
+    }
+}
+
+/*
+ * This function toggles the layout of a given container. toggle_mode can be
+ * either 'default' (toggle only between stacked/tabbed/last_split_layout),
+ * 'split' (toggle only between splitv/splith) or 'all' (toggle between all
+ * layouts).
+ *
+ */
+void con_toggle_layout(Con *con, const char *toggle_mode) {
+    if (strcmp(toggle_mode, "split") == 0) {
+        /* Toggle between splits. When the current layout is not a split
+         * layout, we just switch back to last_split_layout. Otherwise, we
+         * change to the opposite split layout. */
+        if (con->layout != L_SPLITH && con->layout != L_SPLITV)
+            con_set_layout(con, con->last_split_layout);
+        else {
+            if (con->layout == L_SPLITH)
+                con_set_layout(con, L_SPLITV);
+            else con_set_layout(con, L_SPLITH);
+        }
+    } else {
+        if (con->layout == L_STACKED)
+            con_set_layout(con, L_TABBED);
+        else if (con->layout == L_TABBED) {
+            if (strcmp(toggle_mode, "all") == 0)
+                con_set_layout(con, L_SPLITH);
+            else con_set_layout(con, con->last_split_layout);
+        } else if (con->layout == L_SPLITH || con->layout == L_SPLITV) {
+            if (strcmp(toggle_mode, "all") == 0) {
+                /* When toggling through all modes, we toggle between
+                 * splith/splitv, whereas normally we just directly jump to
+                 * stacked. */
+                if (con->layout == L_SPLITH)
+                    con_set_layout(con, L_SPLITV);
+                else con_set_layout(con, L_STACKED);
+            } else {
+                con_set_layout(con, L_STACKED);
+            }
+        }
+    }
 }
 
 /*
@@ -1111,12 +1207,12 @@ Rect con_minimum_size(Con *con) {
     /* For horizontal/vertical split containers we sum up the width (h-split)
      * or height (v-split) and use the maximum of the height (h-split) or width
      * (v-split) as minimum size. */
-    if (con->orientation == HORIZ || con->orientation == VERT) {
+    if (con->split) {
         uint32_t width = 0, height = 0;
         Con *child;
         TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
             Rect min = con_minimum_size(child);
-            if (con->orientation == HORIZ) {
+            if (con->layout == L_SPLITH) {
                 width += min.width;
                 height = max(height, min.height);
             } else {
@@ -1128,7 +1224,67 @@ Rect con_minimum_size(Con *con) {
         return (Rect){ 0, 0, width, height };
     }
 
-    ELOG("Unhandled case, type = %d, layout = %d, orientation = %d\n",
-         con->type, con->layout, con->orientation);
+    ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
+         con->type, con->layout, con->split);
     assert(false);
 }
+
+/*
+ * Returns true if changing the focus to con would be allowed considering
+ * the fullscreen focus constraints. Specifically, if a fullscreen container or
+ * any of its descendants is focused, this function returns true if and only if
+ * focusing con would mean that focus would still be visible on screen, i.e.,
+ * the newly focused container would not be obscured by a fullscreen container.
+ *
+ * In the simplest case, if a fullscreen container or any of its descendants is
+ * fullscreen, this functions returns true if con is the fullscreen container
+ * itself or any of its descendants, as this means focus wouldn't escape the
+ * boundaries of the fullscreen container.
+ *
+ * In case the fullscreen container is of type CF_OUTPUT, this function returns
+ * true if con is on a different workspace, as focus wouldn't be obscured by
+ * the fullscreen container that is constrained to a different workspace.
+ *
+ * Note that this same logic can be applied to moving containers. If a
+ * container can be focused under the fullscreen focus constraints, it can also
+ * become a parent or sibling to the currently focused container.
+ *
+ */
+bool con_fullscreen_permits_focusing(Con *con) {
+    /* No focus, no problem. */
+    if (!focused)
+        return true;
+
+    /* Find the first fullscreen ascendent. */
+    Con *fs = focused;
+    while (fs && fs->fullscreen_mode == CF_NONE)
+        fs = fs->parent;
+
+    /* The most common case is we hit the workspace level. In this
+     * situation, changing focus is also harmless. */
+    assert(fs->fullscreen_mode != CF_NONE);
+    if (fs->type == CT_WORKSPACE)
+        return true;
+
+    /* Allow it if the container itself is the fullscreen container. */
+    if (con == fs)
+        return true;
+
+    /* If fullscreen is per-output, the focus being in a different workspace is
+     * sufficient to guarantee that change won't leave fullscreen in bad shape. */
+    if (fs->fullscreen_mode == CF_OUTPUT &&
+        con_get_workspace(con) != con_get_workspace(fs)) {
+            return true;
+    }
+
+    /* Allow it only if the container to be focused is contained within the
+     * current fullscreen container. */
+    do {
+        if (con->parent == fs)
+            return true;
+        con = con->parent;
+    } while (con);
+
+    /* Focusing con would hide it behind a fullscreen window, disallow it. */
+    return false;
+}
index b90eac3e3fde78eb1da2ace2796affea5eafd2c7..0bc5f6d65aac466814b08c62350c5c4d6fe18d3b 100644 (file)
@@ -40,7 +40,7 @@ void floating_enable(Con *con, bool automatic) {
     }
 
     /* 1: If the container is a workspace container, we need to create a new
-     * split-container with the same orientation and make that one floating. We
+     * split-container with the same layout and make that one floating. We
      * cannot touch the workspace container itself because floating containers
      * are children of the workspace. */
     if (con->type == CT_WORKSPACE) {
@@ -52,7 +52,7 @@ void floating_enable(Con *con, bool automatic) {
         /* TODO: refactor this with src/con.c:con_set_layout */
         Con *new = con_new(NULL, NULL);
         new->parent = con;
-        new->orientation = con->orientation;
+        new->layout = con->layout;
 
         /* since the new container will be set into floating mode directly
          * afterwards, we need to copy the workspace rect. */
@@ -97,8 +97,9 @@ void floating_enable(Con *con, bool automatic) {
      * otherwise. */
     Con *ws = con_get_workspace(con);
     nc->parent = ws;
-    nc->orientation = NO_ORIENTATION;
+    nc->split = true;
     nc->type = CT_FLOATING_CON;
+    nc->layout = L_SPLITH;
     /* We insert nc already, even though its rect is not yet calculated. This
      * is necessary because otherwise the workspace might be empty (and get
      * closed in tree_close()) even though it’s not. */
@@ -619,12 +620,12 @@ void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) {
     int32_t rel_y = (con->rect.y - old_rect->y);
     /* Then we calculate a fraction, for example 0.63 for a window
      * which is at y = 1212 of a 1920 px high output */
-    double fraction_x = ((double)rel_x / (int32_t)old_rect->width);
-    double fraction_y = ((double)rel_y / (int32_t)old_rect->height);
     DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n",
-         rel_x, rel_y, fraction_x, fraction_y, old_rect->width, old_rect->height);
-    con->rect.x = (int32_t)new_rect->x + (fraction_x * (int32_t)new_rect->width);
-    con->rect.y = (int32_t)new_rect->y + (fraction_y * (int32_t)new_rect->height);
+          rel_x, rel_y, (double)rel_x / old_rect->width, (double)rel_y / old_rect->height,
+          old_rect->width, old_rect->height);
+    /* Here we have to multiply at first. Or we will lose precision when not compiled with -msse2 */
+    con->rect.x = (int32_t)new_rect->x + (double)(rel_x * (int32_t)new_rect->width)  / (int32_t)old_rect->width;
+    con->rect.y = (int32_t)new_rect->y + (double)(rel_y * (int32_t)new_rect->height) / (int32_t)old_rect->height;
     DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y);
 }
 
index cb76eb788d99b328f67a194bf4cc34ad72cf2ee7..048d3a3a17992008326f6994bc57a99421ab7202 100644 (file)
@@ -77,58 +77,6 @@ bool event_is_ignored(const int sequence, const int response_type) {
     return false;
 }
 
-
-/*
- * There was a key press. We compare this key code with our bindings table and pass
- * the bound action to parse_command().
- *
- */
-static void handle_key_press(xcb_key_press_event_t *event) {
-
-    last_timestamp = event->time;
-
-    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;
-        }
-    }
-
-    char *command_copy = sstrdup(bind->command);
-    struct CommandResult *command_output = parse_command(command_copy);
-    free(command_copy);
-
-    if (command_output->needs_tree_render)
-        tree_render();
-
-    yajl_gen_free(command_output->json_gen);
-}
-
 /*
  * Called with coordinates of an enter_notify event or motion_notify event
  * to check if the user crossed virtual screen boundaries and adjust the
diff --git a/src/i3.mk b/src/i3.mk
new file mode 100644 (file)
index 0000000..6030bbc
--- /dev/null
+++ b/src/i3.mk
@@ -0,0 +1,63 @@
+ALL_TARGETS += i3
+INSTALL_TARGETS += install-i3
+CLEAN_TARGETS += clean-i3
+
+i3_SOURCES_GENERATED  = src/cfgparse.tab.c src/cfgparse.yy.c
+i3_SOURCES           := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
+i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
+i3_HEADERS           := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h))
+i3_CFLAGS             = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
+i3_LIBS               = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm
+
+i3_OBJECTS := $(i3_SOURCES_GENERATED:.c=.o) $(i3_SOURCES:.c=.o)
+
+src/%.o: src/%.c $(i3_HEADERS)
+       echo "[i3] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
+
+src/cfgparse.yy.c: src/cfgparse.l src/cfgparse.tab.o $(i3_HEADERS)
+       echo "[i3] LEX $<"
+       $(FLEX) -i -o $@ $<
+
+src/cfgparse.tab.c: src/cfgparse.y $(i3_HEADERS)
+       echo "[i3] YACC $<"
+       $(BISON) --debug --verbose -b $(basename $< .y) -d $<
+
+# This target compiles the command parser twice:
+# Once with -DTEST_PARSER, creating a stand-alone executable used for tests,
+# and once as an object file for i3.
+src/commands_parser.o: src/commands_parser.c $(i3_HEADERS) i3-command-parser.stamp
+       echo "[i3] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(I3_LDFLAGS) $(LDFLAGS) -DTEST_PARSER -o test.commands_parser $< $(i3_LIBS) $(LIBS)
+       $(CC) $(I3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
+
+i3-command-parser.stamp: generate-command-parser.pl parser-specs/commands.spec
+       echo "[i3] Generating command parser"
+       (cd include; ../generate-command-parser.pl)
+       touch $@
+
+i3: libi3.a $(i3_OBJECTS)
+       echo "[i3] Link i3"
+       $(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(i3_LIBS) $(LIBS)
+
+install-i3: i3
+       echo "[i3] Install"
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(SYSCONFDIR)/i3
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/include/i3
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/xsessions
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/applications
+       $(INSTALL) -m 0755 i3 $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -m 0755 i3-migrate-config-to-v4 $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -m 0755 i3-sensible-editor $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -m 0755 i3-sensible-pager $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -m 0755 i3-sensible-terminal $(DESTDIR)$(PREFIX)/bin/
+       test -e $(DESTDIR)$(SYSCONFDIR)/i3/config || $(INSTALL) -m 0644 i3.config $(DESTDIR)$(SYSCONFDIR)/i3/config
+       test -e $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes || $(INSTALL) -m 0644 i3.config.keycodes $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes
+       $(INSTALL) -m 0644 i3.xsession.desktop $(DESTDIR)$(PREFIX)/share/xsessions/i3.desktop
+       $(INSTALL) -m 0644 i3.applications.desktop $(DESTDIR)$(PREFIX)/share/applications/i3.desktop
+       $(INSTALL) -m 0644 include/i3/ipc.h $(DESTDIR)$(PREFIX)/include/i3/
+
+clean-i3:
+       echo "[i3] Clean"
+       rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) i3-command-parser.stamp i3 src/*.gcno src/cfgparse.{output,dot}
index 60ce814540d6c7a3c61ac3969b1a652770226c06..1d19fc6cd7e1134bc321040f5876015ad275bf3a 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -161,17 +161,14 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     ystr("type");
     y(integer, con->type);
 
+    /* provided for backwards compatibility only. */
     ystr("orientation");
-    switch (con->orientation) {
-        case NO_ORIENTATION:
-            ystr("none");
-            break;
-        case HORIZ:
+    if (!con->split)
+        ystr("none");
+    else {
+        if (con_orientation(con) == HORIZ)
             ystr("horizontal");
-            break;
-        case VERT:
-            ystr("vertical");
-            break;
+        else ystr("vertical");
     }
 
     ystr("scratchpad_state");
@@ -203,10 +200,20 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     ystr("focused");
     y(bool, (con == focused));
 
+    ystr("split");
+    y(bool, con->split);
+
     ystr("layout");
     switch (con->layout) {
         case L_DEFAULT:
-            ystr("default");
+            DLOG("About to dump layout=default, this is a bug in the code.\n");
+            assert(false);
+            break;
+        case L_SPLITV:
+            ystr("splitv");
+            break;
+        case L_SPLITH:
+            ystr("splith");
             break;
         case L_STACKED:
             ystr("stacked");
@@ -222,6 +229,16 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
             break;
     }
 
+    ystr("last_split_layout");
+    switch (con->layout) {
+        case L_SPLITV:
+            ystr("splitv");
+            break;
+        default:
+            ystr("splith");
+            break;
+    }
+
     ystr("border");
     switch (con->border_style) {
         case BS_NORMAL:
diff --git a/src/key_press.c b/src/key_press.c
new file mode 100644 (file)
index 0000000..9aaea8f
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * key_press.c: key press handler
+ *
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include "all.h"
+
+static int current_nesting_level;
+static bool success_key;
+static bool command_failed;
+
+/* XXX: I don’t want to touch too much of the nagbar code at once, but we
+ * should refactor this with src/cfgparse.y into a clean generic nagbar
+ * interface. It might come in handy in other situations within i3, too. */
+static char *pager_script_path;
+static pid_t nagbar_pid = -1;
+
+/*
+ * Handler which will be called when we get a SIGCHLD for the nagbar, meaning
+ * it exited (or could not be started, depending on the exit code).
+ *
+ */
+static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
+    ev_child_stop(EV_A_ watcher);
+
+    if (unlink(pager_script_path) != 0)
+        warn("Could not delete temporary i3-nagbar script %s", pager_script_path);
+
+    if (!WIFEXITED(watcher->rstatus)) {
+        fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
+        return;
+    }
+
+    int exitcode = WEXITSTATUS(watcher->rstatus);
+    printf("i3-nagbar process exited with status %d\n", exitcode);
+    if (exitcode == 2) {
+        fprintf(stderr, "ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
+    }
+
+    nagbar_pid = -1;
+}
+
+/* We need ev >= 4 for the following code. Since it is not *that* important (it
+ * only makes sure that there are no i3-nagbar instances left behind) we still
+ * support old systems with libev 3. */
+#if EV_VERSION_MAJOR >= 4
+/*
+ * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
+ * SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
+ *
+ */
+static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
+    if (nagbar_pid != -1) {
+        LOG("Sending SIGKILL (%d) to i3-nagbar with PID %d\n", SIGKILL, nagbar_pid);
+        kill(nagbar_pid, SIGKILL);
+    }
+}
+#endif
+
+/*
+ * Writes the given command as a shell script to path.
+ * Returns true unless something went wrong.
+ *
+ */
+static bool write_nagbar_script(const char *path, const char *command) {
+    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
+    if (fd == -1) {
+        warn("Could not create temporary script to store the nagbar command");
+        return false;
+    }
+    write(fd, "#!/bin/sh\n", strlen("#!/bin/sh\n"));
+    write(fd, command, strlen(command));
+    close(fd);
+    return true;
+}
+
+/*
+ * Starts an i3-nagbar process which alerts the user that his configuration
+ * file contains one or more errors. Also offers two buttons: One to launch an
+ * $EDITOR on the config file and another one to launch a $PAGER on the error
+ * logfile.
+ *
+ */
+static void start_commanderror_nagbar(void) {
+    if (nagbar_pid != -1) {
+        DLOG("i3-nagbar for command error already running, not starting again.\n");
+        return;
+    }
+
+    DLOG("Starting i3-nagbar due to command error\n");
+
+    /* We need to create a custom script containing our actual command
+     * since not every terminal emulator which is contained in
+     * i3-sensible-terminal supports -e with multiple arguments (and not
+     * all of them support -e with one quoted argument either).
+     *
+     * NB: The paths need to be unique, that is, don’t assume users close
+     * their nagbars at any point in time (and they still need to work).
+     * */
+    pager_script_path = get_process_filename("nagbar-cfgerror-pager");
+
+    nagbar_pid = fork();
+    if (nagbar_pid == -1) {
+        warn("Could not fork()");
+        return;
+    }
+
+    /* child */
+    if (nagbar_pid == 0) {
+        char *pager_command;
+        sasprintf(&pager_command, "i3-sensible-pager \"%s\"\n", errorfilename);
+        if (!write_nagbar_script(pager_script_path, pager_command))
+            return;
+
+        char *pageraction;
+        sasprintf(&pageraction, "i3-sensible-terminal -e \"%s\"", pager_script_path);
+        char *argv[] = {
+            NULL, /* will be replaced by the executable path */
+            "-t",
+            "error",
+            "-m",
+            "The configured command for this shortcut could not be run successfully.",
+            "-b",
+            "show errors",
+            pageraction,
+            NULL
+        };
+        exec_i3_utility("i3-nagbar", argv);
+    }
+
+    /* parent */
+    /* install a child watcher */
+    ev_child *child = smalloc(sizeof(ev_child));
+    ev_child_init(child, &nagbar_exited, nagbar_pid, 0);
+    ev_child_start(main_loop, child);
+
+/* We need ev >= 4 for the following code. Since it is not *that* important (it
+ * only makes sure that there are no i3-nagbar instances left behind) we still
+ * support old systems with libev 3. */
+#if EV_VERSION_MAJOR >= 4
+    /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
+     * still running) */
+    ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
+    ev_cleanup_init(cleanup, nagbar_cleanup);
+    ev_cleanup_start(main_loop, cleanup);
+#endif
+}
+
+/*
+ * Kills the commanderror i3-nagbar process, if any.
+ *
+ * Called when reloading/restarting, since the user probably fixed his wrong
+ * keybindings.
+ *
+ * If wait_for_it is set (restarting), this function will waitpid(), otherwise,
+ * ev is assumed to handle it (reloading).
+ *
+ */
+void kill_commanderror_nagbar(bool wait_for_it) {
+    if (nagbar_pid == -1)
+        return;
+
+    if (kill(nagbar_pid, SIGTERM) == -1)
+        warn("kill(configerror_nagbar) failed");
+
+    if (!wait_for_it)
+        return;
+
+    /* When restarting, we don’t enter the ev main loop anymore and after the
+     * exec(), our old pid is no longer watched. So, ev won’t handle SIGCHLD
+     * for us and we would end up with a <defunct> process. Therefore we
+     * waitpid() here. */
+    waitpid(nagbar_pid, NULL, 0);
+}
+
+static int json_boolean(void *ctx, int boolval) {
+    DLOG("Got bool: %d, success_key %d, nesting_level %d\n", boolval, success_key, current_nesting_level);
+
+    if (success_key && current_nesting_level == 1 && !boolval)
+        command_failed = true;
+
+    return 1;
+}
+
+#if YAJL_MAJOR >= 2
+static int json_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
+#else
+static int json_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
+#endif
+    success_key = (stringlen >= strlen("success") &&
+                   strncmp((const char*)stringval, "success", strlen("success")) == 0);
+    return 1;
+}
+
+static int json_start_map(void *ctx) {
+    current_nesting_level++;
+    return 1;
+}
+
+static int json_end_map(void *ctx) {
+    current_nesting_level--;
+    return 1;
+}
+
+static yajl_callbacks command_error_callbacks = {
+    NULL,
+    &json_boolean,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    &json_start_map,
+    &json_map_key,
+    &json_end_map,
+    NULL,
+    NULL
+};
+
+/*
+ * There was a key press. We compare this key code with our bindings table and pass
+ * the bound action to parse_command().
+ *
+ */
+void handle_key_press(xcb_key_press_event_t *event) {
+
+    last_timestamp = event->time;
+
+    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;
+        }
+    }
+
+    char *command_copy = sstrdup(bind->command);
+    struct CommandResult *command_output = parse_command(command_copy);
+    free(command_copy);
+
+    if (command_output->needs_tree_render)
+        tree_render();
+
+    /* We parse the JSON reply to figure out whether there was an error
+     * ("success" being false in on of the returned dictionaries). */
+    const unsigned char *reply;
+#if YAJL_MAJOR >= 2
+    size_t length;
+    yajl_handle handle = yajl_alloc(&command_error_callbacks, NULL, NULL);
+#else
+    unsigned int length;
+    yajl_parser_config parse_conf = { 0, 0 };
+
+    yajl_handle handle = yajl_alloc(&command_error_callbacks, &parse_conf, NULL, NULL);
+#endif
+    yajl_gen_get_buf(command_output->json_gen, &reply, &length);
+
+    current_nesting_level = 0;
+    success_key = false;
+    command_failed = false;
+    yajl_status state = yajl_parse(handle, reply, length);
+    if (state != yajl_status_ok) {
+        ELOG("Could not parse my own reply. That's weird. reply is %.*s\n", length, reply);
+    } else {
+        if (command_failed)
+            start_commanderror_nagbar();
+    }
+
+    yajl_free(handle);
+
+    yajl_gen_free(command_output->json_gen);
+}
index a8063dcada1d7cc6b875cb65d4690a57d13a0dbb..54735d915b456acaf117744ac9e2001d15dc583f 100644 (file)
@@ -156,15 +156,25 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) {
             memcpy(json_node->sticky_group, val, len);
             LOG("sticky_group of this container is %s\n", json_node->sticky_group);
         } else if (strcasecmp(last_key, "orientation") == 0) {
+            /* Upgrade path from older versions of i3 (doing an inplace restart
+             * to a newer version):
+             * "orientation" is dumped before "layout". Therefore, we store
+             * whether the orientation was horizontal or vertical in the
+             * last_split_layout. When we then encounter layout == "default",
+             * we will use the last_split_layout as layout instead. */
             char *buf = NULL;
             sasprintf(&buf, "%.*s", (int)len, val);
-            if (strcasecmp(buf, "none") == 0)
-                json_node->orientation = NO_ORIENTATION;
-            else if (strcasecmp(buf, "horizontal") == 0)
-                json_node->orientation = HORIZ;
+            if (strcasecmp(buf, "none") == 0 ||
+                strcasecmp(buf, "horizontal") == 0)
+                json_node->last_split_layout = L_SPLITH;
             else if (strcasecmp(buf, "vertical") == 0)
-                json_node->orientation = VERT;
+                json_node->last_split_layout = L_SPLITV;
             else LOG("Unhandled orientation: %s\n", buf);
+
+            /* What used to be an implicit check whether orientation !=
+             * NO_ORIENTATION is now a proper separate flag. */
+            if (strcasecmp(buf, "none") != 0)
+                json_node->split = true;
             free(buf);
         } else if (strcasecmp(last_key, "border") == 0) {
             char *buf = NULL;
@@ -181,17 +191,33 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) {
             char *buf = NULL;
             sasprintf(&buf, "%.*s", (int)len, val);
             if (strcasecmp(buf, "default") == 0)
-                json_node->layout = L_DEFAULT;
+                /* This set above when we read "orientation". */
+                json_node->layout = json_node->last_split_layout;
             else if (strcasecmp(buf, "stacked") == 0)
                 json_node->layout = L_STACKED;
             else if (strcasecmp(buf, "tabbed") == 0)
                 json_node->layout = L_TABBED;
-            else if (strcasecmp(buf, "dockarea") == 0)
+            else if (strcasecmp(buf, "dockarea") == 0) {
                 json_node->layout = L_DOCKAREA;
-            else if (strcasecmp(buf, "output") == 0)
+                /* Necessary for migrating from older versions of i3. */
+                json_node->split = false;
+            } else if (strcasecmp(buf, "output") == 0)
                 json_node->layout = L_OUTPUT;
+            else if (strcasecmp(buf, "splith") == 0)
+                json_node->layout = L_SPLITH;
+            else if (strcasecmp(buf, "splitv") == 0)
+                json_node->layout = L_SPLITV;
             else LOG("Unhandled \"layout\": %s\n", buf);
             free(buf);
+        } else if (strcasecmp(last_key, "last_split_layout") == 0) {
+            char *buf = NULL;
+            sasprintf(&buf, "%.*s", (int)len, val);
+            if (strcasecmp(buf, "splith") == 0)
+                json_node->last_split_layout = L_SPLITH;
+            else if (strcasecmp(buf, "splitv") == 0)
+                json_node->last_split_layout = L_SPLITV;
+            else LOG("Unhandled \"last_splitlayout\": %s\n", buf);
+            free(buf);
         } else if (strcasecmp(last_key, "mark") == 0) {
             char *buf = NULL;
             sasprintf(&buf, "%.*s", (int)len, val);
@@ -288,6 +314,9 @@ static int json_bool(void *ctx, int val) {
         to_focus = json_node;
     }
 
+    if (strcasecmp(last_key, "split") == 0)
+        json_node->split = val;
+
     if (parsing_swallows) {
         if (strcasecmp(last_key, "restart_mode") == 0)
             current_swallow->restart_mode = val;
index 92e8f57c758f75c4ca2a98b31a5ab42a35ebf8cc..d6f277f2f2bc3a54f2150e79807e4ab23c66f627 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -4,7 +4,7 @@
  * i3 - an improved dynamic tiling window manager
  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
  *
- * log.c: Setting of loglevels, logging functions.
+ * log.c: Logging functions.
  *
  */
 #include <stdarg.h>
 #include "libi3.h"
 #include "shmlog.h"
 
-/* loglevels.h is autogenerated at make time */
-#include "loglevels.h"
-
-static uint64_t loglevel = 0;
+static bool debug_logging = false;
 static bool verbose = false;
 static FILE *errorfile;
 char *errorfilename;
@@ -146,26 +143,11 @@ void set_verbosity(bool _verbose) {
 }
 
 /*
- * Enables the given loglevel.
+ * Set debug logging.
  *
  */
-void add_loglevel(const char *level) {
-    /* Handle the special loglevel "all" */
-    if (strcasecmp(level, "all") == 0) {
-        loglevel = UINT64_MAX;
-        return;
-    }
-
-    for (int i = 0; i < sizeof(loglevels) / sizeof(char*); i++) {
-        if (strcasecmp(loglevels[i], level) != 0)
-            continue;
-
-        /* The position in the array (plus one) is the amount of times
-         * which we need to shift 1 to the left to get our bitmask for
-         * the specific loglevel. */
-        loglevel |= (1 << (i+1));
-        break;
-    }
+void set_debug_logging(const bool _debug_logging) {
+    debug_logging = _debug_logging;
 }
 
 /*
@@ -271,17 +253,17 @@ void errorlog(char *fmt, ...) {
 
 /*
  * Logs the given message to stdout while prefixing the current time to it,
- * but only if the corresponding debug loglevel was activated.
+ * but only if debug logging was activated.
  * This is to be called by DLOG() which includes filename/linenumber
  *
  */
-void debuglog(uint64_t lev, char *fmt, ...) {
+void debuglog(char *fmt, ...) {
     va_list args;
 
-    if (!logbuffer && !(loglevel & lev))
+    if (!logbuffer && !(debug_logging))
         return;
 
     va_start(args, fmt);
-    vlog((loglevel & lev), fmt, args);
+    vlog(debug_logging, fmt, args);
     va_end(args);
 }
index e332f5b45d46e070d7f677dcd773413098b0046e..b8e2e08c05dbac84bb0b1800f8a6b38dc9fcab9a 100644 (file)
@@ -318,8 +318,8 @@ int main(int argc, char *argv[]) {
                 set_verbosity(true);
                 break;
             case 'd':
-                LOG("Enabling debug loglevel %s\n", optarg);
-                add_loglevel(optarg);
+                LOG("Enabling debug logging\n");
+                set_debug_logging(true);
                 break;
             case 'l':
                 /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */
@@ -367,12 +367,12 @@ int main(int argc, char *argv[]) {
                 }
                 /* fall-through */
             default:
-                fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]);
+                fprintf(stderr, "Usage: %s [-c configfile] [-d all] [-a] [-v] [-V] [-C]\n", argv[0]);
                 fprintf(stderr, "\n");
                 fprintf(stderr, "\t-a          disable autostart ('exec' lines in config)\n");
                 fprintf(stderr, "\t-c <file>   use the provided configfile instead\n");
                 fprintf(stderr, "\t-C          validate configuration file and exit\n");
-                fprintf(stderr, "\t-d <level>  enable debug output with the specified loglevel\n");
+                fprintf(stderr, "\t-d all      enable debug output\n");
                 fprintf(stderr, "\t-L <file>   path to the serialized layout during restarts\n");
                 fprintf(stderr, "\t-v          display version and exit\n");
                 fprintf(stderr, "\t-V          enable verbose mode\n");
index d3065c2490bf1faae92e6882231ece95b1a0fe16..d110312aa57ab9b916b9cfe3073b96943b73a6ea 100644 (file)
@@ -169,6 +169,12 @@ void tree_move(int direction) {
     while (above->parent != same_orientation)
         above = above->parent;
 
+    /* Enforce the fullscreen focus restrictions. */
+    if (!con_fullscreen_permits_focusing(above->parent)) {
+        LOG("Cannot move out of fullscreen container\n");
+        return;
+    }
+
     DLOG("above = %p\n", above);
     Con *next;
     position_t position;
index d29ce12882fdbcfa312e89246fd203e8a9386a18..b691a3326a42640b2b0082399f8efd19310a7d01 100644 (file)
@@ -256,7 +256,6 @@ void output_init_con(Output *output) {
     Con *topdock = con_new(NULL, NULL);
     topdock->type = CT_DOCKAREA;
     topdock->layout = L_DOCKAREA;
-    topdock->orientation = VERT;
     /* this container swallows dock clients */
     Match *match = scalloc(sizeof(Match));
     match_init(match);
@@ -278,6 +277,7 @@ void output_init_con(Output *output) {
     DLOG("adding main content container\n");
     Con *content = con_new(NULL, NULL);
     content->type = CT_CON;
+    content->layout = L_SPLITH;
     FREE(content->name);
     content->name = sstrdup("content");
 
@@ -290,7 +290,6 @@ void output_init_con(Output *output) {
     Con *bottomdock = con_new(NULL, NULL);
     bottomdock->type = CT_DOCKAREA;
     bottomdock->layout = L_DOCKAREA;
-    bottomdock->orientation = VERT;
     /* this container swallows dock clients */
     match = scalloc(sizeof(Match));
     match_init(match);
@@ -460,11 +459,11 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) {
             if (con_num_children(workspace) > 1)
                 continue;
 
-            workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
-            DLOG("Setting workspace [%d,%s]'s orientation to %d.\n", workspace->num, workspace->name, workspace->orientation);
+            workspace->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH;
+            DLOG("Setting workspace [%d,%s]'s layout to %d.\n", workspace->num, workspace->name, workspace->layout);
             if ((child = TAILQ_FIRST(&(workspace->nodes_head)))) {
-                child->orientation = workspace->orientation;
-                DLOG("Setting child [%d,%s]'s orientation to %d.\n", child->num, child->name, child->orientation);
+                child->layout = workspace->layout;
+                DLOG("Setting child [%d,%s]'s layout to %d.\n", child->num, child->name, child->layout);
             }
         }
     }
index a0b51f66d2be15cfc4670d3e5011e42a902ad4d7..e5698d021f04ad98092fe0ffaeabbc9b5cf59b3c 100644 (file)
@@ -67,7 +67,7 @@ void regex_free(struct regex *regex) {
 /*
  * Checks if the given regular expression matches the given input and returns
  * true if it does. In either case, it logs the outcome using LOG(), so it will
- * be visible without any debug loglevel.
+ * be visible without debug logging.
  *
  */
 bool regex_matches(struct regex *regex, const char *input) {
index 860219df5f92fbfb76f5901308566b74280d3cfc..bad67f04c090b07d516537a7d22306133fce3e43 100644 (file)
@@ -106,9 +106,9 @@ static void render_l_output(Con *con) {
  */
 void render_con(Con *con, bool render_fullscreen) {
     int children = con_num_children(con);
-    DLOG("Rendering %snode %p / %s / layout %d / children %d / orient %d\n",
+    DLOG("Rendering %snode %p / %s / layout %d / children %d\n",
          (render_fullscreen ? "fullscreen " : ""), con, con->name, con->layout,
-         children, con->orientation);
+         children);
 
     /* Copy container rect, subtract container border */
     /* This is the actually usable space inside this container for clients */
@@ -208,11 +208,11 @@ void render_con(Con *con, bool render_fullscreen) {
 
     /* precalculate the sizes to be able to correct rounding errors */
     int sizes[children];
-    if (con->layout == L_DEFAULT && children > 0) {
+    if ((con->layout == L_SPLITH || con->layout == L_SPLITV) && children > 0) {
         assert(!TAILQ_EMPTY(&con->nodes_head));
         Con *child;
         int i = 0, assigned = 0;
-        int total = con->orientation == HORIZ ? rect.width : rect.height;
+        int total = con_orientation(con) == HORIZ ? rect.width : rect.height;
         TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
             double percentage = child->percent > 0.0 ? child->percent : 1.0 / children;
             assigned += sizes[i++] = percentage * total;
@@ -289,8 +289,8 @@ void render_con(Con *con, bool render_fullscreen) {
         assert(children > 0);
 
         /* default layout */
-        if (con->layout == L_DEFAULT) {
-            if (con->orientation == HORIZ) {
+        if (con->layout == L_SPLITH || con->layout == L_SPLITV) {
+            if (con->layout == L_SPLITH) {
                 child->rect.x = x;
                 child->rect.y = y;
                 child->rect.width = sizes[i];
index f29369c607eba2d0e64b64d630a0d6ffe78753c2..068b05704e8981f6f932e0c1932b244686abae2c 100644 (file)
@@ -39,6 +39,7 @@ static Con *_create___i3(void) {
     content->type = CT_CON;
     FREE(content->name);
     content->name = sstrdup("content");
+    content->layout = L_SPLITH;
 
     x_set_name(content, "[i3 con] content __i3");
     con_attach(content, __i3, false);
@@ -48,6 +49,7 @@ static Con *_create___i3(void) {
     ws->type = CT_WORKSPACE;
     ws->num = -1;
     ws->name = sstrdup("__i3_scratch");
+    ws->layout = L_SPLITH;
     con_attach(ws, content, false);
     x_set_name(ws, "[i3 con] workspace __i3_scratch");
     ws->fullscreen_mode = CF_OUTPUT;
@@ -112,6 +114,7 @@ void tree_init(xcb_get_geometry_reply_t *geometry) {
     FREE(croot->name);
     croot->name = "root";
     croot->type = CT_ROOT;
+    croot->layout = L_SPLITH;
     croot->rect = (Rect){
         geometry->x,
         geometry->y,
@@ -151,6 +154,7 @@ Con *tree_open_con(Con *con, i3Window *window) {
 
     /* 3. create the container and attach it to its parent */
     Con *new = con_new(con, window);
+    new->layout = L_SPLITH;
 
     /* 4: re-calculate child->percent for each child */
     con_fix_percent(con);
@@ -296,7 +300,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
             }
         }
         else {
-            DLOG("not focusing because we're not killing anybody");
+            DLOG("not focusing because we're not killing anybody\n");
         }
     } else {
         DLOG("not focusing, was not mapped\n");
@@ -337,7 +341,7 @@ void tree_split(Con *con, orientation_t orientation) {
     /* for a workspace, we just need to change orientation */
     if (con->type == CT_WORKSPACE) {
         DLOG("Workspace, simply changing orientation to %d\n", orientation);
-        con->orientation = orientation;
+        con->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
         return;
     }
 
@@ -351,8 +355,9 @@ void tree_split(Con *con, orientation_t orientation) {
      * child (its split functionality is unused so far), we just change the
      * orientation (more intuitive than splitting again) */
     if (con_num_children(parent) == 1 &&
-        parent->layout == L_DEFAULT) {
-        parent->orientation = orientation;
+        (parent->layout == L_SPLITH ||
+         parent->layout == L_SPLITV)) {
+        parent->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
         DLOG("Just changing orientation of existing container\n");
         return;
     }
@@ -364,7 +369,8 @@ void tree_split(Con *con, orientation_t orientation) {
     TAILQ_REPLACE(&(parent->nodes_head), con, new, nodes);
     TAILQ_REPLACE(&(parent->focus_head), con, new, focused);
     new->parent = parent;
-    new->orientation = orientation;
+    new->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
+    new->split = true;
 
     /* 3: swap 'percent' (resize factor) */
     new->percent = con->percent;
@@ -375,38 +381,34 @@ void tree_split(Con *con, orientation_t orientation) {
 }
 
 /*
- * Moves focus one level up.
+ * Moves focus one level up. Returns true if focus changed.
  *
  */
-void level_up(void) {
-    /* We cannot go up when we are in fullscreen mode at the moment, that would
-     * be totally not intuitive */
-    if (focused->fullscreen_mode != CF_NONE) {
-        LOG("Currently in fullscreen, not going up\n");
-        return;
-    }
+bool level_up(void) {
     /* We can focus up to the workspace, but not any higher in the tree */
     if ((focused->parent->type != CT_CON &&
         focused->parent->type != CT_WORKSPACE) ||
         focused->type == CT_WORKSPACE) {
         LOG("Cannot go up any further\n");
-        return;
+        return false;
     }
     con_focus(focused->parent);
+    return true;
 }
 
 /*
- * Moves focus one level down.
+ * Moves focus one level down. Returns true if focus changed.
  *
  */
-void level_down(void) {
+bool level_down(void) {
     /* 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;
+        return false;
     }
     con_focus(next);
+    return true;
 }
 
 static void mark_unmapped(Con *con) {
@@ -560,6 +562,10 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
         else next = TAILQ_LAST(&(parent->nodes_head), nodes_head);
     }
 
+    /* Don't violate fullscreen focus restrictions. */
+    if (!con_fullscreen_permits_focusing(next))
+        return false;
+
     /* 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 */
@@ -594,7 +600,9 @@ void tree_flatten(Con *con) {
     DLOG("Checking if I can flatten con = %p / %s\n", con, con->name);
 
     /* We only consider normal containers without windows */
-    if (con->type != CT_CON || con->window != NULL)
+    if (con->type != CT_CON ||
+        parent->layout == L_OUTPUT || /* con == "content" */
+        con->window != NULL)
         goto recurse;
 
     /* Ensure it got only one child */
@@ -602,12 +610,14 @@ void tree_flatten(Con *con) {
     if (child == NULL || TAILQ_NEXT(child, nodes) != NULL)
         goto recurse;
 
+    DLOG("child = %p, con = %p, parent = %p\n", child, con, parent);
+
     /* The child must have a different orientation than the con but the same as
      * the con’s parent to be redundant */
-    if (con->orientation == NO_ORIENTATION ||
-        child->orientation == NO_ORIENTATION ||
-        con->orientation == child->orientation ||
-        child->orientation != parent->orientation)
+    if (con->split ||
+        child->split ||
+        con_orientation(con) == con_orientation(child) ||
+        con_orientation(child) != con_orientation(parent))
         goto recurse;
 
     DLOG("Alright, I have to flatten this situation now. Stay calm.\n");
index d337963eaa093b2c1bd8f7d29684640f4b099bb8..76a74db1bd7de3277d2ebccb35cecaa964a5a904 100644 (file)
@@ -303,6 +303,7 @@ void i3_restart(bool forget_layout) {
     char *restart_filename = forget_layout ? NULL : store_restart_layout();
 
     kill_configerror_nagbar(true);
+    kill_commanderror_nagbar(true);
 
     restore_geometry();
 
index 928f0bd65e88b06e10a012b77a5fd178c344be87..6394084a6c4828cff31606ee1175f6fb93800ce8 100644 (file)
  * back-and-forth switching. */
 static char *previous_workspace_name = NULL;
 
+/*
+ * Sets ws->layout to splith/splitv if default_orientation was specified in the
+ * configfile. Otherwise, it uses splith/splitv depending on whether the output
+ * is higher than wide.
+ *
+ */
+static void _workspace_apply_default_orientation(Con *ws) {
+    /* If default_orientation is set to NO_ORIENTATION we determine
+     * orientation depending on output resolution. */
+    if (config.default_orientation == NO_ORIENTATION) {
+        Con *output = con_get_output(ws);
+        ws->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH;
+        DLOG("Auto orientation. Workspace size set to (%d,%d), setting layout to %d.\n",
+             output->rect.width, output->rect.height, ws->layout);
+    } else {
+        ws->layout = (config.default_orientation == HORIZ) ? L_SPLITH : L_SPLITV;
+    }
+}
+
 /*
  * Returns a pointer to the workspace with the given number (starting at 0),
  * creating the workspace if necessary (by allocating the necessary amount of
@@ -64,16 +83,8 @@ Con *workspace_get(const char *num, bool *created) {
         else workspace->num = parsed_num;
         LOG("num = %d\n", workspace->num);
 
-        /* If default_orientation is set to NO_ORIENTATION we
-         * determine workspace orientation from workspace size.
-         * Otherwise we just set the orientation to default_orientation. */
-        if (config.default_orientation == NO_ORIENTATION) {
-            workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
-            DLOG("Auto orientation. Output resolution set to (%d,%d), setting orientation to %d.\n",
-                 workspace->rect.width, workspace->rect.height, workspace->orientation);
-        } else {
-            workspace->orientation = config.default_orientation;
-        }
+        workspace->parent = content;
+        _workspace_apply_default_orientation(workspace);
 
         con_attach(workspace, content, false);
 
@@ -114,14 +125,15 @@ Con *create_workspace_on_output(Output *output, Con *content) {
         /* We check if this is the workspace
          * next/prev/next_on_output/prev_on_output/back_and_forth/number command.
          * Beware: The workspace names "next", "prev", "next_on_output",
-         * "prev_on_output", "number" and "back_and_forth" are OK, so we check
-         * before stripping the double quotes */
+         * "prev_on_output", "number", "back_and_forth" and "current" are OK,
+         * so we check before stripping the double quotes */
         if (strncasecmp(target, "next", strlen("next")) == 0 ||
             strncasecmp(target, "prev", strlen("prev")) == 0 ||
             strncasecmp(target, "next_on_output", strlen("next_on_output")) == 0 ||
             strncasecmp(target, "prev_on_output", strlen("prev_on_output")) == 0 ||
             strncasecmp(target, "number", strlen("number")) == 0 ||
-            strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0)
+            strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 ||
+            strncasecmp(target, "current", strlen("current")) == 0)
             continue;
         if (*target == '"')
             target++;
@@ -197,19 +209,12 @@ Con *create_workspace_on_output(Output *output, Con *content) {
 
     ws->fullscreen_mode = CF_OUTPUT;
 
-    /* If default_orientation is set to NO_ORIENTATION we determine
-     * orientation depending on output resolution. */
-    if (config.default_orientation == NO_ORIENTATION) {
-        ws->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
-        DLOG("Auto orientation. Workspace size set to (%d,%d), setting orientation to %d.\n",
-             output->rect.width, output->rect.height, ws->orientation);
-    } else {
-        ws->orientation = config.default_orientation;
-    }
+    _workspace_apply_default_orientation(ws);
 
     return ws;
 }
 
+
 /*
  * Returns true if the workspace is currently visible. Especially important for
  * multi-monitor environments, as they can have multiple currenlty active
@@ -685,8 +690,7 @@ void workspace_update_urgent_flag(Con *ws) {
 
 /*
  * 'Forces' workspace orientation by moving all cons into a new split-con with
- * the same orientation as the workspace and then changing the workspace
- * orientation.
+ * the same layout as the workspace and then changing the workspace layout.
  *
  */
 void ws_force_orientation(Con *ws, orientation_t orientation) {
@@ -694,9 +698,8 @@ void ws_force_orientation(Con *ws, orientation_t orientation) {
     Con *split = con_new(NULL, NULL);
     split->parent = ws;
 
-    /* 2: copy layout and orientation from workspace */
+    /* 2: copy layout from workspace */
     split->layout = ws->layout;
-    split->orientation = ws->orientation;
 
     Con *old_focused = TAILQ_FIRST(&(ws->focus_head));
 
@@ -708,8 +711,8 @@ void ws_force_orientation(Con *ws, orientation_t orientation) {
         con_attach(child, split, true);
     }
 
-    /* 4: switch workspace orientation */
-    ws->orientation = orientation;
+    /* 4: switch workspace layout */
+    ws->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
 
     /* 5: attach the new split container to the workspace */
     DLOG("Attaching new split to ws\n");
@@ -744,19 +747,11 @@ Con *workspace_attach_to(Con *ws) {
     /* 1: create a new split container */
     Con *new = con_new(NULL, NULL);
     new->parent = ws;
+    new->split = true;
 
     /* 2: set the requested layout on the split con */
     new->layout = config.default_layout;
 
-    /* 3: While the layout is irrelevant in stacked/tabbed mode, it needs
-     * to be set. Otherwise, this con will not be interpreted as a split
-     * container. */
-    if (config.default_orientation == NO_ORIENTATION) {
-        new->orientation = (ws->rect.height > ws->rect.width) ? VERT : HORIZ;
-    } else {
-        new->orientation = config.default_orientation;
-    }
-
     /* 4: attach the new split container to the workspace */
     DLOG("Attaching new split %p to workspace %p\n", new, ws);
     con_attach(new, ws, false);
index 020e2f90b737ab3499be140f58af4c6fb6c95c44..f55206034038ed968ee03b8c0085360dfc5756a6 100755 (executable)
@@ -46,6 +46,7 @@ my @displays = ();
 my %options = (
     valgrind => 0,
     strace => 0,
+    xtrace => 0,
     coverage => 0,
     restart => 0,
 );
@@ -54,6 +55,7 @@ my $result = GetOptions(
     "coverage-testing" => \$options{coverage},
     "valgrind" => \$options{valgrind},
     "strace" => \$options{strace},
+    "xtrace" => \$options{xtrace},
     "display=s" => \@displays,
     "parallel=i" => \$parallel,
     "help|?" => \$help,
@@ -342,6 +344,11 @@ C<latest/valgrind-for-$test.log>.
 Runs i3 under strace to trace system calls. The output will be available in
 C<latest/strace-for-$test.log>.
 
+=item B<--xtrace>
+
+Runs i3 under xtrace to trace X11 requests/replies. The output will be
+available in C<latest/xtrace-for-$test.log>.
+
 =item B<--coverage-testing>
 
 Exits i3 cleanly (instead of kill -9) to make coverage testing work properly.
index 8f52bddc4a3eb8a92a3ba37ae8363a29cfd9b779..0a062be48199f0e705f08b536749dbf256ef42a8 100644 (file)
@@ -124,6 +124,14 @@ sub activate_i3 {
                      'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"';
         }
 
+        if ($args{xtrace}) {
+            my $out = "$outdir/xtrace-for-$test.log";
+
+            # See comment in $args{strace} branch.
+            $cmd = qq|xtrace -n -o "$out" -- | .
+                     'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"';
+        }
+
         # We need to use the shell due to using output redirections.
         exec '/bin/sh', '-c', $cmd;
 
index 66f22bc03a7bee785012f901399898e68c72b001..a224b718a90f245c954bba1cfa20a05e94d0a479 100644 (file)
@@ -105,12 +105,13 @@ sub worker_wait {
             $test->failure_output(\*STDERR);
             $test->todo_output(\*STDOUT);
 
-            @ENV{qw(DISPLAY TESTNAME OUTDIR VALGRIND STRACE COVERAGE RESTART)}
+            @ENV{qw(DISPLAY TESTNAME OUTDIR VALGRIND STRACE XTRACE COVERAGE RESTART)}
                 = ($self->{display},
                    basename($file),
                    $outdir,
                    $options->{valgrind},
                    $options->{strace},
+                   $options->{xtrace},
                    $options->{coverage},
                    $options->{restart});
 
index 4c41a7f2f27d4ccbc210f913417262dcabdd7992..aff23009986ff19730c42a252d0ce6e999aac3de 100644 (file)
@@ -33,6 +33,7 @@ our @EXPORT = qw(
     open_floating_window
     get_dock_clients
     cmd
+    cmp_float
     sync_with_i3
     does_i3_live
     exit_gracefully
@@ -546,6 +547,7 @@ sub launch_with_config {
         testname => $ENV{TESTNAME},
         valgrind => $ENV{VALGRIND},
         strace => $ENV{STRACE},
+        xtrace => $ENV{XTRACE},
         restart => $ENV{RESTART},
         cv => $cv,
         dont_create_temp_dir => $args{dont_create_temp_dir},
@@ -563,6 +565,14 @@ sub launch_with_config {
     return $i3_pid;
 }
 
+# compares two floats and return true if they differ less
+# then 1e-6
+sub cmp_float {
+  my ($a, $b) = @_;
+
+  return abs($a - $b) < 1e-6;
+}
+
 package i3test::X11;
 use parent 'X11::XCB::Connection';
 
index 3a495e2788d5ee6842c745197fc96977511cb27e..18e210196a3127907e13e16f877159717f08e418 100644 (file)
@@ -39,14 +39,16 @@ my $expected = {
     name => 'root',
     orientation => $ignore,
     type => 0,
+    split => JSON::XS::false,
     id => $ignore,
     rect => $ignore,
     window_rect => $ignore,
     geometry => $ignore,
     swallows => $ignore,
     percent => undef,
-    layout => 'default',
+    layout => 'splith',
     floating => 'auto_off',
+    last_split_layout => 'splith',
     scratchpad_state => 'none',
     focus => $ignore,
     focused => JSON::XS::false,
index f672e9d632ebc4391faca496758ca10bb9a9523d..d491c37a47451057132eca9acf6ab47e773fd5d4 100644 (file)
@@ -19,10 +19,10 @@ sub verify_split_layout {
     $tmp = fresh_workspace;
 
     $ws = get_ws($tmp);
-    is($ws->{orientation}, 'horizontal', 'orientation horizontal by default');
+    is($ws->{layout}, 'splith', 'orientation horizontal by default');
     cmd 'split v';
     $ws = get_ws($tmp);
-    is($ws->{orientation}, 'vertical', 'split v changes workspace orientation');
+    is($ws->{layout}, 'splitv', 'split v changes workspace orientation');
 
     cmd 'open';
     cmd 'open';
@@ -47,7 +47,7 @@ sub verify_split_layout {
 
     is(@{$first->{nodes}}, 0, 'first container has no children');
     isnt($second->{name}, $old_name, 'second container was replaced');
-    is($second->{orientation}, 'horizontal', 'orientation is horizontal');
+    is($second->{layout}, 'splith', 'orientation is horizontal');
     is(@{$second->{nodes}}, 2, 'second container has 2 children');
     is($second->{nodes}->[0]->{name}, $old_name, 'found old second container');
 }
@@ -66,10 +66,10 @@ verify_split_layout(split_command => 'split horizontal');
 $tmp = fresh_workspace;
 
 $ws = get_ws($tmp);
-is($ws->{orientation}, 'horizontal', 'orientation horizontal by default');
+is($ws->{layout}, 'splith', 'orientation horizontal by default');
 cmd 'split v';
 $ws = get_ws($tmp);
-is($ws->{orientation}, 'vertical', 'split v changes workspace orientation');
+is($ws->{layout}, 'splitv', 'split v changes workspace orientation');
 
 cmd 'open';
 my @content = @{get_ws_content($tmp)};
index 3f00428c035739cabf9f6f21a04b68cb226bb592..79753cd7133241a7375b831e075a12ef373d212b 100644 (file)
@@ -99,6 +99,26 @@ cmd 'move workspace prev';
 ok(@{get_ws_content($tmp)} == 3, 'three containers on first ws');
 ok(@{get_ws_content($tmp2)} == 0, 'no containers on second ws');
 
+###################################################################
+# check if 'move workspace current' works
+###################################################################
+
+$tmp = get_unused_workspace();
+$tmp2 = get_unused_workspace();
+
+cmd "workspace $tmp";
+$first = open_window(name => 'win-name');
+ok(@{get_ws_content($tmp)} == 1, 'one container on first ws');
+
+cmd "workspace $tmp2";
+ok(@{get_ws_content($tmp2)} == 0, 'no containers yet');
+
+cmd qq|[title="win-name"] move workspace $tmp2|;
+ok(@{get_ws_content($tmp2)} == 1, 'one container on second ws');
+
+cmd qq|[title="win-name"] move workspace $tmp|;
+ok(@{get_ws_content($tmp2)} == 0, 'no containers on second ws');
+
 ###################################################################
 # check if floating cons are moved to new workspaces properly
 # (that is, if they are floating on the target ws, too)
index 91aeca50314cc899283c2873254a274cf9d77270..86954f1371c1a7270e20ccddf911827ec819631f 100644 (file)
@@ -22,8 +22,8 @@ cmd 'resize grow up 10 px or 25 ppt';
 
 my ($nodes, $focus) = get_ws_content($tmp);
 
-is($nodes->[0]->{percent}, 0.25, 'top window got only 25%');
-is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got only 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%');
 
 
 ############################################################
@@ -34,8 +34,8 @@ cmd 'split h';
 
 ($nodes, $focus) = get_ws_content($tmp);
 
-is($nodes->[0]->{percent}, 0.25, 'top window got only 25%');
-is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got only 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%');
 
 ############################################################
 # checks that resizing within stacked/tabbed cons works
@@ -52,14 +52,14 @@ cmd 'split h';
 cmd 'layout stacked';
 
 ($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.5, 'top window got 50%');
-is($nodes->[1]->{percent}, 0.5, 'bottom window got 50%');
+ok(cmp_float($nodes->[0]->{percent}, 0.5), 'top window got 50%');
+ok(cmp_float($nodes->[1]->{percent}, 0.5), 'bottom window got 50%');
 
 cmd 'resize grow up 10 px or 25 ppt';
 
 ($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.25, 'top window got 25%');
-is($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'top window got 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'bottom window got 75%');
 
 ############################################################
 # Checks that resizing in the parent's parent's orientation works.
@@ -79,14 +79,14 @@ $top = open_window;
 $bottom = open_window;
 
 ($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.5, 'left window got 50%');
-is($nodes->[1]->{percent}, 0.5, 'right window got 50%');
+ok(cmp_float($nodes->[0]->{percent}, 0.5), 'left window got 50%');
+ok(cmp_float($nodes->[1]->{percent}, 0.5), 'right window got 50%');
 
 cmd 'resize grow left 10 px or 25 ppt';
 
 ($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.25, 'left window got 25%');
-is($nodes->[1]->{percent}, 0.75, 'right window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left window got 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right window got 75%');
 
 ################################################################################
 # Check that the resize grow/shrink width/height syntax works.
@@ -101,8 +101,8 @@ $right = open_window;
 cmd 'resize grow width 10 px or 25 ppt';
 
 ($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.25, 'left window got 25%');
-is($nodes->[1]->{percent}, 0.75, 'right window got 75%');
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left window got 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right window got 75%');
 
 # Now test it with four windows
 $tmp = fresh_workspace;
@@ -112,19 +112,19 @@ open_window for (1..4);
 cmd 'resize grow width 10 px or 25 ppt';
 
 ($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%');
-is($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%');
-is($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%');
-is($nodes->[3]->{percent}, 0.50, 'fourth window got 50%');
+ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%');
+ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%');
+ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%');
+ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%');
 
 # height should be a no-op in this situation
 cmd 'resize grow height 10 px or 25 ppt';
 
 ($nodes, $focus) = get_ws_content($tmp);
-is($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%');
-is($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%');
-is($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%');
-is($nodes->[3]->{percent}, 0.50, 'fourth window got 50%');
+ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%');
+ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%');
+ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%');
+ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%');
 
 
 ############################################################
index 9d22afc3d0f955c49e58096a0d7187bf39ca7932..dbd1f2465dd1038549a5e1e9b5911e8d05a64b96 100644 (file)
@@ -22,7 +22,7 @@ cmd 'move up';
 cmd 'move right';
 my $ws = get_ws($tmp);
 
-is($ws->{orientation}, 'horizontal', 'workspace orientation is horizontal');
+is($ws->{layout}, 'splith', 'workspace layout is splith');
 is(@{$ws->{nodes}}, 3, 'all three windows on workspace level');
 
 done_testing;
index f9dc6dce1fe48e1fe158b10c629578fc5e6b780e..b779ce7d8490286a95088aacd2b4d2619b640c5b 100644 (file)
@@ -11,9 +11,9 @@ my $i3 = i3(get_socket_path());
 
 my $tmp = fresh_workspace;
 
-#####################################################################
-# open the left window
-#####################################################################
+################################################################################
+# Open the left window.
+################################################################################
 
 my $left = open_window({ background_color => '#ff0000' });
 
@@ -21,23 +21,26 @@ is($x->input_focus, $left->id, 'left window focused');
 
 diag("left = " . $left->id);
 
-#####################################################################
-# Open the right window
-#####################################################################
+################################################################################
+# Open the right window.
+################################################################################
 
 my $right = open_window({ background_color => '#00ff00' });
 
 diag("right = " . $right->id);
 
-#####################################################################
-# Set the right window to fullscreen
-#####################################################################
+################################################################################
+# Set the right window to fullscreen.
+################################################################################
+
 cmd 'nop setting fullscreen';
 cmd 'fullscreen';
 
-#####################################################################
-# Open a third window
-#####################################################################
+################################################################################
+# Open a third window. Since we're fullscreen, the window won't be # mapped, so
+# don't wait for it to be mapped. Instead, just send the map request and sync
+# with i3 to make sure i3 recognizes it.
+################################################################################
 
 my $third = open_window({
         background_color => '#0000ff',
@@ -51,13 +54,15 @@ sync_with_i3;
 
 diag("third = " . $third->id);
 
-# move the fullscreen window to a different ws
+################################################################################
+# Move the window to a different workspace, and verify that the third window now
+# gets focused in the current workspace.
+################################################################################
 
 my $tmp2 = get_unused_workspace;
 
 cmd "move workspace $tmp2";
 
-# verify that the third window has the focus
 is($x->input_focus, $third->id, 'third window focused');
 
 ################################################################################
@@ -87,20 +92,204 @@ is($nodes->[0]->{id}, $old_id, 'id unchanged');
 is($nodes->[0]->{focused}, 1, 'fullscreen window focused');
 
 ################################################################################
-# Make sure it's possible to focus a container in a different workspace even if
-# we are currently focusing a fullscreen container.
+# Ensure it's possible to change focus if it doesn't escape the fullscreen
+# container with fullscreen global. We can't even focus a container in a
+# different workspace.
 ################################################################################
 
+cmd 'fullscreen';
+
+$tmp = fresh_workspace;
+cmd "workspace $tmp";
+my $diff_ws = open_window;
+
 $tmp2 = fresh_workspace;
-my $focusable_window = open_window;
+cmd "workspace $tmp2";
+cmd 'split h';
+
+$left = open_window;
+my $right1 = open_window;
+cmd 'split v';
+my $right2 = open_window;
+
+cmd 'focus parent';
+cmd 'fullscreen global';
+
+cmd '[id="' . $right1->id . '"] focus';
+is($x->input_focus, $right1->id, 'upper right window focused');
+
+cmd '[id="' . $right2->id . '"] focus';
+is($x->input_focus, $right2->id, 'bottom right window focused');
+
+cmd 'focus parent';
+isnt($x->input_focus, $right2->id, 'bottom right window no longer focused');
+
+cmd 'focus child';
+is($x->input_focus, $right2->id, 'bottom right window focused again');
+
+cmd '[id="' . $left->id . '"] focus';
+is($x->input_focus, $right2->id, 'prevented focus change to left window');
+
+cmd 'focus up';
+is($x->input_focus, $right1->id, 'allowed focus up');
+
+cmd 'focus down';
+is($x->input_focus, $right2->id, 'allowed focus down');
+
+cmd 'focus left';
+is($x->input_focus, $right2->id, 'prevented focus left');
+
+cmd 'focus right';
+is($x->input_focus, $right2->id, 'prevented focus right');
+
+cmd 'focus down';
+is($x->input_focus, $right1->id, 'allowed focus wrap (down)');
+
+cmd 'focus up';
+is($x->input_focus, $right2->id, 'allowed focus wrap (up)');
+
+cmd '[id="' . $diff_ws->id . '"] focus';
+is($x->input_focus, $right2->id, 'prevented focus change to different ws');
+
+################################################################################
+# Same tests when we're in non-global fullscreen mode. It should now be possible
+# to focus a container in a different workspace.
+################################################################################
+
+cmd 'focus parent';
+cmd 'fullscreen global';
+cmd 'fullscreen';
+
+cmd '[id="' . $right1->id . '"] focus';
+is($x->input_focus, $right1->id, 'upper right window focused');
+
+cmd '[id="' . $right2->id . '"] focus';
+is($x->input_focus, $right2->id, 'bottom right window focused');
+
+cmd 'focus parent';
+isnt($x->input_focus, $right2->id, 'bottom right window no longer focused');
+
+cmd 'focus child';
+is($x->input_focus, $right2->id, 'bottom right window focused again');
+
+cmd '[id="' . $left->id . '"] focus';
+is($x->input_focus, $right2->id, 'prevented focus change to left window');
+
+cmd 'focus up';
+is($x->input_focus, $right1->id, 'allowed focus up');
+
+cmd 'focus down';
+is($x->input_focus, $right2->id, 'allowed focus down');
+
+cmd 'focus left';
+is($x->input_focus, $right2->id, 'prevented focus left');
+
+cmd 'focus right';
+is($x->input_focus, $right2->id, 'prevented focus right');
+
+cmd 'focus down';
+is($x->input_focus, $right1->id, 'allowed focus wrap (down)');
+
+cmd 'focus up';
+is($x->input_focus, $right2->id, 'allowed focus wrap (up)');
+
+cmd '[id="' . $diff_ws->id . '"] focus';
+is($x->input_focus, $diff_ws->id, 'allowed focus change to different ws');
+
+################################################################################
+# More testing of the interaction between wrapping and the fullscreen focus
+# restrictions.
+################################################################################
+
+cmd '[id="' . $right1->id . '"] focus';
+is($x->input_focus, $right1->id, 'upper right window focused');
+
+cmd 'focus parent';
+cmd 'fullscreen';
+cmd 'focus child';
+
+cmd 'split v';
+my $right12 = open_window;
+
+cmd 'focus down';
+is($x->input_focus, $right2->id, 'bottom right window focused');
+
+cmd 'split v';
+my $right22 = open_window;
+
+cmd 'focus parent';
+cmd 'fullscreen';
+cmd 'focus child';
+
+cmd 'focus down';
+is($x->input_focus, $right2->id, 'focus did not leave parent container (1)');
+
+cmd 'focus down';
+is($x->input_focus, $right22->id, 'focus did not leave parent container (2)');
+
+cmd 'focus up';
+is($x->input_focus, $right2->id, 'focus did not leave parent container (3)');
+
+cmd 'focus up';
+is($x->input_focus, $right22->id, 'focus did not leave parent container (4)');
+
+################################################################################
+# Ensure that moving in a direction doesn't violate the focus restrictions.
+################################################################################
+
+sub verify_move {
+    my $num = shift;
+    my $msg = shift;
+    my $nodes = get_ws_content($tmp2);
+    my $split = $nodes->[1];
+    my $fs = $split->{nodes}->[1];
+    is(scalar @{$fs->{nodes}}, $num, $msg);
+}
+
+cmd 'move left';
+verify_move(2, 'prevented move left');
+cmd 'move right';
+verify_move(2, 'prevented move right');
+cmd 'move down';
+verify_move(2, 'prevented move down');
+cmd 'move up';
+cmd 'move up';
+verify_move(2, 'prevented move up');
+
+################################################################################
+# Moving to a different workspace is allowed with per-output fullscreen
+# containers.
+################################################################################
+
+cmd "move to workspace $tmp";
+verify_move(1, 'did not prevent move to workspace by name');
 
 cmd "workspace $tmp";
-cmd '[id="' . $focusable_window->id . '"] focus';
+cmd "move to workspace $tmp2";
+cmd "workspace $tmp2";
 
-is(focused_ws(), $tmp2, 'focus went to a different workspace');
+cmd "move to workspace prev";
+verify_move(1, 'did not prevent move to workspace by position');
 
-$nodes = get_ws_content($tmp2);
-is(scalar @$nodes, 1, 'precisely one window');
-is($nodes->[0]->{focused}, 1, 'focusable window focused');
+################################################################################
+# Ensure that is not allowed with global fullscreen containers.
+################################################################################
+
+cmd "workspace $tmp";
+cmd "move to workspace $tmp2";
+cmd "workspace $tmp2";
+
+cmd 'focus parent';
+cmd 'fullscreen';
+cmd 'fullscreen global';
+cmd 'focus child';
+
+cmd "move to workspace $tmp";
+verify_move(2, 'prevented move to workspace by name');
+
+cmd "move to workspace prev";
+verify_move(2, 'prevented move to workspace by position');
+
+# TODO: Tests for "move to output" and "move workspace to output".
 
 done_testing;
diff --git a/testcases/t/157-regress-fullscreen-level-up.t b/testcases/t/157-regress-fullscreen-level-up.t
deleted file mode 100644 (file)
index 316dbca..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!perl
-# vim:ts=4:sw=4:expandtab
-#
-# Regression test: level up should be a noop during fullscreen mode
-#
-use i3test;
-
-my $tmp = fresh_workspace;
-
-#####################################################################
-# open a window, verify it’s not in fullscreen mode
-#####################################################################
-
-my $win = open_window;
-
-my $nodes = get_ws_content $tmp;
-is(@$nodes, 1, 'exactly one client');
-is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen');
-
-#####################################################################
-# make it fullscreen
-#####################################################################
-
-cmd 'nop making fullscreen';
-cmd 'fullscreen';
-
-$nodes = get_ws_content $tmp;
-is($nodes->[0]->{fullscreen_mode}, 1, 'client fullscreen now');
-
-#####################################################################
-# send level up, try to un-fullscreen
-#####################################################################
-cmd 'focus parent';
-cmd 'fullscreen';
-
-$nodes = get_ws_content $tmp;
-is($nodes->[0]->{fullscreen_mode}, 0, 'client not fullscreen any longer');
-
-does_i3_live;
-
-done_testing;
index c63bbbc4e12d6e8efa0cf31292c5b96a04809c7d..7c3946b4e91c1549e39a4a5cdc584a61f5db19cb 100644 (file)
@@ -5,6 +5,7 @@
 #
 use i3test i3_autostart => 0;
 use File::Temp qw(tempfile tempdir);
+use File::Basename;
 use POSIX qw(getuid);
 use v5.10;
 
@@ -20,21 +21,14 @@ EOT
 # ensure XDG_RUNTIME_DIR is not set
 delete $ENV{XDG_RUNTIME_DIR};
 
-# See which files exist in /tmp before to not mistakenly check an already
-# existing tmpdir of another i3 instance.
-my @files_before = </tmp/i3-*>;
 my $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1);
-my @files_after = </tmp/i3-*>;
-@files_after = grep { !($_ ~~ @files_before) } @files_after;
-
-is(@files_after, 1, 'one new temp directory');
-
+my $socketpath = get_socket_path(0);
 my $folder = "/tmp/i3-" . getpwuid(getuid());
-like($files_after[0], qr/^$folder/, 'temp directory matches expected pattern');
-$folder = $files_after[0];
+like(dirname($socketpath), qr/^$folder/, 'temp directory matches expected pattern');
+$folder = dirname($socketpath);
 
 ok(-d $folder, "folder $folder exists");
-my $socketpath = "$folder/ipc-socket." . $pid;
+$socketpath = "$folder/ipc-socket." . $pid;
 ok(-S $socketpath, "file $socketpath exists and is a socket");
 
 exit_gracefully($pid);
index d79c1000e92b59110c2e406d63485071016ef102..3a8a10d5fe166baf27714ab7b55047debd7c3a57 100644 (file)
@@ -57,6 +57,7 @@ my $tmp = fresh_workspace;
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 
 my $window = open_special;
+wait_for_map($window);
 
 ok(@{get_ws_content($tmp)} == 1, 'special window got managed to current (random) workspace');
 
@@ -206,24 +207,16 @@ sub i3nagbar_running {
 $config = <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
-assign "special" → ~
+for_window [title="special"] floating enable
 EOT
 
 $pid = launch_with_config($config);
 
-# Ensure that i3-nagbar is running. It should be started pretty quickly, so we
-# busy-loop with a short delay.
-while (!i3nagbar_running($pid)) {
-    sleep 0.05;
-}
-
 $tmp = fresh_workspace;
 
 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
 my @docked = get_dock_clients;
-# We expect i3-nagbar as the first dock client due to using the old assign
-# syntax
-is(@docked, 1, 'one dock client yet');
+is(@docked, 0, 'one dock client yet');
 
 $window = open_special(
     window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
@@ -233,7 +226,7 @@ $content = get_ws($tmp);
 ok(@{$content->{nodes}} == 0, 'no tiling cons');
 ok(@{$content->{floating_nodes}} == 0, 'one floating con');
 @docked = get_dock_clients;
-is(@docked, 2, 'two dock clients now');
+is(@docked, 1, 'one dock client now');
 
 $window->destroy;
 
index e791bb015cd10315cf306982c5189ab56bda7dc7..dd0cbc6092dbac026cfb18f70c2e7df2cce5305a 100644 (file)
@@ -195,7 +195,7 @@ EOT
 
 $output = migrate_config($input);
 ok(line_exists($output, qr|^bindsym Mod1\+s layout stacking$|), 's replaced');
-ok(line_exists($output, qr|^bindsym Mod1\+s layout default$|), 'd replaced');
+ok(line_exists($output, qr|^bindsym Mod1\+s layout toggle split$|), 'd replaced');
 ok(line_exists($output, qr|^bindsym Mod1\+s layout tabbed$|), 'T replaced');
 ok(line_exists($output, qr|^bindsym Mod1\+s fullscreen$|), 'f replaced');
 ok(line_exists($output, qr|^bindsym Mod1\+s fullscreen global$|), 'fg replaced');
index 8b57a0a1c7c9c4789871353b3e9d0dd5827181eb..335c775de346e1b0c9661e2e21e4a608470a0d83 100644 (file)
@@ -12,7 +12,7 @@ sub parser_calls {
 
     # TODO: use a timeout, so that we can error out if it doesn’t terminate
     # TODO: better way of passing arguments
-    my $stdout = qx(../test.commands_parser '$command' 2>&-);
+    my $stdout = qx(../test.commands_parser '$command' 2>&1 >&-);
 
     # Filter out all debugging output.
     my @lines = split("\n", $stdout);
@@ -127,15 +127,15 @@ is(parser_calls("\nworkspace test"),
 ################################################################################
 
 is(parser_calls('unknown_literal'),
-   "Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode'\n" .
-   "Your command: unknown_literal\n" .
-   "              ^^^^^^^^^^^^^^^",
+   "ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode'\n" .
+   "ERROR: Your command: unknown_literal\n" .
+   "ERROR:               ^^^^^^^^^^^^^^^",
    'error for unknown literal ok');
 
 is(parser_calls('move something to somewhere'),
-   "Expected one of these tokens: 'window', 'container', 'to', 'workspace', 'output', 'scratchpad', 'left', 'right', 'up', 'down', 'position', 'absolute'\n" .
-   "Your command: move something to somewhere\n" .
-   "                   ^^^^^^^^^^^^^^^^^^^^^^",
+   "ERROR: Expected one of these tokens: 'window', 'container', 'to', 'workspace', 'output', 'scratchpad', 'left', 'right', 'up', 'down', 'position', 'absolute'\n" .
+   "ERROR: Your command: move something to somewhere\n" .
+   "ERROR:                    ^^^^^^^^^^^^^^^^^^^^^^",
    'error for unknown literal ok');
 
 ################################################################################
diff --git a/testcases/t/191-resize-levels.t b/testcases/t/191-resize-levels.t
new file mode 100644 (file)
index 0000000..230a491
--- /dev/null
@@ -0,0 +1,29 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+# Verifies that you can resize across different levels of containers even when
+# they are all of the same orientation.
+# (Ticket #754)
+use i3test;
+
+my $tmp = fresh_workspace;
+
+open_window;
+open_window;
+cmd 'split v';
+my $middle = open_window;
+open_window;
+cmd 'focus parent';
+cmd 'split h';
+open_window;
+
+cmd '[id="' . $middle->id . '"] focus';
+is($x->input_focus, $middle->id, 'middle window focused');
+
+cmd 'resize grow left 10px or 25ppt';
+
+my ($nodes, $focus) = get_ws_content($tmp);
+
+ok(cmp_float($nodes->[0]->{percent}, 0.25), 'left container got only 25%');
+ok(cmp_float($nodes->[1]->{percent}, 0.75), 'right container got 75%');
+
+done_testing;
diff --git a/testcases/t/192-layout.t b/testcases/t/192-layout.t
new file mode 100644 (file)
index 0000000..e410d51
--- /dev/null
@@ -0,0 +1,84 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+# Verifies that switching between the different layouts works as expected.
+use i3test;
+
+my $tmp = fresh_workspace;
+
+open_window;
+open_window;
+cmd 'split v';
+open_window;
+
+my ($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout is splitv currently');
+
+cmd 'layout stacked';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
+
+cmd 'layout tabbed';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
+
+cmd 'layout toggle split';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout now splitv again');
+
+cmd 'layout toggle split';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splith', 'layout now splith');
+
+cmd 'layout toggle split';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
+
+cmd 'layout toggle split';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splith', 'layout now splith');
+
+cmd 'layout toggle';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
+
+cmd 'layout toggle';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
+
+cmd 'layout toggle';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splith', 'layout now splith');
+
+cmd 'layout toggle';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
+
+cmd 'layout toggle all';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
+
+cmd 'layout toggle all';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splith', 'layout now splith');
+
+cmd 'layout toggle all';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
+
+cmd 'layout toggle all';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
+
+cmd 'layout toggle all';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
+
+cmd 'layout toggle all';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splith', 'layout now splith');
+
+cmd 'layout toggle all';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
+
+done_testing;
index 57e5694333a30b7013b981f042b174939c15820f..8357e0dac6429dc29d1c42df39a579584cefea66 100644 (file)
@@ -9,6 +9,10 @@ use i3test i3_autostart => 0;
 # TODO:
 # introduce 'move workspace 3 to output <output>' with synonym 'move workspace 3 to <output>'
 
+# Ensure the pointer is at (0, 0) so that we really start on the first
+# (the left) workspace.
+$x->root->warp_pointer(0, 0);
+
 my $config = <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1