Merge branch 'next'
This commit is contained in:
commit
9c3ece56d2
|
@ -1,33 +1,25 @@
|
||||||
*.o
|
*.o
|
||||||
tags
|
tags
|
||||||
include/loglevels.h
|
|
||||||
include/GENERATED_*.h
|
include/GENERATED_*.h
|
||||||
loglevels.tmp
|
include/all.h.pch
|
||||||
*.swp
|
*.swp
|
||||||
*.gcda
|
*.gcda
|
||||||
*.gcno
|
*.gcno
|
||||||
testcases/testsuite-*
|
testcases/testsuite-*
|
||||||
testcases/latest
|
testcases/latest
|
||||||
testcases/Makefile
|
testcases/Makefile
|
||||||
|
testcases/Makefile.old
|
||||||
|
testcases/.last_run_timings.json
|
||||||
|
testcases/_Inline
|
||||||
|
testcases/inc
|
||||||
|
testcases/META.yml
|
||||||
test.commands_parser
|
test.commands_parser
|
||||||
*.output
|
*.output
|
||||||
*.tab.*
|
*.tab.*
|
||||||
*.yy.c
|
*.yy.c
|
||||||
man/i3-msg.1
|
man/*.1
|
||||||
man/i3-msg.xml
|
man/*.xml
|
||||||
man/i3-msg.html
|
man/*.html
|
||||||
man/i3-nagbar.1
|
|
||||||
man/i3-nagbar.xml
|
|
||||||
man/i3-nagbar.html
|
|
||||||
man/i3-wsbar.1
|
|
||||||
man/i3-wsbar.xml
|
|
||||||
man/i3-wsbar.html
|
|
||||||
man/i3-input.1
|
|
||||||
man/i3-input.xml
|
|
||||||
man/i3-input.html
|
|
||||||
man/i3.1
|
|
||||||
man/i3.xml
|
|
||||||
man/i3.html
|
|
||||||
*.tar.bz2*
|
*.tar.bz2*
|
||||||
i3
|
i3
|
||||||
i3-input/i3-input
|
i3-input/i3-input
|
||||||
|
@ -35,5 +27,7 @@ i3-nagbar/i3-nagbar
|
||||||
i3-msg/i3-msg
|
i3-msg/i3-msg
|
||||||
i3-config-wizard/i3-config-wizard
|
i3-config-wizard/i3-config-wizard
|
||||||
i3-dump-log/i3-dump-log
|
i3-dump-log/i3-dump-log
|
||||||
libi3/libi3.a
|
libi3.a
|
||||||
docs/*.pdf
|
docs/*.pdf
|
||||||
|
docs/*.html
|
||||||
|
!/docs/refcard.html
|
||||||
|
|
6
DEPENDS
6
DEPENDS
|
@ -8,7 +8,6 @@
|
||||||
│ dependency │ min. │ lkgv │ URL │
|
│ dependency │ min. │ lkgv │ URL │
|
||||||
├─────────────┼────────┼────────┼────────────────────────────────────────┤
|
├─────────────┼────────┼────────┼────────────────────────────────────────┤
|
||||||
│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │
|
│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │
|
||||||
│ xcb-proto │ 1.3 │ 1.6 │ http://xcb.freedesktop.org/dist/ │
|
|
||||||
│ libxcb │ 1.1.93 │ 1.7 │ http://xcb.freedesktop.org/dist/ │
|
│ libxcb │ 1.1.93 │ 1.7 │ http://xcb.freedesktop.org/dist/ │
|
||||||
│ xcb-util │ 0.3.3 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │
|
│ xcb-util │ 0.3.3 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │
|
||||||
│ libev │ 4.0 │ 4.04 │ http://libev.schmorp.de/ │
|
│ libev │ 4.0 │ 4.04 │ http://libev.schmorp.de/ │
|
||||||
|
@ -17,13 +16,18 @@
|
||||||
│ yajl │ 1.0.8 │ 2.0.1 │ http://lloyd.github.com/yajl/ │
|
│ yajl │ 1.0.8 │ 2.0.1 │ http://lloyd.github.com/yajl/ │
|
||||||
│ asciidoc │ 8.3.0 │ 8.6.4 │ http://www.methods.co.nz/asciidoc/ │
|
│ asciidoc │ 8.3.0 │ 8.6.4 │ http://www.methods.co.nz/asciidoc/ │
|
||||||
│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │
|
│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │
|
||||||
|
│ Pod::Simple²│ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/
|
||||||
│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │
|
│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │
|
||||||
│ libxcursor │ 1.1.11 │ 1.1.11 │ http://ftp.x.org/pub/current/src/lib/ │
|
│ libxcursor │ 1.1.11 │ 1.1.11 │ http://ftp.x.org/pub/current/src/lib/ │
|
||||||
│ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │
|
│ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │
|
||||||
│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │
|
│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │
|
||||||
│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
|
│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
|
||||||
|
│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │
|
||||||
|
│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │
|
||||||
└─────────────┴────────┴────────┴────────────────────────────────────────┘
|
└─────────────┴────────┴────────┴────────────────────────────────────────┘
|
||||||
¹ libsn = libstartup-notification
|
¹ libsn = libstartup-notification
|
||||||
|
² Pod::Simple is a Perl module required for converting the testsuite
|
||||||
|
documentation to HTML. See http://michael.stapelberg.de/cpan/#Pod::Simple
|
||||||
|
|
||||||
i3bar, i3-msg, i3-input, i3-nagbar and i3-config-wizard do not introduce any
|
i3bar, i3-msg, i3-input, i3-nagbar and i3-config-wizard do not introduce any
|
||||||
new dependencies.
|
new dependencies.
|
||||||
|
|
147
Makefile
147
Makefile
|
@ -2,151 +2,58 @@ TOPDIR=$(shell pwd)
|
||||||
|
|
||||||
include $(TOPDIR)/common.mk
|
include $(TOPDIR)/common.mk
|
||||||
|
|
||||||
# Depend on the object files of all source-files in src/*.c and on all header files
|
SUBDIRS:=
|
||||||
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
|
|
||||||
|
|
||||||
# Recursively generate loglevels.h by explicitly calling make
|
ALL_TARGETS =
|
||||||
# We need this step because we need to ensure that loglevels.h will be
|
INSTALL_TARGETS =
|
||||||
# updated if necessary, but we also want to save rebuilds of the object
|
CLEAN_TARGETS =
|
||||||
# files, so we cannot let the object files depend on loglevels.h.
|
DISTCLEAN_TARGETS =
|
||||||
ifeq ($(MAKECMDGOALS),loglevels.h)
|
|
||||||
#UNUSED:=$(warning Generating loglevels.h)
|
|
||||||
else
|
|
||||||
UNUSED:=$(shell $(MAKE) loglevels.h)
|
|
||||||
endif
|
|
||||||
|
|
||||||
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
|
include libi3/libi3.mk
|
||||||
src/%.o: src/%.c ${HEADERS}
|
include src/i3.mk
|
||||||
echo "[i3] CC $<"
|
include i3-config-wizard/i3-config-wizard.mk
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -c -o $@ $<
|
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}
|
install: $(INSTALL_TARGETS)
|
||||||
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
|
|
||||||
|
|
||||||
dist: distclean
|
dist: distclean
|
||||||
[ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION}
|
[ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION}
|
||||||
[ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2
|
[ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2
|
||||||
mkdir i3-${VERSION}
|
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}
|
cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man parser-specs testcases i3-${VERSION}
|
||||||
# Only copy toplevel documentation (important stuff)
|
# Only copy toplevel documentation (important stuff)
|
||||||
mkdir i3-${VERSION}/docs
|
mkdir i3-${VERSION}/docs
|
||||||
# Pre-generate documentation
|
# Pre-generate documentation
|
||||||
$(MAKE) -C docs
|
$(MAKE) docs
|
||||||
$(MAKE) -C i3bar/doc
|
|
||||||
# Cleanup τεχ output files
|
# Cleanup τεχ output files
|
||||||
find docs -regex ".*\.\(aux\|out\|log\|toc\|bm\|dvi\|log\)" -exec rm '{}' \;
|
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 \;
|
find docs -maxdepth 1 -type f ! \( -name "*.xcf" -or -name "*.svg" \) -exec cp '{}' i3-${VERSION}/docs \;
|
||||||
# Only copy source code from i3-input
|
# Only copy source code from i3-input
|
||||||
mkdir i3-${VERSION}/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 \;
|
find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.mk" -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
|
echo -n ${I3_VERSION} > i3-${VERSION}/I3_VERSION
|
||||||
|
echo -n ${VERSION} > i3-${VERSION}/VERSION
|
||||||
# Pre-generate a manpage to allow distributors to skip this step and save some dependencies
|
# 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 man/*.1 i3-${VERSION}/man/
|
||||||
cp i3bar/doc/*.1 i3-${VERSION}/i3bar/doc/
|
|
||||||
tar cfj i3-${VERSION}.tar.bz2 i3-${VERSION}
|
tar cfj i3-${VERSION}.tar.bz2 i3-${VERSION}
|
||||||
rm -rf i3-${VERSION}
|
rm -rf i3-${VERSION}
|
||||||
|
|
||||||
clean:
|
clean: $(CLEAN_TARGETS)
|
||||||
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_*
|
|
||||||
(which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true
|
(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
|
distclean: clean $(DISTCLEAN_TARGETS)
|
||||||
rm -f i3
|
|
||||||
for dir in $(SUBDIRS); do \
|
|
||||||
echo ""; \
|
|
||||||
echo "DISTCLEAN $$dir"; \
|
|
||||||
$(MAKE) TOPDIR=$(TOPDIR) -C $$dir distclean; \
|
|
||||||
done
|
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
rm -f /tmp/i3-coverage.info
|
rm -f /tmp/i3-coverage.info
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
|
||||||
|
┌──────────────────────────────┐
|
||||||
|
│ Release notes for i3 v4.3 │
|
||||||
|
└──────────────────────────────┘
|
||||||
|
|
||||||
|
This is the i3 v4.3. This version is considered stable. All users of i3 are
|
||||||
|
strongly encouraged to upgrade.
|
||||||
|
|
||||||
|
One rather visible change is that commands which could not be parsed properly
|
||||||
|
will now spawn i3-nagbar. In case you used "bindsym $mod+x firefox" (and
|
||||||
|
forgot the "exec" keyword) or you made a typo in your config, you will now
|
||||||
|
notice that :).
|
||||||
|
|
||||||
|
|
||||||
|
We also made the orientation (horizontal/vertical) part of the layout
|
||||||
|
mechanism: Before, we got the default layout and you could change
|
||||||
|
orientation. Now, there are two new layouts "splitv" and "splith", which
|
||||||
|
replace the default layout. The "split h" and "split v" commands continue to
|
||||||
|
work as before, they split the current container and you will end up in a
|
||||||
|
split container with layout splith (after "split h") or splitv (after
|
||||||
|
"split v").
|
||||||
|
|
||||||
|
To change a splith container into a splitv container, use either "layout
|
||||||
|
splitv" or "layout toggle split". The latter command is used in the
|
||||||
|
default config as mod+l (formerly "layout default"). In case you have
|
||||||
|
"layout default" in your config file, it is recommended to just replace
|
||||||
|
it by "layout toggle split", which will work as "layout default" did
|
||||||
|
before when pressing it once, but toggle between horizontal/vertical
|
||||||
|
when pressing it repeatedly.
|
||||||
|
|
||||||
|
The rationale behind this change is that it’s cleaner to have all
|
||||||
|
parameters that influence how windows are rendered in the layout itself
|
||||||
|
rather than having one special layout in combination with an additional
|
||||||
|
orientation. This enables us to change existing split containers in all
|
||||||
|
cases without breaking existing features (see ticket #464). Also, users
|
||||||
|
should feel more confident about whether they are actually splitting or
|
||||||
|
just changing an existing split container now.
|
||||||
|
|
||||||
|
As a nice side-effect, this commit brings back the "layout toggle"
|
||||||
|
feature we once had in i3 v3 (see the userguide).
|
||||||
|
|
||||||
|
|
||||||
|
Another very important change is that we now support pango for rendering text.
|
||||||
|
The default is still to use misc-fixed (X core fonts), but you can use a font
|
||||||
|
specification starting with "xft:" now, such as "xft:DejaVu Sans Mono 10" and
|
||||||
|
i3 will use pango. The sole motivation for this is NOT to have fancier window
|
||||||
|
decorations, but to support fonts which have more glyphs (think Japanese for
|
||||||
|
example) and to support right-to-left rendering (open http://www.ftpal.net/
|
||||||
|
for an example). Supporting users from all over the planet is important, and
|
||||||
|
as such I would strongly advise distribution packagers to leave pango support
|
||||||
|
enabled. In case you are working on a very low-spec embedded device, it is
|
||||||
|
easy enough to disable pango support, see common.mk.
|
||||||
|
|
||||||
|
|
||||||
|
Also, the 'layout' command now always works on the parent split container. This
|
||||||
|
allows you to do things like this:
|
||||||
|
|
||||||
|
for_window [class="XTerm"] layout tabbed
|
||||||
|
|
||||||
|
When you now open XTerm on an empty workspace, the whole workspace will be
|
||||||
|
set to tabbed. In case you want to open XTerm in its own tabbed split
|
||||||
|
container, you need to split before:
|
||||||
|
|
||||||
|
for_window [class="XTerm"] split v, layout tabbed
|
||||||
|
|
||||||
|
|
||||||
|
Furthermore, we decided to entirely ignore resize increment size hints for
|
||||||
|
tiling windows. These are set by terminal emulators (such as urxvt,
|
||||||
|
gnome-terminal, …) and specify that the window may only be resized in
|
||||||
|
multiples of the specified size. All terminal emulators cope with the window
|
||||||
|
manager ignoring these hints and by doing so, they can decide what to do with
|
||||||
|
the lost space (that is, pseudo-transparency now works without black bars in
|
||||||
|
urxvt).
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Changes in v4.3 │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
• docs: there now is documentation about lib::i3test and lib::i3test::Test,
|
||||||
|
the main Perl modules used by our testsuite.
|
||||||
|
• docs/refcard: update for v4
|
||||||
|
• docs/userguide: clarify the default for focus_follows_mouse and new_window
|
||||||
|
• docs/userguide: add section about implicit containers
|
||||||
|
• docs/userguide: give 'move <container|workspace> to output' its own section
|
||||||
|
• docs/ipc: document the 'window' field in the GET_TREE reply
|
||||||
|
• docs/ipc: update links to ipc libraries
|
||||||
|
• docs/ipc: make the reply sections consistent
|
||||||
|
• docs/i3bar-protocol: add example (illustration-only!) shell script
|
||||||
|
• man/i3bar.man: reference i3bar-protocol
|
||||||
|
• IPC: Commands now lead to proper error messages in general. If we forgot
|
||||||
|
about a specific one, please open a ticket.
|
||||||
|
• IPC: implement GET_VERSION to find out the i3 version
|
||||||
|
• i3-dump-log now comes with a massively more helpful error message that
|
||||||
|
should cover all the use cases.
|
||||||
|
• 'workspace number <number>' now opens a new workspace
|
||||||
|
• 'workspace number <number>' now works with the back_and_forth option
|
||||||
|
• Allow focus with target (criteria) when in fullscreen mode in some cases
|
||||||
|
• Allow focus child/parent when in fullscreen mode
|
||||||
|
• Restrict directional focus when in fullscreen mode
|
||||||
|
• Prevent moving out of fullscreen containers
|
||||||
|
• Add 'move to workspace current' (useful when used with criteria)
|
||||||
|
• replace loglevels by a global debug logging
|
||||||
|
• make: new makefile layout
|
||||||
|
• make: canonicalize path when compiling. This leads to sth like
|
||||||
|
../i3-4.2/src/main.c in backtraces, clearly identifying i3 code.
|
||||||
|
• automatically hide i3bar when it’s unneeded (after urgency hints)
|
||||||
|
• i3-config-wizard: use the level 0 keysym whenever it’s unambiguous
|
||||||
|
• i3-nagbar: use custom scripts to work around different terminal emulators
|
||||||
|
using different ways of interpreting the arguments to -e
|
||||||
|
• i3-sensible-terminal: add xfce4-terminal
|
||||||
|
• default config: require confirmation when exiting i3
|
||||||
|
• Display i3-nagbar when a command leads to an error.
|
||||||
|
• testcases: complete-run now supports --xtrace
|
||||||
|
• testcases: handle EAGAIN (fixes hangs)
|
||||||
|
• testcases: handle test bailouts
|
||||||
|
• Introduce splith/splitv layouts, remove orientation
|
||||||
|
• Implement hide_edge_borders option
|
||||||
|
• Support _NET_ACTIVE_WINDOW ClientMessages
|
||||||
|
• Set I3_PID atom on the X11 root window
|
||||||
|
• Implement i3 --moreversion, handy for figuring out whether you run the
|
||||||
|
latest binary which is installed.
|
||||||
|
• i3bar: be less strict about the {"version":1} JSON header
|
||||||
|
• shm-logging: implement i3-dump-log -f (follow)
|
||||||
|
• Implement pango support
|
||||||
|
• 'move workspace number n' will now create the workspace if it doesn’t exist
|
||||||
|
• Accept slashes in RandR output names
|
||||||
|
• Keep startup-notification sequences around for 30s after completion
|
||||||
|
• Introduce bindsym --release, which will trigger the binding not on the
|
||||||
|
KeyPress event, but on the KeyRelease event (useful for import(1) or
|
||||||
|
xdotool(1)).
|
||||||
|
• The signalhandler does not offer you to exit i3 anymore. Instead, there is
|
||||||
|
'b' for writing a backtrace to a file in /tmp (if gdb is installed)
|
||||||
|
• Remove support for resize increment hints for tiling windows
|
||||||
|
• Exit fullscreen mode when 'scratchpad show' is executed while in fullscreen
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Bugfixes │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
• Fix floating precision bug when floating windows are moved between outputs.
|
||||||
|
• i3bar won’t crash when full_text is missing or null in the JSON input
|
||||||
|
• When having "workspace number 1" in your config, there will no longer be a
|
||||||
|
stray workspace "number 1".
|
||||||
|
• i3.config.keycodes used bindsym instead of bindcode for the arrow key
|
||||||
|
resizing bindings by mistake
|
||||||
|
• Fix 'move to workspace' when used with criteria
|
||||||
|
• Handle clicks to the very left edge of i3bar
|
||||||
|
• When using i3 -C, don’t send remaining arguments as an IPC command
|
||||||
|
• Fix reload crashes in rare cases
|
||||||
|
• i3bar: inform all clients of new tray selection owner (fixes tray problems
|
||||||
|
with X-Chat and possibly others)
|
||||||
|
• resizing: traverse containers up properly (fixes non-working resizing when
|
||||||
|
having a h-split within a h-split for example)
|
||||||
|
• Fix floating coordinates when moving assigned workspaces
|
||||||
|
• Properly fix floating coordinates when disabling outputs
|
||||||
|
• floating_fix_coordinates: properly deal with negative positions
|
||||||
|
• floating windows: add deco_height only when in normal border mode (fixes
|
||||||
|
initial floating window position/size when using a different default border
|
||||||
|
setting).
|
||||||
|
• Fix moving scratchpad window
|
||||||
|
• Cleanup zero-byte logfile on immediate exit (they are created by i3
|
||||||
|
--get-socketpath for example).
|
||||||
|
• Fix resizing floating windows by height
|
||||||
|
• Fix back_and_forth in 'workspace number' for named workspaces
|
||||||
|
• Grab server and process pending events before managing existing windows
|
||||||
|
(fixes problems with GIMP windows not being managed after an in-place
|
||||||
|
restart)
|
||||||
|
• Don’t allow ConfigureRequests while in fullscreen (fixes a compatibility
|
||||||
|
issue with gnome-terminal and xfce’s terminal)
|
||||||
|
• Fix flickering with 1pixel border tabbed layouts
|
||||||
|
• Use _exit() instead of exit() when i3 utility programs cannot be executed
|
||||||
|
• Don’t focus the wrong workspace when moving to scratchpad
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Thanks! │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
|
||||||
|
|
||||||
|
aksr, Axel Wagner, darkraven, David Coppa, eeemsi, Felicitus, Fernando Tarlá
|
||||||
|
Cardoso Lemos, Iakov Davydov, jh, Joel Stemmer, Julius Plenz, loblik, Marcel
|
||||||
|
Hellwig, Marcus, mloskot, Moritz Bandemer, oblique, Ondrej Grover, Pavel
|
||||||
|
Löbl, Philipp Middendorf, prg, Quentin Glidic, Sebastian Ullrich, Simon
|
||||||
|
Elsbrock, somelauw, stfn, tucos, TunnelWicht, Valentin Haenel
|
||||||
|
|
||||||
|
-- Michael Stapelberg, 2012-09-19
|
197
common.mk
197
common.mk
|
@ -14,9 +14,52 @@ ifndef SYSCONFDIR
|
||||||
SYSCONFDIR=$(PREFIX)/etc
|
SYSCONFDIR=$(PREFIX)/etc
|
||||||
endif
|
endif
|
||||||
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'))"
|
# In dist tarballs, the version is stored in the I3_VERSION and VERSION files.
|
||||||
VERSION:=$(shell git describe --tags --abbrev=0)
|
I3_VERSION := '$(shell [ -f $(TOPDIR)/I3_VERSION ] && cat $(TOPDIR)/I3_VERSION)'
|
||||||
|
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/::)\")'
|
||||||
|
endif
|
||||||
|
|
||||||
|
MAJOR_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 1)
|
||||||
|
MINOR_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 2)
|
||||||
|
PATCH_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 3)
|
||||||
|
ifeq (${PATCH_VERSION},)
|
||||||
|
PATCH_VERSION := 0
|
||||||
|
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 += -DMAJOR_VERSION=${MAJOR_VERSION}
|
||||||
|
I3_CPPFLAGS += -DMINOR_VERSION=${MINOR_VERSION}
|
||||||
|
I3_CPPFLAGS += -DPATCH_VERSION=${PATCH_VERSION}
|
||||||
|
I3_CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\"
|
||||||
|
I3_CPPFLAGS += -DI3__FILE__=__FILE__
|
||||||
|
|
||||||
|
|
||||||
|
## Libraries flags
|
||||||
|
|
||||||
ifeq ($(shell which pkg-config 2>/dev/null 1>/dev/null || echo 1),1)
|
ifeq ($(shell which pkg-config 2>/dev/null 1>/dev/null || echo 1),1)
|
||||||
$(error "pkg-config was not found")
|
$(error "pkg-config was not found")
|
||||||
|
@ -35,79 +78,92 @@ endif
|
||||||
cflags_for_lib = $(shell pkg-config --silence-errors --cflags $(1) 2>/dev/null)
|
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))
|
ldflags_for_lib = $(shell pkg-config --exists 2>/dev/null $(1) && pkg-config --libs $(1) 2>/dev/null || echo -l$(2))
|
||||||
|
|
||||||
CFLAGS += -std=c99
|
# XCB common stuff
|
||||||
CFLAGS += -pipe
|
XCB_CFLAGS := $(call cflags_for_lib, xcb)
|
||||||
CFLAGS += -Wall
|
XCB_CFLAGS += $(call cflags_for_lib, xcb-event)
|
||||||
# unused-function, unused-label, unused-variable are turned on by -Wall
|
XCB_LIBS := $(call ldflags_for_lib, xcb,xcb)
|
||||||
# We don’t want unused-parameter because of the use of many callbacks
|
XCB_LIBS += $(call ldflags_for_lib, xcb-event,xcb-event)
|
||||||
CFLAGS += -Wunused-value
|
|
||||||
CFLAGS += -Iinclude
|
|
||||||
CFLAGS += $(call cflags_for_lib, xcb-keysyms)
|
|
||||||
ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1)
|
ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1)
|
||||||
CPPFLAGS += -DXCB_COMPAT
|
XCB_CFLAGS += $(call cflags_for_lib, xcb-atom)
|
||||||
CFLAGS += $(call cflags_for_lib, xcb-atom)
|
XCB_CFLAGS += $(call cflags_for_lib, xcb-aux)
|
||||||
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)
|
||||||
|
XCB_CPPFLAGS+= -DXCB_COMPAT
|
||||||
else
|
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
|
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)
|
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
|
endif
|
||||||
|
PCRE_LIBS := $(call ldflags_for_lib, libpcre,pcre)
|
||||||
|
|
||||||
LIBS += -lm
|
# startup-notification
|
||||||
# Darwin (Mac OS X) doesn’t have librt
|
LIBSN_CFLAGS := $(call cflags_for_lib, libstartup-notification-1.0)
|
||||||
ifneq ($(UNAME),Darwin)
|
LIBSN_LIBS := $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1)
|
||||||
LIBS += -lrt
|
|
||||||
endif
|
# Pango
|
||||||
LIBS += -L $(TOPDIR)/libi3 -li3
|
PANGO_CFLAGS := $(call cflags_for_lib, cairo)
|
||||||
LIBS += $(call ldflags_for_lib, xcb-event,xcb-event)
|
PANGO_CFLAGS += $(call cflags_for_lib, pangocairo)
|
||||||
LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms)
|
I3_CPPFLAGS += -DPANGO_SUPPORT=1
|
||||||
ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1)
|
PANGO_LIBS := $(call ldflags_for_lib, cairo)
|
||||||
LIBS += $(call ldflags_for_lib, xcb-atom,xcb-atom)
|
PANGO_LIBS += $(call ldflags_for_lib, pangocairo)
|
||||||
LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux)
|
|
||||||
else
|
# libi3
|
||||||
LIBS += $(call ldflags_for_lib, xcb-util)
|
LIBS = -L$(TOPDIR) -li3
|
||||||
endif
|
|
||||||
LIBS += $(call ldflags_for_lib, xcb-icccm,xcb-icccm)
|
## Platform-specific flags
|
||||||
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)
|
|
||||||
|
|
||||||
# Please test if -Wl,--as-needed works on your platform and send me a patch.
|
# 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)
|
# it is known not to work on Darwin (Mac OS X)
|
||||||
ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
|
ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
|
||||||
LDFLAGS += -Wl,--as-needed
|
as_needed_LDFLAG = -Wl,--as-needed
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(UNAME),NetBSD)
|
ifeq ($(UNAME),NetBSD)
|
||||||
# We need -idirafter instead of -I to prefer the system’s iconv over GNU libiconv
|
# We need -idirafter instead of -I to prefer the system’s iconv over GNU libiconv
|
||||||
CFLAGS += -idirafter /usr/pkg/include
|
I3_CFLAGS += -idirafter /usr/pkg/include
|
||||||
LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib
|
I3_LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(UNAME),OpenBSD)
|
ifeq ($(UNAME),OpenBSD)
|
||||||
CFLAGS += -I${X11BASE}/include
|
I3_CFLAGS += -I${X11BASE}/include
|
||||||
LIBS += -liconv
|
LIBS += -liconv
|
||||||
LDFLAGS += -L${X11BASE}/lib
|
I3_LDFLAGS += -L${X11BASE}/lib
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(UNAME),FreeBSD)
|
ifeq ($(UNAME),FreeBSD)
|
||||||
|
@ -116,33 +172,32 @@ endif
|
||||||
|
|
||||||
ifeq ($(UNAME),Darwin)
|
ifeq ($(UNAME),Darwin)
|
||||||
LIBS += -liconv
|
LIBS += -liconv
|
||||||
|
else
|
||||||
|
# Darwin (Mac OS X) doesn’t have librt
|
||||||
|
LIBS += -lrt
|
||||||
endif
|
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)))
|
ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
|
||||||
CPPFLAGS += -D_GNU_SOURCE
|
I3_CPPFLAGS += -D_GNU_SOURCE
|
||||||
endif
|
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)
|
ifeq ($(COVERAGE),1)
|
||||||
CFLAGS += -fprofile-arcs -ftest-coverage
|
I3_CFLAGS += -fprofile-arcs -ftest-coverage
|
||||||
LIBS += -lgcov
|
LIBS += -lgcov
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
V ?= 0
|
||||||
|
ifeq ($(V),0)
|
||||||
# Don’t print command lines which are run
|
# Don’t print command lines which are run
|
||||||
.SILENT:
|
.SILENT:
|
||||||
|
|
||||||
|
# echo-ing vars
|
||||||
|
V_ASCIIDOC = echo ASCIIDOC $@;
|
||||||
|
V_POD2HTML = echo POD2HTML $@;
|
||||||
|
V_A2X = echo A2X $@;
|
||||||
|
endif
|
||||||
|
|
||||||
# Always remake the following targets
|
# Always remake the following targets
|
||||||
.PHONY: install clean dist distclean
|
.PHONY: install clean dist distclean
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# vim:ts=4:sw=4:expandtab
|
||||||
|
# © 2012 Michael Stapelberg, Public Domain
|
||||||
|
|
||||||
|
# This script is a trivial shell script to send your own output to i3bar while
|
||||||
|
# using the JSON protocol.
|
||||||
|
#
|
||||||
|
# It is ugly and that is inherent to using JSON with shell scripts. You
|
||||||
|
# _really_ should not do that. See i3status or i3status’s contrib/ directory
|
||||||
|
# for examples of how to handle the output in higher-level languages.
|
||||||
|
#
|
||||||
|
# This example is purely for illustration of the protocol. DO NOT USE IT IN THE
|
||||||
|
# REAL WORLD.
|
||||||
|
|
||||||
|
# Send the header so that i3bar knows we want to use JSON:
|
||||||
|
echo '{ "version": 1 }'
|
||||||
|
|
||||||
|
# Begin the endless array.
|
||||||
|
echo '['
|
||||||
|
|
||||||
|
# We send an empty first array of blocks to make the loop simpler:
|
||||||
|
echo '[]'
|
||||||
|
|
||||||
|
# Now send blocks with information forever:
|
||||||
|
while :;
|
||||||
|
do
|
||||||
|
echo ",[{\"name\":\"time\",\"full_text\":\"$(date)\"}]"
|
||||||
|
sleep 1
|
||||||
|
done
|
|
@ -2,14 +2,34 @@ Source: i3-wm
|
||||||
Section: x11
|
Section: x11
|
||||||
Priority: extra
|
Priority: extra
|
||||||
Maintainer: Michael Stapelberg <stapelberg@debian.org>
|
Maintainer: Michael Stapelberg <stapelberg@debian.org>
|
||||||
Build-Depends: debhelper (>= 7.0.50~), libx11-dev, libxcb-util0-dev (>= 0.3.8), libxcb-keysyms1-dev, libxcb-xinerama0-dev (>= 1.1), libxcb-randr0-dev, libxcb-icccm4-dev, libxcursor-dev, asciidoc (>= 8.4.4), xmlto, docbook-xml, pkg-config, libev-dev, flex, bison, libyajl-dev, libpcre3-dev, libstartup-notification0-dev (>= 0.10)
|
Build-Depends: debhelper (>= 7.0.50~),
|
||||||
|
libx11-dev,
|
||||||
|
libxcb-util0-dev (>= 0.3.8),
|
||||||
|
libxcb-keysyms1-dev,
|
||||||
|
libxcb-xinerama0-dev (>= 1.1),
|
||||||
|
libxcb-randr0-dev,
|
||||||
|
libxcb-icccm4-dev,
|
||||||
|
libxcursor-dev,
|
||||||
|
asciidoc (>= 8.4.4),
|
||||||
|
xmlto,
|
||||||
|
docbook-xml,
|
||||||
|
pkg-config,
|
||||||
|
libev-dev,
|
||||||
|
flex,
|
||||||
|
bison,
|
||||||
|
libyajl-dev,
|
||||||
|
libpcre3-dev,
|
||||||
|
libstartup-notification0-dev (>= 0.10),
|
||||||
|
libcairo2-dev,
|
||||||
|
libpango1.0-dev,
|
||||||
|
libpod-simple-perl
|
||||||
Standards-Version: 3.9.3
|
Standards-Version: 3.9.3
|
||||||
Homepage: http://i3wm.org/
|
Homepage: http://i3wm.org/
|
||||||
|
|
||||||
Package: i3
|
Package: i3
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: i3-wm (=${binary:Version}), ${misc:Depends}
|
Depends: i3-wm (=${binary:Version}), ${misc:Depends}
|
||||||
Recommends: i3lock (>= 2.2), suckless-tools, i3status (>= 2.3)
|
Recommends: i3lock (>= 2.2), suckless-tools, i3status (>= 2.3), dunst
|
||||||
Description: metapackage (i3 window manager, screen locker, menu, statusbar)
|
Description: metapackage (i3 window manager, screen locker, menu, statusbar)
|
||||||
This metapackage installs the i3 window manager (i3-wm), the i3lock screen
|
This metapackage installs the i3 window manager (i3-wm), the i3lock screen
|
||||||
locker, i3status (for system information) and suckless-tools (for dmenu).
|
locker, i3status (for system information) and suckless-tools (for dmenu).
|
||||||
|
|
|
@ -28,3 +28,5 @@ docs/tree-shot4.png
|
||||||
docs/refcard.html
|
docs/refcard.html
|
||||||
docs/refcard_style.css
|
docs/refcard_style.css
|
||||||
docs/logo-30.png
|
docs/logo-30.png
|
||||||
|
docs/lib-i3test.html
|
||||||
|
docs/lib-i3test-test.html
|
||||||
|
|
|
@ -8,4 +8,4 @@ man/i3-migrate-config-to-v4.1
|
||||||
man/i3-sensible-pager.1
|
man/i3-sensible-pager.1
|
||||||
man/i3-sensible-editor.1
|
man/i3-sensible-editor.1
|
||||||
man/i3-sensible-terminal.1
|
man/i3-sensible-terminal.1
|
||||||
i3bar/doc/i3bar.1
|
man/i3bar.1
|
||||||
|
|
|
@ -1,36 +1,7 @@
|
||||||
# To pass additional parameters for asciidoc
|
all:
|
||||||
ASCIIDOC=asciidoc
|
$(MAKE) -C .. docs
|
||||||
|
|
||||||
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 $<
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f ${ASCIIDOC_TARGETS}
|
$(MAKE) -C .. clean-docs
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
|
@ -647,7 +647,7 @@ endif::doctype-manpage[]
|
||||||
</div>
|
</div>
|
||||||
{disable-javascript%<div id="footnotes"><hr /></div>}
|
{disable-javascript%<div id="footnotes"><hr /></div>}
|
||||||
<div id="footer" lang="de">
|
<div id="footer" lang="de">
|
||||||
© 2009-2011 Michael Stapelberg, <a href="/impress.html">Impressum</a>
|
© 2009-2012 Michael Stapelberg, <a href="/impress.html">Impressum</a>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
DISTCLEAN_TARGETS += clean-docs
|
||||||
|
|
||||||
|
# To pass additional parameters for asciidoc
|
||||||
|
ASCIIDOC = asciidoc
|
||||||
|
I3POD2HTML = ./docs/i3-pod2html
|
||||||
|
|
||||||
|
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 $@ $<
|
||||||
|
|
||||||
|
POD2HTML_TARGETS = \
|
||||||
|
docs/lib-i3test.html \
|
||||||
|
docs/lib-i3test-test.html
|
||||||
|
|
||||||
|
docs/lib-i3test.html: testcases/lib/i3test.pm
|
||||||
|
$(V_POD2HTML)$(I3POD2HTML) $< $@
|
||||||
|
|
||||||
|
docs/lib-i3test-test.html: testcases/lib/i3test/Test.pm
|
||||||
|
$(V_POD2HTML)$(I3POD2HTML) $< $@
|
||||||
|
|
||||||
|
docs: $(ASCIIDOC_TARGETS) $(POD2HTML_TARGETS)
|
||||||
|
|
||||||
|
$(ASCIIDOC_TOC_TARGETS): docs/%.html: docs/%
|
||||||
|
$(ASCIIDOC_TOC_CALL)
|
||||||
|
|
||||||
|
$(ASCIIDOC_NOTOC_TARGETS): docs/%.html: docs/%
|
||||||
|
$(ASCIIDOC_CALL)
|
||||||
|
|
||||||
|
clean-docs:
|
||||||
|
rm -f $(ASCIIDOC_TARGETS) $(POD2HTML_TARGETS)
|
|
@ -77,7 +77,7 @@ workspace, the split container we are talking about is the workspace.
|
||||||
|
|
||||||
To get an impression of how different layouts are represented, just play around
|
To get an impression of how different layouts are represented, just play around
|
||||||
and look at the data structures -- they are exposed as a JSON hash. See
|
and look at the data structures -- they are exposed as a JSON hash. See
|
||||||
http://i3wm.org/docs/ipc.html#_get_tree_reply for documentation on that and an
|
http://i3wm.org/docs/ipc.html#_tree_reply for documentation on that and an
|
||||||
example.
|
example.
|
||||||
|
|
||||||
== Files
|
== Files
|
||||||
|
@ -141,7 +141,7 @@ src/load_layout.c::
|
||||||
Contains code for loading layouts from JSON files.
|
Contains code for loading layouts from JSON files.
|
||||||
|
|
||||||
src/log.c::
|
src/log.c::
|
||||||
Handles the setting of loglevels, contains the logging functions.
|
Contains the logging functions.
|
||||||
|
|
||||||
src/main.c::
|
src/main.c::
|
||||||
Initializes the window manager.
|
Initializes the window manager.
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
# vim:ts=4:sw=4:expandtab
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Pod::Simple::HTML;
|
||||||
|
use v5.10;
|
||||||
|
|
||||||
|
$Pod::Simple::HTML::Tagmap{'Verbatim'} = '<pre><tt>';
|
||||||
|
$Pod::Simple::HTML::Tagmap{'VerbatimFormatted'} = '<pre><tt>';
|
||||||
|
$Pod::Simple::HTML::Tagmap{'/Verbatim'} = '</tt></pre>';
|
||||||
|
$Pod::Simple::HTML::Tagmap{'/VerbatimFormatted'} = '</tt></pre>';
|
||||||
|
|
||||||
|
if (@ARGV < 2) {
|
||||||
|
say STDERR "Syntax: i3-pod2html <pod-input-path> <html-output-path>";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
open(my $in, '<', $ARGV[0]) or die "Couldn’t open $ARGV[0] for reading: $!\n";
|
||||||
|
open(my $out, '>', $ARGV[1]) or die "Couldn’t open $ARGV[1] for writing: $!\n";
|
||||||
|
|
||||||
|
my $parser = Pod::Simple::HTML->new();
|
||||||
|
|
||||||
|
$parser->index(1);
|
||||||
|
$parser->html_header_before_title(
|
||||||
|
<<'EOF'
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="generator" content="Pod::Simple::HTML">
|
||||||
|
<meta name="description" content="i3 Perl documentation (testsuite)">
|
||||||
|
<link rel="stylesheet" href="http://i3wm.org/css/style.css" type="text/css" />
|
||||||
|
<style type="text/css">
|
||||||
|
.pod pre {
|
||||||
|
background: #333;
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-left: 5px solid #555;
|
||||||
|
padding: 0.5em;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
white-space: pre;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pod ul {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
.pod li {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt {
|
||||||
|
font-family: 'Droid Sans Mono', sans-serif;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pod h1 a, .pod h2 a, .pod h3 a, .pod h4 a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>
|
||||||
|
EOF
|
||||||
|
);
|
||||||
|
$parser->html_header_after_title(
|
||||||
|
<<'EOF'
|
||||||
|
</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="main">
|
||||||
|
<a href="/"><h1 id="title">i3 - improved tiling WM</h1></a>
|
||||||
|
<ul id="nav">
|
||||||
|
<li><a style="border-bottom: 2px solid #fff" href="/docs">Docs</a></li>
|
||||||
|
<li><a href="/screenshots">Screens</a></li>
|
||||||
|
<li><a href="http://faq.i3wm.org/">FAQ</a></li>
|
||||||
|
<li><a href="/contact">Contact</a></li>
|
||||||
|
<li><a href="http://bugs.i3wm.org/">Bugs</a></li>
|
||||||
|
</ul>
|
||||||
|
<br style="clear: both">
|
||||||
|
<div id="content" class="pod">
|
||||||
|
<h1>i3 Perl documentation (testsuite)</h1>
|
||||||
|
|
||||||
|
EOF
|
||||||
|
);
|
||||||
|
|
||||||
|
$parser->output_fh($out);
|
||||||
|
$parser->parse_file($in);
|
|
@ -1,7 +1,7 @@
|
||||||
i3bar input protocol
|
i3bar input protocol
|
||||||
====================
|
====================
|
||||||
Michael Stapelberg <michael@i3wm.org>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
February 2012
|
August 2012
|
||||||
|
|
||||||
This document explains the protocol in which i3bar expects its input. It
|
This document explains the protocol in which i3bar expects its input. It
|
||||||
provides support for colors, urgency, shortening and easy manipulation.
|
provides support for colors, urgency, shortening and easy manipulation.
|
||||||
|
@ -49,6 +49,9 @@ consists of a single JSON hash:
|
||||||
{ "version": 1 }
|
{ "version": 1 }
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
(Note that before i3 v4.3 the precise format had to be +{"version":1}+,
|
||||||
|
byte-for-byte.)
|
||||||
|
|
||||||
What follows is an infinite array (so it should be parsed by a streaming JSON
|
What follows is an infinite array (so it should be parsed by a streaming JSON
|
||||||
parser, but as described above you can go for a simpler solution), whose
|
parser, but as described above you can go for a simpler solution), whose
|
||||||
elements are one array per status line. A status line is one unit of
|
elements are one array per status line. A status line is one unit of
|
||||||
|
@ -86,6 +89,10 @@ Please note that this example was pretty printed for human consumption.
|
||||||
i3status and others will output single statuslines in one line, separated by
|
i3status and others will output single statuslines in one line, separated by
|
||||||
\n.
|
\n.
|
||||||
|
|
||||||
|
You can find an example of a shell script which can be used as your
|
||||||
|
+status_command+ in the bar configuration at
|
||||||
|
http://code.stapelberg.de/git/i3/tree/contrib/trivial-bar-script.sh?h=next
|
||||||
|
|
||||||
=== Blocks in detail
|
=== Blocks in detail
|
||||||
|
|
||||||
full_text::
|
full_text::
|
||||||
|
|
54
docs/ipc
54
docs/ipc
|
@ -1,7 +1,7 @@
|
||||||
IPC interface (interprocess communication)
|
IPC interface (interprocess communication)
|
||||||
==========================================
|
==========================================
|
||||||
Michael Stapelberg <michael@i3wm.org>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
February 2012
|
August 2012
|
||||||
|
|
||||||
This document describes how to interface with i3 from a separate process. This
|
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
|
is useful for example to remote-control i3 (to write test cases for example) or
|
||||||
|
@ -70,6 +70,9 @@ GET_BAR_CONFIG (6)::
|
||||||
Gets the configuration (as JSON map) of the workspace bar with the
|
Gets the configuration (as JSON map) of the workspace bar with the
|
||||||
given ID. If no ID is provided, an array with all configured bar IDs is
|
given ID. If no ID is provided, an array with all configured bar IDs is
|
||||||
returned instead.
|
returned instead.
|
||||||
|
GET_VERSION (7)::
|
||||||
|
Gets the version of i3. The reply will be a JSON-encoded dictionary
|
||||||
|
with the major, minor, patch and human-readable version.
|
||||||
|
|
||||||
So, a typical message could look like this:
|
So, a typical message could look like this:
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
@ -125,6 +128,8 @@ MARKS (5)::
|
||||||
Reply to the GET_MARKS message.
|
Reply to the GET_MARKS message.
|
||||||
BAR_CONFIG (6)::
|
BAR_CONFIG (6)::
|
||||||
Reply to the GET_BAR_CONFIG message.
|
Reply to the GET_BAR_CONFIG message.
|
||||||
|
VERSION (7)::
|
||||||
|
Reply to the GET_VERSION message.
|
||||||
|
|
||||||
=== COMMAND reply
|
=== COMMAND reply
|
||||||
|
|
||||||
|
@ -206,7 +211,7 @@ default) or whether a JSON parse error occurred.
|
||||||
{ "success": true }
|
{ "success": true }
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
=== GET_OUTPUTS reply
|
=== OUTPUTS reply
|
||||||
|
|
||||||
The reply consists of a serialized list of outputs. Each output has the
|
The reply consists of a serialized list of outputs. Each output has the
|
||||||
following properties:
|
following properties:
|
||||||
|
@ -270,12 +275,15 @@ border (string)::
|
||||||
Can be either "normal", "none" or "1pixel", dependending on the
|
Can be either "normal", "none" or "1pixel", dependending on the
|
||||||
container’s border style.
|
container’s border style.
|
||||||
layout (string)::
|
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
|
Other values might be possible in the future, should we add new
|
||||||
layouts.
|
layouts.
|
||||||
orientation (string)::
|
orientation (string)::
|
||||||
Can be either "none" (for non-split containers), "horizontal" or
|
Can be either "none" (for non-split containers), "horizontal" or
|
||||||
"vertical".
|
"vertical".
|
||||||
|
THIS FIELD IS OBSOLETE. It is still present, but your code should not
|
||||||
|
use it. Instead, rely on the layout field.
|
||||||
percent (float)::
|
percent (float)::
|
||||||
The percentage which this container takes in its parent. A value of
|
The percentage which this container takes in its parent. A value of
|
||||||
+null+ means that the percent property does not make sense for this
|
+null+ means that the percent property does not make sense for this
|
||||||
|
@ -296,6 +304,11 @@ window_rect (map)::
|
||||||
geometry (map)::
|
geometry (map)::
|
||||||
The original geometry the window specified when i3 mapped it. Used when
|
The original geometry the window specified when i3 mapped it. Used when
|
||||||
switching a window to floating mode, for example.
|
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)::
|
urgent (bool)::
|
||||||
Whether this container (window or workspace) has the urgency hint set.
|
Whether this container (window or workspace) has the urgency hint set.
|
||||||
focused (bool)::
|
focused (bool)::
|
||||||
|
@ -526,6 +539,35 @@ urgent_workspace_text/urgent_workspace_bar::
|
||||||
}
|
}
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
=== VERSION reply
|
||||||
|
|
||||||
|
The reply consists of a single JSON dictionary with the following keys:
|
||||||
|
|
||||||
|
major (integer)::
|
||||||
|
The major version of i3, such as +4+.
|
||||||
|
minor (integer)::
|
||||||
|
The minor version of i3, such as +2+. Changes in the IPC interface (new
|
||||||
|
features) will only occur with new minor (or major) releases. However,
|
||||||
|
bugfixes might be introduced in patch releases, too.
|
||||||
|
patch (integer)::
|
||||||
|
The patch version of i3, such as +1+ (when the complete version is
|
||||||
|
+4.2.1+). For versions such as +4.2+, patch will be set to +0+.
|
||||||
|
human_readable (string)::
|
||||||
|
A human-readable version of i3 containing the precise git version,
|
||||||
|
build date and branch name. When you need to display the i3 version to
|
||||||
|
your users, use the human-readable version whenever possible (since
|
||||||
|
this is what +i3 --version+ displays, too).
|
||||||
|
|
||||||
|
*Example:*
|
||||||
|
-------------------
|
||||||
|
{
|
||||||
|
"human_readable" : "4.2-169-gf80b877 (2012-08-05, branch \"next\")",
|
||||||
|
"minor" : 2,
|
||||||
|
"patch" : 0,
|
||||||
|
"major" : 4
|
||||||
|
}
|
||||||
|
-------------------
|
||||||
|
|
||||||
== Events
|
== Events
|
||||||
|
|
||||||
[[events]]
|
[[events]]
|
||||||
|
@ -621,7 +663,7 @@ C::
|
||||||
Ruby::
|
Ruby::
|
||||||
http://github.com/badboy/i3-ipc
|
http://github.com/badboy/i3-ipc
|
||||||
Perl::
|
Perl::
|
||||||
http://search.cpan.org/search?query=AnyEvent::I3
|
https://metacpan.org/module/AnyEvent::I3
|
||||||
Python::
|
Python::
|
||||||
https://github.com/whitelynx/i3ipc
|
* https://github.com/whitelynx/i3ipc
|
||||||
https://github.com/ziberna/i3-py (includes higher-level features)
|
* https://github.com/ziberna/i3-py (includes higher-level features)
|
||||||
|
|
|
@ -42,76 +42,99 @@
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Basics</h2>
|
<h2>Basics</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<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
|
<td>open new terminal
|
||||||
|
|
||||||
<tr>
|
<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
|
<td>focus left
|
||||||
|
|
||||||
<tr>
|
<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
|
<td>focus down
|
||||||
|
|
||||||
<tr>
|
<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
|
<td>focus up
|
||||||
|
|
||||||
<tr>
|
<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
|
<td>focus right
|
||||||
|
<tr>
|
||||||
|
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
|
||||||
|
<td>toggle focus mode
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Changing the container layout</h2>
|
<h2>Moving windows</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>e</kbd>
|
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>j</kbd>
|
||||||
<td>default
|
<td>move window left
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>s</kbd>
|
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>k</kbd>
|
||||||
<td>stacking
|
<td>move window down
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>w</kbd>
|
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>l</kbd>
|
||||||
<td>tabbed
|
<td>move window up
|
||||||
|
<tr>
|
||||||
|
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>;</kbd>
|
||||||
|
<td>move window right
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</div><div>
|
</div><div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Fullscreen mode</h2>
|
<h2>Modifying windows</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<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
|
<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>Changing the container layout</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<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
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>w</kbd>
|
||||||
|
<td>tabbed
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Opening other applications</h2>
|
<h2>Floating</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>d</kbd>
|
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd></kbd>
|
||||||
<td>open application (with dmenu)
|
<td>toggle floating
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Closing windows</h2>
|
|
||||||
<table>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd> </kbd>+ <kbd>q</kbd>
|
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
|
||||||
<td>kill a window
|
<td>drag floating
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -120,17 +143,10 @@
|
||||||
<h2>Using workspaces</h2>
|
<h2>Using workspaces</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<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
|
<td>switch to another workspace
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Moving windows to workspaces</h2>
|
|
||||||
<table>
|
|
||||||
<tr>
|
<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></kbd> + <kbd>0</kbd>-<kbd>9</kbd>
|
||||||
<td>move a window to another workspace
|
<td>move a window to another workspace
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
@ -138,37 +154,33 @@
|
||||||
</div><div>
|
</div><div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Resizing</h2>
|
<h2>Opening applications / Closing windows</h2>
|
||||||
<p class="ref">Look at “Resizing containers / windows” section of the user guide.</p>
|
<table>
|
||||||
|
<tr>
|
||||||
|
<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>q</kbd>
|
||||||
|
<td>kill a window
|
||||||
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Restart / Exit</h2>
|
<h2>Restart / Exit</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>r</kbd>
|
<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
|
<td>restart i3 inplace
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>e</kbd>
|
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>e</kbd>
|
||||||
|
<td>exit i3
|
||||||
</section><td>exit i3
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Floating</h2>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<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>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<!-- footer -->
|
<!-- footer -->
|
||||||
<p id="copyright">
|
<p id="copyright">
|
||||||
|
@ -176,7 +188,7 @@
|
||||||
<br />
|
<br />
|
||||||
All rights reserved
|
All rights reserved
|
||||||
<br />
|
<br />
|
||||||
Designed by Zeus Panchenko
|
Designed by Zeus Panchenko, updated by Moritz Bandemer
|
||||||
</p>
|
</p>
|
||||||
<p id="licence">
|
<p id="licence">
|
||||||
Permission is granted to copy, distribute and/or modify this document provided
|
Permission is granted to copy, distribute and/or modify this document provided
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
i3 testsuite
|
i3 testsuite
|
||||||
============
|
============
|
||||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
September 2011
|
September 2012
|
||||||
|
|
||||||
This document explains how the i3 testsuite works, how to use it and extend it.
|
This document explains how the i3 testsuite works, how to use it and extend it.
|
||||||
It is targeted at developers who not necessarily have been doing testing before
|
It is targeted at developers who not necessarily have been doing testing before
|
||||||
|
@ -33,6 +33,19 @@ able to easily test if the feature is working correctly. Many developers will
|
||||||
test manually if everything works. Having a testcase not only helps you with
|
test manually if everything works. Having a testcase not only helps you with
|
||||||
that, but it will also be useful for every future change.
|
that, but it will also be useful for every future change.
|
||||||
|
|
||||||
|
== Relevant documentation
|
||||||
|
|
||||||
|
Apart from this document, you should also have a look at:
|
||||||
|
|
||||||
|
1. The "Modern Perl" book, which can be found at
|
||||||
|
http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
|
||||||
|
2. The latest Perl documentation of the "i3test" (general testcase setup) and
|
||||||
|
"i3test::Test" (additional test instructions) modules:
|
||||||
|
http://build.i3wm.org/docs/lib-i3test.html respectively
|
||||||
|
http://build.i3wm.org/docs/lib-i3test-test.html
|
||||||
|
3. The latest documentation on i3’s IPC interface:
|
||||||
|
http://build.i3wm.org/docs/ipc.html
|
||||||
|
|
||||||
== Implementation
|
== Implementation
|
||||||
|
|
||||||
For several reasons, the i3 testsuite has been implemented in Perl:
|
For several reasons, the i3 testsuite has been implemented in Perl:
|
||||||
|
@ -45,6 +58,8 @@ For several reasons, the i3 testsuite has been implemented in Perl:
|
||||||
|
|
||||||
2. Perl is widely available and has a well-working package infrastructure.
|
2. Perl is widely available and has a well-working package infrastructure.
|
||||||
3. The author is familiar with Perl :).
|
3. The author is familiar with Perl :).
|
||||||
|
4. It is a good idea to use a different language for the tests than the
|
||||||
|
implementation itself.
|
||||||
|
|
||||||
Please do not start programming language flamewars at this point.
|
Please do not start programming language flamewars at this point.
|
||||||
|
|
||||||
|
|
200
docs/userguide
200
docs/userguide
|
@ -1,11 +1,11 @@
|
||||||
i3 User’s Guide
|
i3 User’s Guide
|
||||||
===============
|
===============
|
||||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
April 2012
|
August 2012
|
||||||
|
|
||||||
This document contains all the information you need to configure and use the i3
|
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
|
window manager. If it does not, please check http://faq.i3wm.org/ first, then
|
||||||
question(s) on the mailing list.
|
contact us on IRC (preferred) or post your question(s) on the mailing list.
|
||||||
|
|
||||||
== Default keybindings
|
== Default keybindings
|
||||||
|
|
||||||
|
@ -61,16 +61,18 @@ windows.
|
||||||
|
|
||||||
TODO: picture of the tree
|
TODO: picture of the tree
|
||||||
|
|
||||||
To split a window vertically, press +mod+v+. To split it horizontally, press
|
To split a window vertically, press +mod+v+ before you create the new window.
|
||||||
+mod+h+.
|
To split it horizontally, press +mod+h+.
|
||||||
|
|
||||||
=== Changing the container layout
|
=== Changing the container layout
|
||||||
|
|
||||||
A split container can have one of the following layouts:
|
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
|
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::
|
stacking::
|
||||||
Only the focused window in the container is displayed. You get a list of
|
Only the focused window in the container is displayed. You get a list of
|
||||||
windows at the top of the container.
|
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
|
The same principle as +stacking+, but the list of windows at the top is only
|
||||||
a single line which is vertically split.
|
a single line which is vertically split.
|
||||||
|
|
||||||
To switch modes, press +mod+e+ for default, +mod+s+ for stacking and
|
To switch modes, press +mod+e+ for splith/splitv (it toggles), +mod+s+ for
|
||||||
+mod+w+ for tabbed.
|
stacking and +mod+w+ for tabbed.
|
||||||
|
|
||||||
image:modes.png[Container modes]
|
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
|
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
|
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
|
orientation (horizontal, vertical or unspecified) and the orientation depends
|
||||||
workspace, the default orientation of the workspace +Container+ is horizontal
|
on the layout the container is in (vertical for splitv and stacking, horizontal
|
||||||
(most monitors are widescreen nowadays). If you change the orientation to
|
for splith and tabbed). So, in our example with the workspace, the default
|
||||||
vertical (+mod+v+ in the default config) and *then* open two terminals, i3 will
|
layout of the workspace +Container+ is splith (most monitors are widescreen
|
||||||
configure your windows like this:
|
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"]
|
image::tree-shot2.png["shot2",title="Vertical Workspace Orientation"]
|
||||||
|
|
||||||
An interesting new feature of the tree branch is the ability to split anything:
|
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 horizontal
|
Let’s assume you have two terminals on a workspace (with splith layout, that is
|
||||||
orientation), focus is on the right terminal. Now you want to open another
|
horizontal orientation), focus is on the right terminal. Now you want to open
|
||||||
terminal window below the current one. If you would just open a new terminal
|
another terminal window below the current one. If you would just open a new
|
||||||
window, it would show up to the right due to the horizontal workspace
|
terminal window, it would show up to the right due to the splith layout.
|
||||||
orientation. Instead, press +mod+v+ to create a +Vertical Split Container+ (to
|
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
|
open a +Horizontal Split Container+, use +mod+h+). Now you can open a new
|
||||||
terminal and it will open below the current one:
|
terminal and it will open below the current one:
|
||||||
|
|
||||||
|
@ -291,11 +294,21 @@ a # and can only be used at the beginning of a line:
|
||||||
# This is a comment
|
# This is a comment
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
[[fonts]]
|
||||||
|
|
||||||
=== Fonts
|
=== Fonts
|
||||||
|
|
||||||
i3 uses X core fonts (not Xft) for rendering window titles. You can use
|
i3 has support for both X core fonts and FreeType fonts (through Pango) to
|
||||||
+xfontsel(1)+ to generate such a font description. To see special characters
|
render window titles.
|
||||||
(Unicode), you need to use a font which supports the ISO-10646 encoding.
|
|
||||||
|
To generate an X core font description, you can use +xfontsel(1)+. To see
|
||||||
|
special characters (Unicode), you need to use a font which supports the
|
||||||
|
ISO-10646 encoding.
|
||||||
|
|
||||||
|
A FreeType font description is composed by a font family, a style, a weight,
|
||||||
|
a variant, a stretch and a size.
|
||||||
|
FreeType fonts support right-to-left rendering and contain often more
|
||||||
|
Unicode glyphs than X core fonts.
|
||||||
|
|
||||||
If i3 cannot open the configured font, it will output an error in the logfile
|
If i3 cannot open the configured font, it will output an error in the logfile
|
||||||
and fall back to a working font.
|
and fall back to a working font.
|
||||||
|
@ -303,11 +316,13 @@ and fall back to a working font.
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
------------------------------
|
------------------------------
|
||||||
font <X core font description>
|
font <X core font description>
|
||||||
|
font xft:<a FreeType font description>
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
|
font xft:DejaVu Sans Mono 10
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
[[keybindings]]
|
[[keybindings]]
|
||||||
|
@ -333,10 +348,15 @@ your bindings in the same physical location on the keyboard, use keycodes.
|
||||||
If you don’t switch layouts, and want a clean and simple config file, use
|
If you don’t switch layouts, and want a clean and simple config file, use
|
||||||
keysyms.
|
keysyms.
|
||||||
|
|
||||||
|
Some tools (such as +import+ or +xdotool+) might be unable to run upon a
|
||||||
|
KeyPress event, because the keyboard/pointer is still grabbed. For these
|
||||||
|
situations, the +--release+ flag can be used, which will execute the command
|
||||||
|
after the keys have been released.
|
||||||
|
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
----------------------------------
|
----------------------------------
|
||||||
bindsym [Modifiers+]keysym command
|
bindsym [--release] [Modifiers+]keysym command
|
||||||
bindcode [Modifiers+]keycode command
|
bindcode [--release] [Modifiers+]keycode command
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
|
@ -348,7 +368,13 @@ bindsym mod+f fullscreen
|
||||||
bindsym mod+Shift+r restart
|
bindsym mod+Shift+r restart
|
||||||
|
|
||||||
# Notebook-specific hotkeys
|
# Notebook-specific hotkeys
|
||||||
bindcode 214 exec /home/michael/toggle_beamer.sh
|
bindcode 214 exec --no-startup-id /home/michael/toggle_beamer.sh
|
||||||
|
|
||||||
|
# Simulate ctrl+v upon pressing $mod+x
|
||||||
|
bindsym --release $mod+x exec --no-startup-id xdotool key --clearmodifiers ctrl+v
|
||||||
|
|
||||||
|
# Take a screenshot upon pressing $mod+x (select an area)
|
||||||
|
bindsym --release $mod+x exec --no-startup-id import /tmp/latest-screenshot.png
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
Available Modifiers:
|
Available Modifiers:
|
||||||
|
@ -465,6 +491,22 @@ new_window <normal|1pixel|none>
|
||||||
new_window 1pixel
|
new_window 1pixel
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
=== Hiding vertical borders
|
||||||
|
|
||||||
|
You can hide vertical borders adjacent to the screen edges using
|
||||||
|
+hide_edge_borders+. This is useful if you are using scrollbars, or do not want
|
||||||
|
to waste even two pixels in displayspace. Default is none.
|
||||||
|
|
||||||
|
*Syntax*:
|
||||||
|
----------------------------
|
||||||
|
hide_edge_borders <none|vertical|horizontal|both>
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
*Example*:
|
||||||
|
----------------------
|
||||||
|
hide_edge_borders vertical
|
||||||
|
----------------------
|
||||||
|
|
||||||
=== Arbitrary commands for specific windows (for_window)
|
=== Arbitrary commands for specific windows (for_window)
|
||||||
|
|
||||||
With the +for_window+ command, you can let i3 execute any command when it
|
With the +for_window+ command, you can let i3 execute any command when it
|
||||||
|
@ -573,6 +615,22 @@ logfile first (see http://i3wm.org/docs/debugging.html). It includes more
|
||||||
details about the matching process and the window’s actual class, instance and
|
details about the matching process and the window’s actual class, instance and
|
||||||
title when starting up.
|
title when starting up.
|
||||||
|
|
||||||
|
Note that if you want to start an application just once on a specific
|
||||||
|
workspace, but you don’t want to assign all instances of it permanently, you
|
||||||
|
can make use of i3’s startup-notification support (see <<exec>>) in your config
|
||||||
|
file in the following way:
|
||||||
|
|
||||||
|
*Start iceweasel on workspace 3 (once)*:
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
# Start iceweasel on workspace 3, then switch back to workspace 1
|
||||||
|
# (Being a command-line utility, i3-msg does not support startup notifications,
|
||||||
|
# hence the exec --no-startup-id.)
|
||||||
|
# (Starting iceweasel with i3’s exec command is important in order to make i3
|
||||||
|
# create a startup notification context, without which the iceweasel window(s)
|
||||||
|
# cannot be matched onto the workspace on which the command was started.)
|
||||||
|
exec --no-startup-id i3-msg 'workspace 3; exec iceweasel; workspace 1'
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
=== Automatically starting applications on i3 startup
|
=== Automatically starting applications on i3 startup
|
||||||
|
|
||||||
By using the +exec+ keyword outside a keybinding, you can configure
|
By using the +exec+ keyword outside a keybinding, you can configure
|
||||||
|
@ -1010,8 +1068,7 @@ xrandr --output <output> --primary
|
||||||
|
|
||||||
=== Font
|
=== Font
|
||||||
|
|
||||||
Specifies the font (again, X core font, not Xft, just like in i3) to be used in
|
Specifies the font to be used in the bar. See <<fonts>>.
|
||||||
the bar.
|
|
||||||
|
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -1022,6 +1079,7 @@ font <font>
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
bar {
|
bar {
|
||||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
|
font xft:DejaVu Sans Mono 10
|
||||||
}
|
}
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -1190,13 +1248,15 @@ cursor for 60 seconds.
|
||||||
=== Splitting containers
|
=== Splitting containers
|
||||||
|
|
||||||
The split command makes the current window a split container. Split 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
|
can contain multiple windows. Depending on the layout of the split container,
|
||||||
either split horizontally (a new window gets placed to the right of the current
|
new windows get placed to the right of the current one (splith) or new windows
|
||||||
one) or vertically (a new window gets placed below the current one).
|
get placed below the current one (splitv).
|
||||||
|
|
||||||
If you apply this command to a split container with the same orientation,
|
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
|
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*:
|
*Syntax*:
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -1211,19 +1271,32 @@ bindsym mod+h split horizontal
|
||||||
|
|
||||||
=== Manipulating layout
|
=== Manipulating layout
|
||||||
|
|
||||||
Use +layout default+, +layout stacking+ or +layout tabbed+ to change the
|
Use +layout toggle split+, +layout stacking+ or +layout tabbed+ to change the
|
||||||
current container layout to default, stacking or tabbed layout, respectively.
|
current container layout to splith/splitv, stacking or tabbed layout,
|
||||||
|
respectively.
|
||||||
|
|
||||||
To make the current window (!) fullscreen, use +fullscreen+, to make
|
To make the current window (!) fullscreen, use +fullscreen+, to make
|
||||||
it floating (or tiling again) use +floating enable+ respectively +floating disable+
|
it floating (or tiling again) use +floating enable+ respectively +floating disable+
|
||||||
(or +floating toggle+):
|
(or +floating toggle+):
|
||||||
|
|
||||||
|
*Syntax*:
|
||||||
|
--------------
|
||||||
|
layout <tabbed|stacking>
|
||||||
|
layout toggle [split|all]
|
||||||
|
--------------
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
--------------
|
--------------
|
||||||
bindsym mod+s layout stacking
|
bindsym mod+s layout stacking
|
||||||
bindsym mod+l layout default
|
bindsym mod+l layout toggle split
|
||||||
bindsym mod+w layout tabbed
|
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
|
# Toggle fullscreen
|
||||||
bindsym mod+f fullscreen
|
bindsym mod+f fullscreen
|
||||||
|
|
||||||
|
@ -1313,22 +1386,28 @@ 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
|
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
|
combination. To restrict those to the current output, use +workspace
|
||||||
next_on_output+ and +workspace prev_on_output+. Similarly, you can use +move
|
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 workspace next+, +move container to workspace prev+ to move a
|
||||||
container to the next/previous workspace.
|
container to the next/previous workspace and +move container to workspace current+
|
||||||
|
(the last one makes sense only when used with criteria).
|
||||||
|
|
||||||
|
See <<move_to_outputs>> for how to move a container/workspace to a different
|
||||||
|
RandR output.
|
||||||
|
|
||||||
[[back_and_forth]]
|
[[back_and_forth]]
|
||||||
To switch back to the previously focused workspace, use +workspace
|
To switch back to the previously focused workspace, use +workspace
|
||||||
back_and_forth+.
|
back_and_forth+.
|
||||||
|
|
||||||
To move a container to another xrandr output such as +LVDS1+ or +VGA1+, you can
|
*Syntax*:
|
||||||
use the +move container to output+ command followed by the name of the target
|
-----------------------------------
|
||||||
output. You may also use +left+, +right+, +up+, +down+ instead of the xrandr
|
workspace <next|prev|next_on_output|prev_on_output>
|
||||||
output name to move to the next output in the specified direction.
|
workspace back_and_forth
|
||||||
|
workspace <name>
|
||||||
|
workspace number <number>
|
||||||
|
|
||||||
To move a whole workspace to another xrandr output such as +LVDS1+ or +VGA1+,
|
move [window|container] [to] workspace <name>
|
||||||
you can use the +move workspace to output+ command followed by the name of the
|
move [window|container] [to] workspace number <number>
|
||||||
target output. You may also use +left+, +right+, +up+, +down+ instead of the
|
move [window|container] [to] workspace <prev|next|current>
|
||||||
xrandr output name to move to the next output in the specified direction.
|
-----------------------------------
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
-------------------------
|
-------------------------
|
||||||
|
@ -1345,6 +1424,9 @@ bindsym mod+b workspace back_and_forth
|
||||||
|
|
||||||
# move the whole workspace to the next output
|
# move the whole workspace to the next output
|
||||||
bindsym mod+x move workspace to output right
|
bindsym mod+x move workspace to output right
|
||||||
|
|
||||||
|
# move firefox to current workspace
|
||||||
|
bindsym mod+F1 [class="Firefox"] move workspace current
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
==== Named workspaces
|
==== Named workspaces
|
||||||
|
@ -1376,7 +1458,7 @@ to switch to the workspace which begins with number 1, regardless of which name
|
||||||
it has. This is useful in case you are changing the workspace’s name
|
it has. This is useful in case you are changing the workspace’s name
|
||||||
dynamically.
|
dynamically.
|
||||||
|
|
||||||
=== Renaming workspaces
|
==== Renaming workspaces
|
||||||
|
|
||||||
You can rename workspaces. This might be useful to start with the default
|
You can rename workspaces. This might be useful to start with the default
|
||||||
numbered workspaces, do your work, and rename the workspaces afterwards to
|
numbered workspaces, do your work, and rename the workspaces afterwards to
|
||||||
|
@ -1394,6 +1476,30 @@ i3-msg 'rename workspace 1 to "1: www"'
|
||||||
i3-msg 'rename workspace "1: www" to "10: www"'
|
i3-msg 'rename workspace "1: www" to "10: www"'
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
|
=== Moving containers/workspaces to RandR outputs
|
||||||
|
|
||||||
|
[[move_to_outputs]]
|
||||||
|
|
||||||
|
To move a container to another RandR output (addressed by names like +LVDS1+ or
|
||||||
|
+VGA1+) or to a RandR output identified by a specific direction (like +left+,
|
||||||
|
+right+, +up+ or +down+), there are two commands:
|
||||||
|
|
||||||
|
*Syntax*:
|
||||||
|
--------------------------------------------------------
|
||||||
|
move container to output <<left|right|down|up>|<output>>
|
||||||
|
move workspace to output <<left|right|down|up>|<output>>
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
*Examples*:
|
||||||
|
--------------------------------------------------------
|
||||||
|
# Move the current workspace to the next output
|
||||||
|
# (effectively toggles when you only have two outputs)
|
||||||
|
bindsym mod+x move workspace to output right
|
||||||
|
|
||||||
|
# Put this window on the presentation output.
|
||||||
|
bindsym mod+x move container to output VGA1
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
[[resizingconfig]]
|
[[resizingconfig]]
|
||||||
|
|
||||||
=== Resizing containers/windows
|
=== Resizing containers/windows
|
||||||
|
|
|
@ -152,7 +152,7 @@ for my $state (@keys) {
|
||||||
$cmd =~ s/[^(]+\(//;
|
$cmd =~ s/[^(]+\(//;
|
||||||
$cmd =~ s/\)$//;
|
$cmd =~ s/\)$//;
|
||||||
$cmd = ", $cmd" if length($cmd) > 0;
|
$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 '#endif';
|
||||||
say $callfh " state = $next_state;";
|
say $callfh " state = $next_state;";
|
||||||
say $callfh " break;";
|
say $callfh " break;";
|
||||||
|
|
|
@ -1,47 +1,10 @@
|
||||||
# Default value so one can compile i3-input standalone
|
all:
|
||||||
TOPDIR=..
|
$(MAKE) -C .. i3-config-wizard/i3-config-wizard
|
||||||
|
|
||||||
include $(TOPDIR)/common.mk
|
install:
|
||||||
|
$(MAKE) -C .. install-i3-config-wizard
|
||||||
# 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/
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o cfgparse.tab.{c,h} cfgparse.output cfgparse.yy.c
|
$(MAKE) -C .. clean-i3-config-wizard
|
||||||
|
|
||||||
distclean: clean
|
.PHONY: all install clean
|
||||||
rm -f i3-config-wizard
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ extern FILE *yyin;
|
||||||
YY_BUFFER_STATE yy_scan_string(const char *);
|
YY_BUFFER_STATE yy_scan_string(const char *);
|
||||||
|
|
||||||
static struct context *context;
|
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
|
/* 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
|
* 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 *rewrite_binding(const char *bindingline) {
|
||||||
char *result = NULL;
|
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);
|
context = calloc(sizeof(struct context), 1);
|
||||||
|
|
||||||
yy_scan_string(bindingline);
|
yy_scan_string(bindingline);
|
||||||
|
@ -81,6 +90,8 @@ char *rewrite_binding(const char *bindingline) {
|
||||||
if (context->line_copy)
|
if (context->line_copy)
|
||||||
free(context->line_copy);
|
free(context->line_copy);
|
||||||
free(context);
|
free(context);
|
||||||
|
xcb_key_symbols_free(keysyms);
|
||||||
|
xcb_disconnect(conn);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -101,6 +112,28 @@ static char *modifier_to_string(int modifiers) {
|
||||||
else return strdup("");
|
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
|
%error-verbose
|
||||||
|
@ -139,11 +172,22 @@ bindcode:
|
||||||
* different key than the upper-case one (unlikely for letters, but
|
* different key than the upper-case one (unlikely for letters, but
|
||||||
* more likely for special characters). */
|
* more likely for special characters). */
|
||||||
level = 1;
|
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);
|
KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, level);
|
||||||
char *str = XKeysymToString(sym);
|
char *str = XKeysymToString(sym);
|
||||||
char *modifiers = modifier_to_string($<number>3);
|
char *modifiers = modifier_to_string($<number>3);
|
||||||
// TODO: modifier to string
|
|
||||||
sasprintf(&(context->result), "bindsym %s%s %s\n", modifiers, str, $<string>6);
|
sasprintf(&(context->result), "bindsym %s%s %s\n", modifiers, str, $<string>6);
|
||||||
free(modifiers);
|
free(modifiers);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) $(PANGO_CFLAGS)
|
||||||
|
i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_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) $(XCB_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,$^) $(LIBS) $(i3_config_wizard_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,tab.h,y.o}
|
|
@ -69,6 +69,7 @@ enum { MOD_Mod1, MOD_Mod4 } modifier = MOD_Mod4;
|
||||||
static char *config_path;
|
static char *config_path;
|
||||||
static uint32_t xcb_numlock_mask;
|
static uint32_t xcb_numlock_mask;
|
||||||
xcb_connection_t *conn;
|
xcb_connection_t *conn;
|
||||||
|
xcb_screen_t *root_screen;
|
||||||
static xcb_get_modifier_mapping_reply_t *modmap_reply;
|
static xcb_get_modifier_mapping_reply_t *modmap_reply;
|
||||||
static i3Font font;
|
static i3Font font;
|
||||||
static i3Font bold_font;
|
static i3Font bold_font;
|
||||||
|
@ -83,6 +84,26 @@ Display *dpy;
|
||||||
char *rewrite_binding(const char *bindingline);
|
char *rewrite_binding(const char *bindingline);
|
||||||
static void finish();
|
static void finish();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Having verboselog() and errorlog() is necessary when using libi3.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void verboselog(char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stdout, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void errorlog(char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stderr, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function resolves ~ in pathnames.
|
* This function resolves ~ in pathnames.
|
||||||
* It may resolve wildcards in the first part of the path, but if no match
|
* It may resolve wildcards in the first part of the path, but if no match
|
||||||
|
@ -128,7 +149,7 @@ static int handle_expose() {
|
||||||
set_font(&font);
|
set_font(&font);
|
||||||
|
|
||||||
#define txt(x, row, text) \
|
#define txt(x, row, text) \
|
||||||
draw_text(text, strlen(text), false, pixmap, pixmap_gc,\
|
draw_text_ascii(text, pixmap, pixmap_gc,\
|
||||||
x, (row - 1) * font.height + 4, 300 - x * 2)
|
x, (row - 1) * font.height + 4, 300 - x * 2)
|
||||||
|
|
||||||
if (current_step == STEP_WELCOME) {
|
if (current_step == STEP_WELCOME) {
|
||||||
|
@ -456,7 +477,7 @@ int main(int argc, char *argv[]) {
|
||||||
#include "atoms.xmacro"
|
#include "atoms.xmacro"
|
||||||
#undef xmacro
|
#undef xmacro
|
||||||
|
|
||||||
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
|
root_screen = xcb_aux_get_screen(conn, screens);
|
||||||
root = root_screen->root;
|
root = root_screen->root;
|
||||||
|
|
||||||
if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL)))
|
if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL)))
|
||||||
|
|
|
@ -1,32 +1,10 @@
|
||||||
# Default value so one can compile i3-dump-log standalone
|
all:
|
||||||
TOPDIR=..
|
$(MAKE) -C .. i3-dump-log/i3-dump-log
|
||||||
|
|
||||||
include $(TOPDIR)/common.mk
|
install:
|
||||||
|
$(MAKE) -C .. install-i3-dump-log
|
||||||
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/
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o
|
$(MAKE) -C .. clean-i3-dump-log
|
||||||
|
|
||||||
distclean: clean
|
.PHONY: all install clean
|
||||||
rm -f i3-dump-log
|
|
||||||
|
|
|
@ -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) $(PANGO_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) $(XCB_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
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* i3-dump-log/main.c: Dumps the i3 SHM log to stdout.
|
* i3-dump-log/main.c: Dumps the i3 SHM log to stdout.
|
||||||
*
|
*
|
||||||
|
@ -28,18 +28,47 @@
|
||||||
#include "shmlog.h"
|
#include "shmlog.h"
|
||||||
#include <i3/ipc.h>
|
#include <i3/ipc.h>
|
||||||
|
|
||||||
|
static uint32_t offset_next_write,
|
||||||
|
wrap_count;
|
||||||
|
|
||||||
|
static i3_shmlog_header *header;
|
||||||
|
static char *logbuffer,
|
||||||
|
*walk;
|
||||||
|
|
||||||
|
static int check_for_wrap(void) {
|
||||||
|
if (wrap_count == header->wrap_count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* The log wrapped. Print the remaining content and reset walk to the top
|
||||||
|
* of the log. */
|
||||||
|
wrap_count = header->wrap_count;
|
||||||
|
write(STDOUT_FILENO, walk, ((logbuffer + header->offset_last_wrap) - walk));
|
||||||
|
walk = logbuffer + sizeof(i3_shmlog_header);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_till_end(void) {
|
||||||
|
check_for_wrap();
|
||||||
|
int n = write(STDOUT_FILENO, walk, ((logbuffer + header->offset_next_write) - walk));
|
||||||
|
if (n > 0) {
|
||||||
|
walk += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int o, option_index = 0;
|
int o, option_index = 0;
|
||||||
bool verbose = false;
|
bool verbose = false,
|
||||||
|
follow = false;
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"version", no_argument, 0, 'v'},
|
{"version", no_argument, 0, 'v'},
|
||||||
{"verbose", no_argument, 0, 'V'},
|
{"verbose", no_argument, 0, 'V'},
|
||||||
|
{"follow", no_argument, 0, 'f'},
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
char *options_string = "s:vVh";
|
char *options_string = "s:vfVh";
|
||||||
|
|
||||||
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
|
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
|
||||||
if (o == 'v') {
|
if (o == 'v') {
|
||||||
|
@ -47,9 +76,11 @@ int main(int argc, char *argv[]) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (o == 'V') {
|
} else if (o == 'V') {
|
||||||
verbose = true;
|
verbose = true;
|
||||||
|
} else if (o == 'f') {
|
||||||
|
follow = true;
|
||||||
} else if (o == 'h') {
|
} else if (o == 'h') {
|
||||||
printf("i3-dump-log " I3_VERSION "\n");
|
printf("i3-dump-log " I3_VERSION "\n");
|
||||||
printf("i3-dump-log [-s <socket>]\n");
|
printf("i3-dump-log [-f] [-s <socket>]\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,45 +121,61 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
|
|
||||||
int logbuffer_shm = shm_open(shmname, O_RDONLY, 0);
|
/* NB: While we must never read, we need O_RDWR for the pthread condvar. */
|
||||||
|
int logbuffer_shm = shm_open(shmname, O_RDWR, 0);
|
||||||
if (logbuffer_shm == -1)
|
if (logbuffer_shm == -1)
|
||||||
err(EXIT_FAILURE, "Could not shm_open SHM segment for the i3 log (%s)", shmname);
|
err(EXIT_FAILURE, "Could not shm_open SHM segment for the i3 log (%s)", shmname);
|
||||||
|
|
||||||
if (fstat(logbuffer_shm, &statbuf) != 0)
|
if (fstat(logbuffer_shm, &statbuf) != 0)
|
||||||
err(EXIT_FAILURE, "stat(%s)", shmname);
|
err(EXIT_FAILURE, "stat(%s)", shmname);
|
||||||
|
|
||||||
char *logbuffer = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, logbuffer_shm, 0);
|
/* NB: While we must never read, we need O_RDWR for the pthread condvar. */
|
||||||
|
logbuffer = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
|
||||||
if (logbuffer == MAP_FAILED)
|
if (logbuffer == MAP_FAILED)
|
||||||
err(EXIT_FAILURE, "Could not mmap SHM segment for the i3 log");
|
err(EXIT_FAILURE, "Could not mmap SHM segment for the i3 log");
|
||||||
|
|
||||||
i3_shmlog_header *header = (i3_shmlog_header*)logbuffer;
|
header = (i3_shmlog_header*)logbuffer;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n",
|
printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n",
|
||||||
header->offset_next_write, header->offset_last_wrap, header->size, shmname);
|
header->offset_next_write, header->offset_last_wrap, header->size, shmname);
|
||||||
int chars;
|
walk = logbuffer + header->offset_next_write;
|
||||||
char *walk = logbuffer + header->offset_next_write;
|
|
||||||
/* Skip the first line, it very likely is mangled. Not a problem, though,
|
|
||||||
* the log is chatty enough to have plenty lines left. */
|
|
||||||
while (*walk != '\0')
|
|
||||||
walk++;
|
|
||||||
|
|
||||||
/* Print the oldest log lines. We use printf("%s") to stop on \0. */
|
/* We first need to print old content in case there was at least one
|
||||||
while (walk < (logbuffer + header->offset_last_wrap)) {
|
* wrapping already. */
|
||||||
chars = printf("%s", walk);
|
|
||||||
/* Shortcut: If there are two consecutive \0 bytes, this part of the
|
if (*walk != '\0') {
|
||||||
* buffer was never touched. To not call printf() for every byte of the
|
/* In case there was a write to the buffer already, skip the first
|
||||||
* buffer, we directly exit the loop. */
|
* old line, it very likely is mangled. Not a problem, though, the log
|
||||||
if (*walk == '\0' && *(walk+1) == '\0')
|
* is chatty enough to have plenty lines left. */
|
||||||
break;
|
while (*walk != '\n')
|
||||||
walk += (chars > 0 ? chars : 1);
|
walk++;
|
||||||
|
walk++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* In case there was no wrapping, this is a no-op, otherwise it prints the
|
||||||
|
* old lines. */
|
||||||
|
wrap_count = 0;
|
||||||
|
check_for_wrap();
|
||||||
|
|
||||||
/* Then start from the beginning and print the newer lines */
|
/* Then start from the beginning and print the newer lines */
|
||||||
walk = logbuffer + sizeof(i3_shmlog_header);
|
walk = logbuffer + sizeof(i3_shmlog_header);
|
||||||
while (walk < (logbuffer + header->offset_next_write)) {
|
print_till_end();
|
||||||
chars = printf("%s", walk);
|
|
||||||
walk += (chars > 0 ? chars : 1);
|
if (follow) {
|
||||||
|
/* Since pthread_cond_wait() expects a mutex, we need to provide one.
|
||||||
|
* To not lock i3 (that’s bad, mhkay?) we just define one outside of
|
||||||
|
* the shared memory. */
|
||||||
|
pthread_mutex_t dummy_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
pthread_mutex_lock(&dummy_mutex);
|
||||||
|
while (1) {
|
||||||
|
pthread_cond_wait(&(header->condvar), &dummy_mutex);
|
||||||
|
/* If this was not a spurious wakeup, print the new lines. */
|
||||||
|
if (header->offset_next_write != offset_next_write) {
|
||||||
|
offset_next_write = header->offset_next_write;
|
||||||
|
print_till_end();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,35 +1,10 @@
|
||||||
# Default value so one can compile i3-input standalone
|
all:
|
||||||
TOPDIR=..
|
$(MAKE) -C .. i3-input/i3-input
|
||||||
|
|
||||||
include $(TOPDIR)/common.mk
|
install:
|
||||||
|
$(MAKE) -C .. install-i3-input
|
||||||
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/
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o
|
$(MAKE) -C .. clean-i3-input
|
||||||
|
|
||||||
distclean: clean
|
.PHONY: all install clean
|
||||||
rm -f i3-input
|
|
||||||
|
|
|
@ -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) $(PANGO_CFLAGS)
|
||||||
|
i3_input_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_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) $(XCB_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,$^) $(LIBS) $(i3_input_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
|
|
@ -45,15 +45,36 @@ static bool modeswitch_active = false;
|
||||||
static xcb_window_t win;
|
static xcb_window_t win;
|
||||||
static xcb_pixmap_t pixmap;
|
static xcb_pixmap_t pixmap;
|
||||||
static xcb_gcontext_t pixmap_gc;
|
static xcb_gcontext_t pixmap_gc;
|
||||||
static char *glyphs_ucs[512];
|
static xcb_char2b_t glyphs_ucs[512];
|
||||||
static char *glyphs_utf8[512];
|
static char *glyphs_utf8[512];
|
||||||
static int input_position;
|
static int input_position;
|
||||||
static i3Font font;
|
static i3Font font;
|
||||||
static char *prompt;
|
static i3String *prompt;
|
||||||
static size_t prompt_len;
|
static int prompt_offset = 0;
|
||||||
static int limit;
|
static int limit;
|
||||||
xcb_window_t root;
|
xcb_window_t root;
|
||||||
xcb_connection_t *conn;
|
xcb_connection_t *conn;
|
||||||
|
xcb_screen_t *root_screen;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Having verboselog() and errorlog() is necessary when using libi3.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void verboselog(char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stdout, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void errorlog(char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stderr, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for
|
* Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for
|
||||||
|
@ -96,25 +117,21 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
|
||||||
/* restore font color */
|
/* restore font color */
|
||||||
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
|
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
|
||||||
|
|
||||||
/* draw the text */
|
/* draw the prompt … */
|
||||||
uint8_t *con = concat_strings(glyphs_ucs, input_position);
|
|
||||||
char *full_text = (char*)con;
|
|
||||||
if (prompt != NULL) {
|
if (prompt != NULL) {
|
||||||
full_text = malloc((prompt_len + input_position) * 2 + 1);
|
draw_text(prompt, pixmap, pixmap_gc, 4, 4, 492);
|
||||||
if (full_text == NULL)
|
}
|
||||||
err(EXIT_FAILURE, "malloc() failed\n");
|
/* … and the text */
|
||||||
memcpy(full_text, prompt, prompt_len * 2);
|
if (input_position > 0)
|
||||||
memcpy(full_text + (prompt_len * 2), con, input_position * 2);
|
{
|
||||||
|
i3String *input = i3string_from_ucs2(glyphs_ucs, input_position);
|
||||||
|
draw_text(input, pixmap, pixmap_gc, prompt_offset + 4, 4, 492);
|
||||||
|
i3string_free(input);
|
||||||
}
|
}
|
||||||
if (input_position + prompt_len != 0)
|
|
||||||
draw_text(full_text, input_position + prompt_len, true, pixmap, pixmap_gc, 4, 4, 492);
|
|
||||||
|
|
||||||
/* Copy the contents of the pixmap to the real window */
|
/* Copy the contents of the pixmap to the real window */
|
||||||
xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8);
|
xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
free(con);
|
|
||||||
if (prompt != NULL)
|
|
||||||
free(full_text);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -126,16 +143,6 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
|
||||||
static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_event_t *event) {
|
static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_event_t *event) {
|
||||||
printf("releasing %d, state raw = %d\n", event->detail, event->state);
|
printf("releasing %d, state raw = %d\n", event->detail, event->state);
|
||||||
|
|
||||||
/* See the documentation of xcb_key_symbols_get_keysym for this one.
|
|
||||||
* Basically: We get either col 0 or col 1, depending on whether shift is
|
|
||||||
* pressed. */
|
|
||||||
int col = (event->state & XCB_MOD_MASK_SHIFT);
|
|
||||||
|
|
||||||
/* If modeswitch is currently active, we need to look in group 2 or 3,
|
|
||||||
* respectively. */
|
|
||||||
if (modeswitch_active)
|
|
||||||
col += 2;
|
|
||||||
|
|
||||||
xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, event->state);
|
xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, event->state);
|
||||||
if (sym == XK_Mode_switch) {
|
if (sym == XK_Mode_switch) {
|
||||||
printf("Mode switch disabled\n");
|
printf("Mode switch disabled\n");
|
||||||
|
@ -226,7 +233,6 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
input_position--;
|
input_position--;
|
||||||
free(glyphs_ucs[input_position]);
|
|
||||||
free(glyphs_utf8[input_position]);
|
free(glyphs_utf8[input_position]);
|
||||||
|
|
||||||
handle_expose(NULL, conn, NULL);
|
handle_expose(NULL, conn, NULL);
|
||||||
|
@ -257,18 +263,16 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* store the UCS into a string */
|
xcb_char2b_t inp;
|
||||||
uint8_t inp[3] = {(ucs & 0xFF00) >> 8, (ucs & 0xFF), 0};
|
inp.byte1 = ( ucs & 0xff00 ) >> 2;
|
||||||
|
inp.byte2 = ( ucs & 0x00ff ) >> 0;
|
||||||
|
|
||||||
printf("inp[0] = %02x, inp[1] = %02x, inp[2] = %02x\n", inp[0], inp[1], inp[2]);
|
printf("inp.byte1 = %02x, inp.byte2 = %02x\n", inp.byte1, inp.byte2);
|
||||||
/* convert it to UTF-8 */
|
/* convert it to UTF-8 */
|
||||||
char *out = convert_ucs2_to_utf8((xcb_char2b_t*)inp, 1);
|
char *out = convert_ucs2_to_utf8(&inp, 1);
|
||||||
printf("converted to %s\n", out);
|
printf("converted to %s\n", out);
|
||||||
|
|
||||||
glyphs_ucs[input_position] = malloc(3 * sizeof(uint8_t));
|
glyphs_ucs[input_position] = inp;
|
||||||
if (glyphs_ucs[input_position] == NULL)
|
|
||||||
err(EXIT_FAILURE, "malloc() failed\n");
|
|
||||||
memcpy(glyphs_ucs[input_position], inp, 3);
|
|
||||||
glyphs_utf8[input_position] = out;
|
glyphs_utf8[input_position] = out;
|
||||||
input_position++;
|
input_position++;
|
||||||
|
|
||||||
|
@ -318,8 +322,8 @@ int main(int argc, char *argv[]) {
|
||||||
limit = atoi(optarg);
|
limit = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
FREE(prompt);
|
i3string_free(prompt);
|
||||||
prompt = strdup(optarg);
|
prompt = i3string_from_utf8(optarg);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
FREE(pattern);
|
FREE(pattern);
|
||||||
|
@ -349,15 +353,12 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
sockfd = ipc_connect(socket_path);
|
sockfd = ipc_connect(socket_path);
|
||||||
|
|
||||||
if (prompt != NULL)
|
|
||||||
prompt = (char*)convert_utf8_to_ucs2(prompt, &prompt_len);
|
|
||||||
|
|
||||||
int screens;
|
int screens;
|
||||||
conn = xcb_connect(NULL, &screens);
|
conn = xcb_connect(NULL, &screens);
|
||||||
if (!conn || xcb_connection_has_error(conn))
|
if (!conn || xcb_connection_has_error(conn))
|
||||||
die("Cannot open display\n");
|
die("Cannot open display\n");
|
||||||
|
|
||||||
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
|
root_screen = xcb_aux_get_screen(conn, screens);
|
||||||
root = root_screen->root;
|
root = root_screen->root;
|
||||||
|
|
||||||
symbols = xcb_key_symbols_alloc(conn);
|
symbols = xcb_key_symbols_alloc(conn);
|
||||||
|
@ -365,6 +366,9 @@ int main(int argc, char *argv[]) {
|
||||||
font = load_font(pattern, true);
|
font = load_font(pattern, true);
|
||||||
set_font(&font);
|
set_font(&font);
|
||||||
|
|
||||||
|
if (prompt != NULL)
|
||||||
|
prompt_offset = predict_text_width(prompt);
|
||||||
|
|
||||||
/* Open an input window */
|
/* Open an input window */
|
||||||
win = xcb_generate_id(conn);
|
win = xcb_generate_id(conn);
|
||||||
xcb_create_window(
|
xcb_create_window(
|
||||||
|
|
|
@ -202,7 +202,7 @@ sub convert_command {
|
||||||
# simple replacements
|
# simple replacements
|
||||||
my @replace = (
|
my @replace = (
|
||||||
qr/^s/ => 'layout stacking',
|
qr/^s/ => 'layout stacking',
|
||||||
qr/^d/ => 'layout default',
|
qr/^d/ => 'layout toggle split',
|
||||||
qr/^T/ => 'layout tabbed',
|
qr/^T/ => 'layout tabbed',
|
||||||
qr/^f($|[^go])/ => 'fullscreen',
|
qr/^f($|[^go])/ => 'fullscreen',
|
||||||
qr/^fg/ => 'fullscreen global',
|
qr/^fg/ => 'fullscreen global',
|
||||||
|
|
|
@ -1,32 +1,10 @@
|
||||||
# Default value so one can compile i3-msg standalone
|
all:
|
||||||
TOPDIR=..
|
$(MAKE) -C .. i3-msg/i3-msg
|
||||||
|
|
||||||
include $(TOPDIR)/common.mk
|
install:
|
||||||
|
$(MAKE) -C .. install-i3-msg
|
||||||
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/
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o
|
$(MAKE) -C .. clean-i3-msg
|
||||||
|
|
||||||
distclean: clean
|
.PHONY: all install clean
|
||||||
rm -f i3-msg
|
|
||||||
|
|
|
@ -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) $(PANGO_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) $(XCB_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
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
* © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
|
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* i3-msg/main.c: Utility which sends messages to a running i3-instance using
|
* i3-msg/main.c: Utility which sends messages to a running i3-instance using
|
||||||
* IPC via UNIX domain sockets.
|
* IPC via UNIX domain sockets.
|
||||||
|
@ -73,9 +73,11 @@ int main(int argc, char *argv[]) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
|
||||||
else if (strcasecmp(optarg, "get_bar_config") == 0)
|
else if (strcasecmp(optarg, "get_bar_config") == 0)
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
|
||||||
|
else if (strcasecmp(optarg, "get_version") == 0)
|
||||||
|
message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION;
|
||||||
else {
|
else {
|
||||||
printf("Unknown message type\n");
|
printf("Unknown message type\n");
|
||||||
printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config\n");
|
printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_version\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
} else if (o == 'q') {
|
} else if (o == 'q') {
|
||||||
|
|
|
@ -1,35 +1,10 @@
|
||||||
# Default value so one can compile i3-nagbar standalone
|
all:
|
||||||
TOPDIR=..
|
$(MAKE) -C .. i3-nagbar/i3-nagbar
|
||||||
|
|
||||||
include $(TOPDIR)/common.mk
|
install:
|
||||||
|
$(MAKE) -C .. install-i3-nagbar
|
||||||
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/
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o
|
$(MAKE) -C .. clean-i3-nagbar
|
||||||
|
|
||||||
distclean: clean
|
.PHONY: all install clean
|
||||||
rm -f i3-nagbar
|
|
||||||
|
|
|
@ -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) $(PANGO_CFLAGS)
|
||||||
|
i3_nagbar_LIBS = $(XCB_LIBS) $(PANGO_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) $(XCB_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,$^) $(LIBS) $(i3_nagbar_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
|
|
@ -30,7 +30,7 @@
|
||||||
#include "i3-nagbar.h"
|
#include "i3-nagbar.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *label;
|
i3String *label;
|
||||||
char *action;
|
char *action;
|
||||||
int16_t x;
|
int16_t x;
|
||||||
uint16_t width;
|
uint16_t width;
|
||||||
|
@ -41,7 +41,7 @@ static xcb_pixmap_t pixmap;
|
||||||
static xcb_gcontext_t pixmap_gc;
|
static xcb_gcontext_t pixmap_gc;
|
||||||
static xcb_rectangle_t rect = { 0, 0, 600, 20 };
|
static xcb_rectangle_t rect = { 0, 0, 600, 20 };
|
||||||
static i3Font font;
|
static i3Font font;
|
||||||
static char *prompt;
|
static i3String *prompt;
|
||||||
static button_t *buttons;
|
static button_t *buttons;
|
||||||
static int buttoncnt;
|
static int buttoncnt;
|
||||||
|
|
||||||
|
@ -54,6 +54,27 @@ static uint32_t color_text; /* color of the text */
|
||||||
|
|
||||||
xcb_window_t root;
|
xcb_window_t root;
|
||||||
xcb_connection_t *conn;
|
xcb_connection_t *conn;
|
||||||
|
xcb_screen_t *root_screen;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Having verboselog() and errorlog() is necessary when using libi3.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void verboselog(char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stdout, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void errorlog(char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stderr, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Starts the given application by passing it through a shell. We use double fork
|
* Starts the given application by passing it through a shell. We use double fork
|
||||||
|
@ -132,7 +153,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
||||||
|
|
||||||
/* restore font color */
|
/* restore font color */
|
||||||
set_font_colors(pixmap_gc, color_text, color_background);
|
set_font_colors(pixmap_gc, color_text, color_background);
|
||||||
draw_text(prompt, strlen(prompt), false, pixmap, pixmap_gc,
|
draw_text(prompt, pixmap, pixmap_gc,
|
||||||
4 + 4, 4 + 4, rect.width - 4 - 4);
|
4 + 4, 4 + 4, rect.width - 4 - 4);
|
||||||
|
|
||||||
/* render close button */
|
/* render close button */
|
||||||
|
@ -159,7 +180,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
||||||
|
|
||||||
values[0] = 1;
|
values[0] = 1;
|
||||||
set_font_colors(pixmap_gc, color_text, color_button_background);
|
set_font_colors(pixmap_gc, color_text, color_button_background);
|
||||||
draw_text("X", 1, false, pixmap, pixmap_gc, y - w - line_width + w / 2 - 4,
|
draw_text_ascii("X", pixmap, pixmap_gc, y - w - line_width + w / 2 - 4,
|
||||||
4 + 4 - 1, rect.width - y + w + line_width - w / 2 + 4);
|
4 + 4 - 1, rect.width - y + w + line_width - w / 2 + 4);
|
||||||
y -= w;
|
y -= w;
|
||||||
|
|
||||||
|
@ -190,7 +211,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
||||||
values[0] = color_text;
|
values[0] = color_text;
|
||||||
values[1] = color_button_background;
|
values[1] = color_button_background;
|
||||||
set_font_colors(pixmap_gc, color_text, color_button_background);
|
set_font_colors(pixmap_gc, color_text, color_button_background);
|
||||||
draw_text(buttons[c].label, strlen(buttons[c].label), false, pixmap, pixmap_gc,
|
draw_text(buttons[c].label, pixmap, pixmap_gc,
|
||||||
y - w - line_width + 6, 4 + 3, rect.width - y + w + line_width - 6);
|
y - w - line_width + 6, 4 + 3, rect.width - y + w + line_width - 6);
|
||||||
|
|
||||||
y -= w;
|
y -= w;
|
||||||
|
@ -216,7 +237,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
char *pattern = strdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
|
char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
|
||||||
int o, option_index = 0;
|
int o, option_index = 0;
|
||||||
enum { TYPE_ERROR = 0, TYPE_WARNING = 1 } bar_type = TYPE_ERROR;
|
enum { TYPE_ERROR = 0, TYPE_WARNING = 1 } bar_type = TYPE_ERROR;
|
||||||
|
|
||||||
|
@ -232,7 +253,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
char *options_string = "b:f:m:t:vh";
|
char *options_string = "b:f:m:t:vh";
|
||||||
|
|
||||||
prompt = strdup("Please do not run this program.");
|
prompt = i3string_from_utf8("Please do not run this program.");
|
||||||
|
|
||||||
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
|
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
|
||||||
switch (o) {
|
switch (o) {
|
||||||
|
@ -241,11 +262,11 @@ int main(int argc, char *argv[]) {
|
||||||
return 0;
|
return 0;
|
||||||
case 'f':
|
case 'f':
|
||||||
FREE(pattern);
|
FREE(pattern);
|
||||||
pattern = strdup(optarg);
|
pattern = sstrdup(optarg);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
FREE(prompt);
|
i3string_free(prompt);
|
||||||
prompt = strdup(optarg);
|
prompt = i3string_from_utf8(optarg);
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
bar_type = (strcasecmp(optarg, "warning") == 0 ? TYPE_WARNING : TYPE_ERROR);
|
bar_type = (strcasecmp(optarg, "warning") == 0 ? TYPE_WARNING : TYPE_ERROR);
|
||||||
|
@ -256,10 +277,10 @@ int main(int argc, char *argv[]) {
|
||||||
return 0;
|
return 0;
|
||||||
case 'b':
|
case 'b':
|
||||||
buttons = realloc(buttons, sizeof(button_t) * (buttoncnt + 1));
|
buttons = realloc(buttons, sizeof(button_t) * (buttoncnt + 1));
|
||||||
buttons[buttoncnt].label = optarg;
|
buttons[buttoncnt].label = i3string_from_utf8(optarg);
|
||||||
buttons[buttoncnt].action = argv[optind];
|
buttons[buttoncnt].action = argv[optind];
|
||||||
printf("button with label *%s* and action *%s*\n",
|
printf("button with label *%s* and action *%s*\n",
|
||||||
buttons[buttoncnt].label,
|
i3string_as_utf8(buttons[buttoncnt].label),
|
||||||
buttons[buttoncnt].action);
|
buttons[buttoncnt].action);
|
||||||
buttoncnt++;
|
buttoncnt++;
|
||||||
printf("now %d buttons\n", buttoncnt);
|
printf("now %d buttons\n", buttoncnt);
|
||||||
|
@ -280,7 +301,7 @@ int main(int argc, char *argv[]) {
|
||||||
#include "atoms.xmacro"
|
#include "atoms.xmacro"
|
||||||
#undef xmacro
|
#undef xmacro
|
||||||
|
|
||||||
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens);
|
root_screen = xcb_aux_get_screen(conn, screens);
|
||||||
root = root_screen->root;
|
root = root_screen->root;
|
||||||
|
|
||||||
if (bar_type == TYPE_ERROR) {
|
if (bar_type == TYPE_ERROR) {
|
||||||
|
@ -432,5 +453,7 @@ int main(int argc, char *argv[]) {
|
||||||
free(event);
|
free(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FREE(pattern);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
# Distributions/packagers should enhance this script with a
|
# Distributions/packagers should enhance this script with a
|
||||||
# distribution-specific mechanism to find the preferred terminal emulator. On
|
# distribution-specific mechanism to find the preferred terminal emulator. On
|
||||||
# Debian, there is the x-terminal-emulator symlink for example.
|
# 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
|
if which $terminal > /dev/null 2>&1; then
|
||||||
exec $terminal "$@"
|
exec $terminal "$@"
|
||||||
fi
|
fi
|
||||||
|
|
14
i3.config
14
i3.config
|
@ -9,8 +9,14 @@
|
||||||
# layout, use the i3-config-wizard
|
# layout, use the i3-config-wizard
|
||||||
#
|
#
|
||||||
|
|
||||||
# font for window titles. ISO 10646 = Unicode
|
# Font for window titles. Will also be used by the bar unless a different font
|
||||||
|
# is used in the bar {} block below. ISO 10646 = Unicode
|
||||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
|
# The font above is very space-efficient, that is, it looks good, sharp and
|
||||||
|
# clear in small sizes. However, if you need a lot of unicode glyphs or
|
||||||
|
# right-to-left text rendering, you should instead use pango for rendering and
|
||||||
|
# chose an xft font, such as:
|
||||||
|
# font xft:DejaVu Sans Mono 10
|
||||||
|
|
||||||
# use Mouse+Mod1 to drag floating windows to their wanted position
|
# use Mouse+Mod1 to drag floating windows to their wanted position
|
||||||
floating_modifier Mod1
|
floating_modifier Mod1
|
||||||
|
@ -57,10 +63,10 @@ bindsym Mod1+v split v
|
||||||
# enter fullscreen mode for the focused container
|
# enter fullscreen mode for the focused container
|
||||||
bindsym Mod1+f fullscreen
|
bindsym Mod1+f fullscreen
|
||||||
|
|
||||||
# change container layout (stacked, tabbed, default)
|
# change container layout (stacked, tabbed, toggle split)
|
||||||
bindsym Mod1+s layout stacking
|
bindsym Mod1+s layout stacking
|
||||||
bindsym Mod1+w layout tabbed
|
bindsym Mod1+w layout tabbed
|
||||||
bindsym Mod1+e layout default
|
bindsym Mod1+e layout toggle split
|
||||||
|
|
||||||
# toggle tiling / floating
|
# toggle tiling / floating
|
||||||
bindsym Mod1+Shift+space floating toggle
|
bindsym Mod1+Shift+space floating toggle
|
||||||
|
@ -103,7 +109,7 @@ bindsym Mod1+Shift+c reload
|
||||||
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
||||||
bindsym Mod1+Shift+r restart
|
bindsym Mod1+Shift+r restart
|
||||||
# exit i3 (logs you out of your X session)
|
# 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)
|
# resize window (you can also use the mouse for that)
|
||||||
mode "resize" {
|
mode "resize" {
|
||||||
|
|
|
@ -10,8 +10,14 @@
|
||||||
|
|
||||||
set $mod Mod1
|
set $mod Mod1
|
||||||
|
|
||||||
# font for window titles. ISO 10646 = Unicode
|
# Font for window titles. Will also be used by the bar unless a different font
|
||||||
|
# is used in the bar {} block below. ISO 10646 = Unicode
|
||||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
|
# The font above is very space-efficient, that is, it looks good, sharp and
|
||||||
|
# clear in small sizes. However, if you need a lot of unicode glyphs or
|
||||||
|
# right-to-left text rendering, you should instead use pango for rendering and
|
||||||
|
# chose an xft font, such as:
|
||||||
|
# font xft:DejaVu Sans Mono 10
|
||||||
|
|
||||||
# Use Mouse+$mod to drag floating windows to their wanted position
|
# Use Mouse+$mod to drag floating windows to their wanted position
|
||||||
floating_modifier $mod
|
floating_modifier $mod
|
||||||
|
@ -58,10 +64,10 @@ bindcode $mod+55 split v
|
||||||
# enter fullscreen mode for the focused container
|
# enter fullscreen mode for the focused container
|
||||||
bindcode $mod+41 fullscreen
|
bindcode $mod+41 fullscreen
|
||||||
|
|
||||||
# change container layout (stacked, tabbed, default)
|
# change container layout (stacked, tabbed, toggle split)
|
||||||
bindcode $mod+39 layout stacking
|
bindcode $mod+39 layout stacking
|
||||||
bindcode $mod+25 layout tabbed
|
bindcode $mod+25 layout tabbed
|
||||||
bindcode $mod+26 layout default
|
bindcode $mod+26 layout toggle split
|
||||||
|
|
||||||
# toggle tiling / floating
|
# toggle tiling / floating
|
||||||
bindcode $mod+Shift+65 floating toggle
|
bindcode $mod+Shift+65 floating toggle
|
||||||
|
@ -104,7 +110,7 @@ bindcode $mod+Shift+54 reload
|
||||||
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
||||||
bindcode $mod+Shift+27 restart
|
bindcode $mod+Shift+27 restart
|
||||||
# exit i3 (logs you out of your X session)
|
# 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)
|
# resize window (you can also use the mouse for that)
|
||||||
mode "resize" {
|
mode "resize" {
|
||||||
|
|
|
@ -1,42 +1,10 @@
|
||||||
TOPDIR=..
|
all:
|
||||||
|
$(MAKE) -C .. i3bar/i3bar
|
||||||
|
|
||||||
include $(TOPDIR)/common.mk
|
install:
|
||||||
|
$(MAKE) -C .. install-i3bar
|
||||||
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
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f src/*.o
|
$(MAKE) -C .. clean-i3bar
|
||||||
$(MAKE) -C doc clean
|
|
||||||
|
|
||||||
distclean: clean
|
.PHONY: all install clean
|
||||||
rm -f i3bar
|
|
||||||
$(MAKE) -C doc distclean
|
|
||||||
|
|
||||||
.PHONY: install clean distclean doc
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
|
||||||
|
i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_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) $(XCB_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,$^) $(LIBS) $(i3bar_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
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* child.c: Getting Input for the statusline
|
* child.c: Getting Input for the statusline
|
||||||
*
|
*
|
||||||
|
@ -24,25 +24,25 @@ void start_child(char *command);
|
||||||
* kill()s the child-process (if any). Called when exit()ing.
|
* kill()s the child-process (if any). Called when exit()ing.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void kill_child_at_exit();
|
void kill_child_at_exit(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* kill()s the child-process (if any) and closes and
|
* kill()s the child-process (if any) and closes and
|
||||||
* free()s the stdin- and sigchild-watchers
|
* free()s the stdin- and sigchild-watchers
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void kill_child();
|
void kill_child(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sends a SIGSTOP to the child-process (if existent)
|
* Sends a SIGSTOP to the child-process (if existent)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void stop_child();
|
void stop_child(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sends a SIGCONT to the child-process (if existent)
|
* Sends a SIGCONT to the child-process (if existent)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cont_child();
|
void cont_child(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/xproto.h>
|
#include <xcb/xproto.h>
|
||||||
|
#include "libi3.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
|
||||||
typedef struct rect_t rect;
|
typedef struct rect_t rect;
|
||||||
|
@ -29,15 +30,10 @@ struct rect_t {
|
||||||
/* This data structure represents one JSON dictionary, multiple of these make
|
/* This data structure represents one JSON dictionary, multiple of these make
|
||||||
* up one status line. */
|
* up one status line. */
|
||||||
struct status_block {
|
struct status_block {
|
||||||
char *full_text;
|
i3String *full_text;
|
||||||
|
|
||||||
char *color;
|
char *color;
|
||||||
|
|
||||||
/* full_text, but converted to UCS-2. This variable is only temporarily
|
|
||||||
* used in refresh_statusline(). */
|
|
||||||
xcb_char2b_t *ucs2_full_text;
|
|
||||||
size_t glyph_count_full_text;
|
|
||||||
|
|
||||||
/* The amount of pixels necessary to render this block. This variable is
|
/* The amount of pixels necessary to render this block. This variable is
|
||||||
* only temporarily used in refresh_statusline(). */
|
* only temporarily used in refresh_statusline(). */
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
|
@ -56,5 +52,6 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head;
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "libi3.h"
|
#include "libi3.h"
|
||||||
|
#include "determine_json_version.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* determine_json_version.c: Determines the JSON protocol version based on the
|
||||||
|
* first line of input from a child program.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef DETERMINE_JSON_VERSION_H_
|
||||||
|
#define DETERMINE_JSON_VERSION_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determines the JSON i3bar protocol version from the given buffer. In case
|
||||||
|
* the buffer does not contain valid JSON, or no version field is found, this
|
||||||
|
* function returns -1. The amount of bytes consumed by parsing the header is
|
||||||
|
* returned in *consumed (if non-NULL).
|
||||||
|
*
|
||||||
|
* The return type is an int32_t to avoid machines with different sizes of
|
||||||
|
* 'int' to allow different values here. It’s highly unlikely we ever exceed
|
||||||
|
* even an int8_t, but still…
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed);
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* ipc.c: Communicating with i3
|
* ipc.c: Communicating with i3
|
||||||
*
|
*
|
||||||
|
@ -23,7 +23,7 @@ int init_connection(const char *socket_path);
|
||||||
* Destroy the connection to i3.
|
* Destroy the connection to i3.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void destroy_connection();
|
void destroy_connection(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sends a Message to i3.
|
* Sends a Message to i3.
|
||||||
|
@ -36,6 +36,6 @@ int i3_send_msg(uint32_t type, const char* payload);
|
||||||
* Subscribe to all the i3-events, we need
|
* Subscribe to all the i3-events, we need
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void subscribe_events();
|
void subscribe_events(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* outputs.c: Maintaining the output-list
|
* outputs.c: Maintaining the output-list
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ void parse_outputs_json(char* json);
|
||||||
* Initiate the output-list
|
* Initiate the output-list
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void init_outputs();
|
void init_outputs(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the output with the given name
|
* Returns the output with the given name
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
|
||||||
/* Get the maximum/minimum of x and y */
|
/* Get the maximum/minimum of x and y */
|
||||||
|
#undef MAX
|
||||||
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
||||||
|
#undef MIN
|
||||||
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
||||||
|
|
||||||
/* Securely free p */
|
/* Securely free p */
|
||||||
|
@ -51,6 +53,11 @@
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
/* We will include libi3.h which define its own version of ELOG.
|
||||||
|
* We want *our* version, so we undef the libi3 one. */
|
||||||
|
#if defined(ELOG)
|
||||||
|
#undef ELOG
|
||||||
|
#endif
|
||||||
#define ELOG(fmt, ...) do { \
|
#define ELOG(fmt, ...) do { \
|
||||||
fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
|
fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* workspaces.c: Maintaining the workspace-lists
|
* workspaces.c: Maintaining the workspace-lists
|
||||||
*
|
*
|
||||||
|
@ -28,13 +28,11 @@ void parse_workspaces_json(char *json);
|
||||||
* free() all workspace data-structures
|
* free() all workspace data-structures
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void free_workspaces();
|
void free_workspaces(void);
|
||||||
|
|
||||||
struct i3_ws {
|
struct i3_ws {
|
||||||
int num; /* The internal number of the ws */
|
int num; /* The internal number of the ws */
|
||||||
char *name; /* The name (in utf8) of the ws */
|
i3String *name; /* The name of the ws */
|
||||||
xcb_char2b_t *ucs2_name; /* The name (in ucs2) of the ws */
|
|
||||||
int name_glyphs; /* The length (in glyphs) of the name */
|
|
||||||
int name_width; /* The rendered width of the name */
|
int name_width; /* The rendered width of the name */
|
||||||
bool visible; /* If the ws is currently visible on an output */
|
bool visible; /* If the ws is currently visible on an output */
|
||||||
bool focused; /* If the ws is currently focused */
|
bool focused; /* If the ws is currently focused */
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* xcb.c: Communicating with X
|
* xcb.c: Communicating with X
|
||||||
*
|
*
|
||||||
|
@ -69,13 +69,13 @@ void init_colors(const struct xcb_color_strings_t *colors);
|
||||||
* Called once, before the program terminates.
|
* Called once, before the program terminates.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void clean_xcb();
|
void clean_xcb(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the earlier requested atoms and save them in the prepared data-structure
|
* Get the earlier requested atoms and save them in the prepared data-structure
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void get_atoms();
|
void get_atoms(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reparents all tray clients of the specified output to the root window. This
|
* Reparents all tray clients of the specified output to the root window. This
|
||||||
|
@ -98,24 +98,24 @@ void destroy_window(i3_output *output);
|
||||||
* Reallocate the statusline-buffer
|
* Reallocate the statusline-buffer
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void realloc_sl_buffer();
|
void realloc_sl_buffer(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reconfigure all bars and create new for newly activated outputs
|
* Reconfigure all bars and create new for newly activated outputs
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void reconfig_windows();
|
void reconfig_windows(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Render the bars, with buttons and statusline
|
* Render the bars, with buttons and statusline
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void draw_bars();
|
void draw_bars(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Redraw the bars, i.e. simply copy the buffer to the barwindow
|
* Redraw the bars, i.e. simply copy the buffer to the barwindow
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void redraw_bars();
|
void redraw_bars(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* child.c: Getting Input for the statusline
|
* child.c: Getting Input for the statusline
|
||||||
*
|
*
|
||||||
|
@ -56,7 +56,7 @@ char *statusline_buffer = NULL;
|
||||||
* Stop and free() the stdin- and sigchild-watchers
|
* Stop and free() the stdin- and sigchild-watchers
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cleanup() {
|
void cleanup(void) {
|
||||||
if (stdin_io != NULL) {
|
if (stdin_io != NULL) {
|
||||||
ev_io_stop(main_loop, stdin_io);
|
ev_io_stop(main_loop, stdin_io);
|
||||||
FREE(stdin_io);
|
FREE(stdin_io);
|
||||||
|
@ -80,7 +80,7 @@ static int stdin_start_array(void *context) {
|
||||||
struct status_block *first;
|
struct status_block *first;
|
||||||
while (!TAILQ_EMPTY(&statusline_head)) {
|
while (!TAILQ_EMPTY(&statusline_head)) {
|
||||||
first = TAILQ_FIRST(&statusline_head);
|
first = TAILQ_FIRST(&statusline_head);
|
||||||
FREE(first->full_text);
|
I3STRING_FREE(first->full_text);
|
||||||
FREE(first->color);
|
FREE(first->color);
|
||||||
TAILQ_REMOVE(&statusline_head, first, blocks);
|
TAILQ_REMOVE(&statusline_head, first, blocks);
|
||||||
free(first);
|
free(first);
|
||||||
|
@ -116,7 +116,7 @@ static int stdin_string(void *context, const unsigned char *val, unsigned int le
|
||||||
#endif
|
#endif
|
||||||
parser_ctx *ctx = context;
|
parser_ctx *ctx = context;
|
||||||
if (strcasecmp(ctx->last_map_key, "full_text") == 0) {
|
if (strcasecmp(ctx->last_map_key, "full_text") == 0) {
|
||||||
sasprintf(&(ctx->block.full_text), "%.*s", len, val);
|
ctx->block.full_text = i3string_from_utf8_with_length((const char *)val, len);
|
||||||
}
|
}
|
||||||
if (strcasecmp(ctx->last_map_key, "color") == 0) {
|
if (strcasecmp(ctx->last_map_key, "color") == 0) {
|
||||||
sasprintf(&(ctx->block.color), "%.*s", len, val);
|
sasprintf(&(ctx->block.color), "%.*s", len, val);
|
||||||
|
@ -131,7 +131,7 @@ static int stdin_end_map(void *context) {
|
||||||
/* Ensure we have a full_text set, so that when it is missing (or null),
|
/* Ensure we have a full_text set, so that when it is missing (or null),
|
||||||
* i3bar doesn’t crash and the user gets an annoying message. */
|
* i3bar doesn’t crash and the user gets an annoying message. */
|
||||||
if (!new_block->full_text)
|
if (!new_block->full_text)
|
||||||
new_block->full_text = sstrdup("SPEC VIOLATION (null)");
|
new_block->full_text = i3string_from_utf8("SPEC VIOLATION (null)");
|
||||||
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ static int stdin_end_array(void *context) {
|
||||||
DLOG("dumping statusline:\n");
|
DLOG("dumping statusline:\n");
|
||||||
struct status_block *current;
|
struct status_block *current;
|
||||||
TAILQ_FOREACH(current, &statusline_head, blocks) {
|
TAILQ_FOREACH(current, &statusline_head, blocks) {
|
||||||
DLOG("full_text = %s\n", current->full_text);
|
DLOG("full_text = %s\n", i3string_as_utf8(current->full_text));
|
||||||
DLOG("color = %s\n", current->color);
|
DLOG("color = %s\n", current->color);
|
||||||
}
|
}
|
||||||
DLOG("end of dump\n");
|
DLOG("end of dump\n");
|
||||||
|
@ -192,19 +192,18 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
if (first_line) {
|
if (first_line) {
|
||||||
DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer);
|
DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer);
|
||||||
/* Detect whether this is JSON or plain text. */
|
/* Detect whether this is JSON or plain text. */
|
||||||
plaintext = (strncasecmp((char*)buffer, "{\"version\":", strlen("{\"version\":")) != 0);
|
unsigned int consumed = 0;
|
||||||
|
/* At the moment, we don’t care for the version. This might change
|
||||||
|
* in the future, but for now, we just discard it. */
|
||||||
|
plaintext = (determine_json_version(buffer, buffer_len, &consumed) == -1);
|
||||||
if (plaintext) {
|
if (plaintext) {
|
||||||
/* In case of plaintext, we just add a single block and change its
|
/* In case of plaintext, we just add a single block and change its
|
||||||
* full_text pointer later. */
|
* full_text pointer later. */
|
||||||
struct status_block *new_block = scalloc(sizeof(struct status_block));
|
struct status_block *new_block = scalloc(sizeof(struct status_block));
|
||||||
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
||||||
} else {
|
} else {
|
||||||
/* At the moment, we don’t care for the version. This might change
|
json_input += consumed;
|
||||||
* in the future, but for now, we just discard it. */
|
rec -= consumed;
|
||||||
while (*json_input != '\n' && *json_input != '\0') {
|
|
||||||
json_input++;
|
|
||||||
rec--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
first_line = false;
|
first_line = false;
|
||||||
}
|
}
|
||||||
|
@ -218,18 +217,18 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
fprintf(stderr, "[i3bar] Could not parse JSON input (code %d): %.*s\n",
|
fprintf(stderr, "[i3bar] Could not parse JSON input (code %d): %.*s\n",
|
||||||
status, rec, json_input);
|
status, rec, json_input);
|
||||||
}
|
}
|
||||||
free(buffer);
|
|
||||||
} else {
|
} else {
|
||||||
struct status_block *first = TAILQ_FIRST(&statusline_head);
|
struct status_block *first = TAILQ_FIRST(&statusline_head);
|
||||||
/* Clear the old buffer if any. */
|
/* Clear the old buffer if any. */
|
||||||
FREE(first->full_text);
|
I3STRING_FREE(first->full_text);
|
||||||
/* Remove the trailing newline and terminate the string at the same
|
/* Remove the trailing newline and terminate the string at the same
|
||||||
* time. */
|
* time. */
|
||||||
if (buffer[rec-1] == '\n' || buffer[rec-1] == '\r')
|
if (buffer[rec-1] == '\n' || buffer[rec-1] == '\r')
|
||||||
buffer[rec-1] = '\0';
|
buffer[rec-1] = '\0';
|
||||||
else buffer[rec] = '\0';
|
else buffer[rec] = '\0';
|
||||||
first->full_text = (char*)buffer;
|
first->full_text = i3string_from_utf8((const char *)buffer);
|
||||||
}
|
}
|
||||||
|
free(buffer);
|
||||||
draw_bars();
|
draw_bars();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +327,7 @@ void start_child(char *command) {
|
||||||
* kill()s the child-process (if any). Called when exit()ing.
|
* kill()s the child-process (if any). Called when exit()ing.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void kill_child_at_exit() {
|
void kill_child_at_exit(void) {
|
||||||
if (child_pid != 0) {
|
if (child_pid != 0) {
|
||||||
kill(child_pid, SIGCONT);
|
kill(child_pid, SIGCONT);
|
||||||
kill(child_pid, SIGTERM);
|
kill(child_pid, SIGTERM);
|
||||||
|
@ -340,7 +339,7 @@ void kill_child_at_exit() {
|
||||||
* free()s the stdin- and sigchild-watchers
|
* free()s the stdin- and sigchild-watchers
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void kill_child() {
|
void kill_child(void) {
|
||||||
if (child_pid != 0) {
|
if (child_pid != 0) {
|
||||||
kill(child_pid, SIGCONT);
|
kill(child_pid, SIGCONT);
|
||||||
kill(child_pid, SIGTERM);
|
kill(child_pid, SIGTERM);
|
||||||
|
@ -355,7 +354,7 @@ void kill_child() {
|
||||||
* Sends a SIGSTOP to the child-process (if existent)
|
* Sends a SIGSTOP to the child-process (if existent)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void stop_child() {
|
void stop_child(void) {
|
||||||
if (child_pid != 0) {
|
if (child_pid != 0) {
|
||||||
kill(child_pid, SIGSTOP);
|
kill(child_pid, SIGSTOP);
|
||||||
}
|
}
|
||||||
|
@ -365,7 +364,7 @@ void stop_child() {
|
||||||
* Sends a SIGCONT to the child-process (if existent)
|
* Sends a SIGCONT to the child-process (if existent)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cont_child() {
|
void cont_child(void) {
|
||||||
if (child_pid != 0) {
|
if (child_pid != 0) {
|
||||||
kill(child_pid, SIGCONT);
|
kill(child_pid, SIGCONT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* determine_json_version.c: Determines the JSON protocol version based on the
|
||||||
|
* first line of input from a child program.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <ev.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <yajl/yajl_common.h>
|
||||||
|
#include <yajl/yajl_parse.h>
|
||||||
|
#include <yajl/yajl_version.h>
|
||||||
|
|
||||||
|
static bool version_key;
|
||||||
|
static int32_t version_number;
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int version_integer(void *ctx, long long val) {
|
||||||
|
#else
|
||||||
|
static int version_integer(void *ctx, long val) {
|
||||||
|
#endif
|
||||||
|
if (version_key)
|
||||||
|
version_number = (uint32_t)val;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
|
||||||
|
#else
|
||||||
|
static int version_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
|
||||||
|
#endif
|
||||||
|
version_key = (stringlen == strlen("version") &&
|
||||||
|
strncmp((const char*)stringval, "version", strlen("version")) == 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static yajl_callbacks version_callbacks = {
|
||||||
|
NULL, /* null */
|
||||||
|
NULL, /* boolean */
|
||||||
|
&version_integer,
|
||||||
|
NULL, /* double */
|
||||||
|
NULL, /* number */
|
||||||
|
NULL, /* string */
|
||||||
|
NULL, /* start_map */
|
||||||
|
&version_map_key,
|
||||||
|
NULL, /* end_map */
|
||||||
|
NULL, /* start_array */
|
||||||
|
NULL /* end_array */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determines the JSON i3bar protocol version from the given buffer. In case
|
||||||
|
* the buffer does not contain valid JSON, or no version field is found, this
|
||||||
|
* function returns -1. The amount of bytes consumed by parsing the header is
|
||||||
|
* returned in *consumed (if non-NULL).
|
||||||
|
*
|
||||||
|
* The return type is an int32_t to avoid machines with different sizes of
|
||||||
|
* 'int' to allow different values here. It’s highly unlikely we ever exceed
|
||||||
|
* even an int8_t, but still…
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed) {
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL);
|
||||||
|
/* Allow trailing garbage. yajl 1 always behaves that way anyways, but for
|
||||||
|
* yajl 2, we need to be explicit. */
|
||||||
|
yajl_config(handle, yajl_allow_trailing_garbage, 1);
|
||||||
|
#else
|
||||||
|
yajl_parser_config parse_conf = { 0, 0 };
|
||||||
|
|
||||||
|
yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
version_key = false;
|
||||||
|
version_number = -1;
|
||||||
|
|
||||||
|
yajl_status state = yajl_parse(handle, buffer, length);
|
||||||
|
if (state != yajl_status_ok) {
|
||||||
|
version_number = -1;
|
||||||
|
if (consumed != NULL)
|
||||||
|
*consumed = 0;
|
||||||
|
} else {
|
||||||
|
if (consumed != NULL)
|
||||||
|
*consumed = yajl_get_bytes_consumed(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
yajl_free(handle);
|
||||||
|
|
||||||
|
return version_number;
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* ipc.c: Communicating with i3
|
* ipc.c: Communicating with i3
|
||||||
*
|
*
|
||||||
|
@ -286,7 +286,7 @@ int init_connection(const char *socket_path) {
|
||||||
/*
|
/*
|
||||||
* Destroy the connection to i3.
|
* Destroy the connection to i3.
|
||||||
*/
|
*/
|
||||||
void destroy_connection() {
|
void destroy_connection(void) {
|
||||||
close(i3_connection->fd);
|
close(i3_connection->fd);
|
||||||
ev_io_stop(main_loop, i3_connection);
|
ev_io_stop(main_loop, i3_connection);
|
||||||
}
|
}
|
||||||
|
@ -295,7 +295,7 @@ void destroy_connection() {
|
||||||
* Subscribe to all the i3-events, we need
|
* Subscribe to all the i3-events, we need
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void subscribe_events() {
|
void subscribe_events(void) {
|
||||||
if (config.disable_ws) {
|
if (config.disable_ws) {
|
||||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\" ]");
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\" ]");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -17,6 +17,26 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Having verboselog() and errorlog() is necessary when using libi3.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void verboselog(char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stdout, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void errorlog(char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stderr, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Glob path, i.e. expand ~
|
* Glob path, i.e. expand ~
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* outputs.c: Maintaining the output-list
|
* outputs.c: Maintaining the output-list
|
||||||
*
|
*
|
||||||
|
@ -266,7 +266,7 @@ yajl_callbacks outputs_callbacks = {
|
||||||
* Initiate the output-list
|
* Initiate the output-list
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void init_outputs() {
|
void init_outputs(void) {
|
||||||
outputs = smalloc(sizeof(struct outputs_head));
|
outputs = smalloc(sizeof(struct outputs_head));
|
||||||
SLIST_INIT(outputs);
|
SLIST_INIT(outputs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* workspaces.c: Maintaining the workspace-lists
|
* workspaces.c: Maintaining the workspace-lists
|
||||||
*
|
*
|
||||||
|
@ -114,23 +114,16 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne
|
||||||
|
|
||||||
if (!strcmp(params->cur_key, "name")) {
|
if (!strcmp(params->cur_key, "name")) {
|
||||||
/* Save the name */
|
/* Save the name */
|
||||||
params->workspaces_walk->name = smalloc(sizeof(const unsigned char) * (len + 1));
|
params->workspaces_walk->name = i3string_from_utf8_with_length((const char *)val, len);
|
||||||
strncpy(params->workspaces_walk->name, (const char*) val, len);
|
|
||||||
params->workspaces_walk->name[len] = '\0';
|
|
||||||
|
|
||||||
/* Convert the name to ucs2, save its length in glyphs and calculate its rendered width */
|
/* Save its rendered width */
|
||||||
size_t ucs2_len;
|
|
||||||
xcb_char2b_t *ucs2_name = (xcb_char2b_t*) convert_utf8_to_ucs2(params->workspaces_walk->name, &ucs2_len);
|
|
||||||
params->workspaces_walk->ucs2_name = ucs2_name;
|
|
||||||
params->workspaces_walk->name_glyphs = ucs2_len;
|
|
||||||
params->workspaces_walk->name_width =
|
params->workspaces_walk->name_width =
|
||||||
predict_text_width((char *)params->workspaces_walk->ucs2_name,
|
predict_text_width(params->workspaces_walk->name);
|
||||||
params->workspaces_walk->name_glyphs, true);
|
|
||||||
|
|
||||||
DLOG("Got Workspace %s, name_width: %d, glyphs: %d\n",
|
DLOG("Got Workspace %s, name_width: %d, glyphs: %zu\n",
|
||||||
params->workspaces_walk->name,
|
i3string_as_utf8(params->workspaces_walk->name),
|
||||||
params->workspaces_walk->name_width,
|
params->workspaces_walk->name_width,
|
||||||
params->workspaces_walk->name_glyphs);
|
i3string_get_num_glyphs(params->workspaces_walk->name));
|
||||||
FREE(params->cur_key);
|
FREE(params->cur_key);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -269,7 +262,7 @@ void parse_workspaces_json(char *json) {
|
||||||
* free() all workspace data-structures. Does not free() the heads of the tailqueues.
|
* free() all workspace data-structures. Does not free() the heads of the tailqueues.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void free_workspaces() {
|
void free_workspaces(void) {
|
||||||
i3_output *outputs_walk;
|
i3_output *outputs_walk;
|
||||||
if (outputs == NULL) {
|
if (outputs == NULL) {
|
||||||
return;
|
return;
|
||||||
|
@ -279,8 +272,7 @@ void free_workspaces() {
|
||||||
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||||
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
|
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
|
||||||
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
||||||
FREE(ws_walk->name);
|
I3STRING_FREE(ws_walk->name);
|
||||||
FREE(ws_walk->ucs2_name);
|
|
||||||
}
|
}
|
||||||
FREE_TAILQ(outputs_walk->workspaces, i3_ws);
|
FREE_TAILQ(outputs_walk->workspaces, i3_ws);
|
||||||
}
|
}
|
||||||
|
|
106
i3bar/src/xcb.c
106
i3bar/src/xcb.c
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
* © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* xcb.c: Communicating with X
|
* xcb.c: Communicating with X
|
||||||
*
|
*
|
||||||
|
@ -47,7 +47,7 @@ xcb_atom_t atoms[NUM_ATOMS];
|
||||||
/* Variables, that are the same for all functions at all times */
|
/* Variables, that are the same for all functions at all times */
|
||||||
xcb_connection_t *xcb_connection;
|
xcb_connection_t *xcb_connection;
|
||||||
int screen;
|
int screen;
|
||||||
xcb_screen_t *xcb_screen;
|
xcb_screen_t *root_screen;
|
||||||
xcb_window_t xcb_root;
|
xcb_window_t xcb_root;
|
||||||
|
|
||||||
/* This is needed for integration with libi3 */
|
/* This is needed for integration with libi3 */
|
||||||
|
@ -108,20 +108,18 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
|
||||||
* Redraws the statusline to the buffer
|
* Redraws the statusline to the buffer
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void refresh_statusline() {
|
void refresh_statusline(void) {
|
||||||
struct status_block *block;
|
struct status_block *block;
|
||||||
|
|
||||||
uint32_t old_statusline_width = statusline_width;
|
uint32_t old_statusline_width = statusline_width;
|
||||||
statusline_width = 0;
|
statusline_width = 0;
|
||||||
|
|
||||||
/* Convert all blocks from UTF-8 to UCS-2 and predict the text width (in
|
/* Predict the text width of all blocks (in pixels). */
|
||||||
* pixels). */
|
|
||||||
TAILQ_FOREACH(block, &statusline_head, blocks) {
|
TAILQ_FOREACH(block, &statusline_head, blocks) {
|
||||||
if (strlen(block->full_text) == 0)
|
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
block->ucs2_full_text = (xcb_char2b_t*)convert_utf8_to_ucs2(block->full_text, &(block->glyph_count_full_text));
|
block->width = predict_text_width(block->full_text);
|
||||||
block->width = predict_text_width((char*)block->ucs2_full_text, block->glyph_count_full_text, true);
|
|
||||||
/* If this is not the last block, add some pixels for a separator. */
|
/* If this is not the last block, add some pixels for a separator. */
|
||||||
if (TAILQ_NEXT(block, blocks) != NULL)
|
if (TAILQ_NEXT(block, blocks) != NULL)
|
||||||
block->width += 9;
|
block->width += 9;
|
||||||
|
@ -130,24 +128,23 @@ void refresh_statusline() {
|
||||||
|
|
||||||
/* If the statusline is bigger than our screen we need to make sure that
|
/* If the statusline is bigger than our screen we need to make sure that
|
||||||
* the pixmap provides enough space, so re-allocate if the width grew */
|
* the pixmap provides enough space, so re-allocate if the width grew */
|
||||||
if (statusline_width > xcb_screen->width_in_pixels &&
|
if (statusline_width > root_screen->width_in_pixels &&
|
||||||
statusline_width > old_statusline_width)
|
statusline_width > old_statusline_width)
|
||||||
realloc_sl_buffer();
|
realloc_sl_buffer();
|
||||||
|
|
||||||
/* Clear the statusline pixmap. */
|
/* Clear the statusline pixmap. */
|
||||||
xcb_rectangle_t rect = { 0, 0, xcb_screen->width_in_pixels, font.height };
|
xcb_rectangle_t rect = { 0, 0, root_screen->width_in_pixels, font.height };
|
||||||
xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
|
xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
|
||||||
|
|
||||||
/* Draw the text of each block. */
|
/* Draw the text of each block. */
|
||||||
uint32_t x = 0;
|
uint32_t x = 0;
|
||||||
TAILQ_FOREACH(block, &statusline_head, blocks) {
|
TAILQ_FOREACH(block, &statusline_head, blocks) {
|
||||||
if (strlen(block->full_text) == 0)
|
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
|
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
|
||||||
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg);
|
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg);
|
||||||
draw_text((char*)block->ucs2_full_text, block->glyph_count_full_text,
|
draw_text(block->full_text, statusline_pm, statusline_ctx, x, 0, block->width);
|
||||||
true, statusline_pm, statusline_ctx, x, 0, block->width);
|
|
||||||
x += block->width;
|
x += block->width;
|
||||||
|
|
||||||
if (TAILQ_NEXT(block, blocks) != NULL) {
|
if (TAILQ_NEXT(block, blocks) != NULL) {
|
||||||
|
@ -157,8 +154,6 @@ void refresh_statusline() {
|
||||||
statusline_ctx, 2,
|
statusline_ctx, 2,
|
||||||
(xcb_point_t[]){ { x - 5, 2 }, { x - 5, font.height - 2 } });
|
(xcb_point_t[]){ { x - 5, 2 }, { x - 5, font.height - 2 } });
|
||||||
}
|
}
|
||||||
|
|
||||||
FREE(block->ucs2_full_text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +161,7 @@ void refresh_statusline() {
|
||||||
* Hides all bars (unmaps them)
|
* Hides all bars (unmaps them)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void hide_bars() {
|
void hide_bars(void) {
|
||||||
if (!config.hide_on_modifier) {
|
if (!config.hide_on_modifier) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -185,7 +180,7 @@ void hide_bars() {
|
||||||
* Unhides all bars (maps them)
|
* Unhides all bars (maps them)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void unhide_bars() {
|
void unhide_bars(void) {
|
||||||
if (!config.hide_on_modifier) {
|
if (!config.hide_on_modifier) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -326,7 +321,8 @@ void handle_button(xcb_button_press_event_t *event) {
|
||||||
* buffer, then we copy character by character. */
|
* buffer, then we copy character by character. */
|
||||||
int num_quotes = 0;
|
int num_quotes = 0;
|
||||||
size_t namelen = 0;
|
size_t namelen = 0;
|
||||||
for (char *walk = cur_ws->name; *walk != '\0'; walk++) {
|
const char *utf8_name = i3string_as_utf8(cur_ws->name);
|
||||||
|
for (const char *walk = utf8_name; *walk != '\0'; walk++) {
|
||||||
if (*walk == '"')
|
if (*walk == '"')
|
||||||
num_quotes++;
|
num_quotes++;
|
||||||
/* While we’re looping through the name anyway, we can save one
|
/* While we’re looping through the name anyway, we can save one
|
||||||
|
@ -341,11 +337,11 @@ void handle_button(xcb_button_press_event_t *event) {
|
||||||
for (inpos = 0, outpos = strlen("workspace \"");
|
for (inpos = 0, outpos = strlen("workspace \"");
|
||||||
inpos < namelen;
|
inpos < namelen;
|
||||||
inpos++, outpos++) {
|
inpos++, outpos++) {
|
||||||
if (cur_ws->name[inpos] == '"') {
|
if (utf8_name[inpos] == '"') {
|
||||||
buffer[outpos] = '\\';
|
buffer[outpos] = '\\';
|
||||||
outpos++;
|
outpos++;
|
||||||
}
|
}
|
||||||
buffer[outpos] = cur_ws->name[inpos];
|
buffer[outpos] = utf8_name[inpos];
|
||||||
}
|
}
|
||||||
buffer[outpos] = '"';
|
buffer[outpos] = '"';
|
||||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer);
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer);
|
||||||
|
@ -357,7 +353,7 @@ void handle_button(xcb_button_press_event_t *event) {
|
||||||
* new tray client or removing an old one.
|
* new tray client or removing an old one.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void configure_trayclients() {
|
static void configure_trayclients(void) {
|
||||||
trayclient *trayclient;
|
trayclient *trayclient;
|
||||||
i3_output *output;
|
i3_output *output;
|
||||||
SLIST_FOREACH(output, outputs, slist) {
|
SLIST_FOREACH(output, outputs, slist) {
|
||||||
|
@ -828,8 +824,8 @@ char *init_xcb_early() {
|
||||||
#define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
|
#define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
|
||||||
#include "xcb_atoms.def"
|
#include "xcb_atoms.def"
|
||||||
|
|
||||||
xcb_screen = xcb_aux_get_screen(xcb_connection, screen);
|
root_screen = xcb_aux_get_screen(xcb_connection, screen);
|
||||||
xcb_root = xcb_screen->root;
|
xcb_root = root_screen->root;
|
||||||
|
|
||||||
/* We draw the statusline to a seperate pixmap, because it looks the same on all bars and
|
/* We draw the statusline to a seperate pixmap, because it looks the same on all bars and
|
||||||
* this way, we can choose to crop it */
|
* this way, we can choose to crop it */
|
||||||
|
@ -852,11 +848,11 @@ char *init_xcb_early() {
|
||||||
|
|
||||||
statusline_pm = xcb_generate_id(xcb_connection);
|
statusline_pm = xcb_generate_id(xcb_connection);
|
||||||
xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
||||||
xcb_screen->root_depth,
|
root_screen->root_depth,
|
||||||
statusline_pm,
|
statusline_pm,
|
||||||
xcb_root,
|
xcb_root,
|
||||||
xcb_screen->width_in_pixels,
|
root_screen->width_in_pixels,
|
||||||
xcb_screen->height_in_pixels);
|
root_screen->height_in_pixels);
|
||||||
|
|
||||||
|
|
||||||
/* The various Watchers to communicate with xcb */
|
/* The various Watchers to communicate with xcb */
|
||||||
|
@ -970,7 +966,7 @@ void init_xcb_late(char *fontname) {
|
||||||
* atom. Afterwards, tray clients will send ClientMessages to our window.
|
* atom. Afterwards, tray clients will send ClientMessages to our window.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void init_tray() {
|
void init_tray(void) {
|
||||||
DLOG("Initializing system tray functionality\n");
|
DLOG("Initializing system tray functionality\n");
|
||||||
/* request the tray manager atom for the X11 display we are running on */
|
/* request the tray manager atom for the X11 display we are running on */
|
||||||
char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
|
char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
|
||||||
|
@ -984,14 +980,14 @@ void init_tray() {
|
||||||
uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT;
|
uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT;
|
||||||
uint32_t selval[] = { 1 };
|
uint32_t selval[] = { 1 };
|
||||||
xcb_create_window(xcb_connection,
|
xcb_create_window(xcb_connection,
|
||||||
xcb_screen->root_depth,
|
root_screen->root_depth,
|
||||||
selwin,
|
selwin,
|
||||||
xcb_root,
|
xcb_root,
|
||||||
-1, -1,
|
-1, -1,
|
||||||
1, 1,
|
1, 1,
|
||||||
1,
|
1,
|
||||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||||
xcb_screen->root_visual,
|
root_screen->root_visual,
|
||||||
selmask,
|
selmask,
|
||||||
selval);
|
selval);
|
||||||
|
|
||||||
|
@ -1059,7 +1055,7 @@ void init_tray() {
|
||||||
* Called once, before the program terminates.
|
* Called once, before the program terminates.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void clean_xcb() {
|
void clean_xcb(void) {
|
||||||
i3_output *o_walk;
|
i3_output *o_walk;
|
||||||
free_workspaces();
|
free_workspaces();
|
||||||
SLIST_FOREACH(o_walk, outputs, slist) {
|
SLIST_FOREACH(o_walk, outputs, slist) {
|
||||||
|
@ -1087,7 +1083,7 @@ void clean_xcb() {
|
||||||
* Get the earlier requested atoms and save them in the prepared data structure
|
* Get the earlier requested atoms and save them in the prepared data structure
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void get_atoms() {
|
void get_atoms(void) {
|
||||||
xcb_intern_atom_reply_t *reply;
|
xcb_intern_atom_reply_t *reply;
|
||||||
#define ATOM_DO(name) reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \
|
#define ATOM_DO(name) reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \
|
||||||
if (reply == NULL) { \
|
if (reply == NULL) { \
|
||||||
|
@ -1149,17 +1145,17 @@ void destroy_window(i3_output *output) {
|
||||||
* Reallocate the statusline-buffer
|
* Reallocate the statusline-buffer
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void realloc_sl_buffer() {
|
void realloc_sl_buffer(void) {
|
||||||
DLOG("Re-allocating statusline-buffer, statusline_width = %d, xcb_screen->width_in_pixels = %d\n",
|
DLOG("Re-allocating statusline-buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n",
|
||||||
statusline_width, xcb_screen->width_in_pixels);
|
statusline_width, root_screen->width_in_pixels);
|
||||||
xcb_free_pixmap(xcb_connection, statusline_pm);
|
xcb_free_pixmap(xcb_connection, statusline_pm);
|
||||||
statusline_pm = xcb_generate_id(xcb_connection);
|
statusline_pm = xcb_generate_id(xcb_connection);
|
||||||
xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
||||||
xcb_screen->root_depth,
|
root_screen->root_depth,
|
||||||
statusline_pm,
|
statusline_pm,
|
||||||
xcb_root,
|
xcb_root,
|
||||||
MAX(xcb_screen->width_in_pixels, statusline_width),
|
MAX(root_screen->width_in_pixels, statusline_width),
|
||||||
xcb_screen->height_in_pixels);
|
root_screen->height_in_pixels);
|
||||||
|
|
||||||
uint32_t mask = XCB_GC_FOREGROUND;
|
uint32_t mask = XCB_GC_FOREGROUND;
|
||||||
uint32_t vals[2] = { colors.bar_bg, colors.bar_bg };
|
uint32_t vals[2] = { colors.bar_bg, colors.bar_bg };
|
||||||
|
@ -1193,7 +1189,7 @@ void realloc_sl_buffer() {
|
||||||
* Reconfigure all bars and create new bars for recently activated outputs
|
* Reconfigure all bars and create new bars for recently activated outputs
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void reconfig_windows() {
|
void reconfig_windows(void) {
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
uint32_t values[5];
|
uint32_t values[5];
|
||||||
static bool tray_configured = false;
|
static bool tray_configured = false;
|
||||||
|
@ -1229,20 +1225,20 @@ void reconfig_windows() {
|
||||||
values[2] |= XCB_EVENT_MASK_BUTTON_PRESS;
|
values[2] |= XCB_EVENT_MASK_BUTTON_PRESS;
|
||||||
}
|
}
|
||||||
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
|
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
|
||||||
xcb_screen->root_depth,
|
root_screen->root_depth,
|
||||||
walk->bar,
|
walk->bar,
|
||||||
xcb_root,
|
xcb_root,
|
||||||
walk->rect.x, walk->rect.y + walk->rect.h - font.height - 6,
|
walk->rect.x, walk->rect.y + walk->rect.h - font.height - 6,
|
||||||
walk->rect.w, font.height + 6,
|
walk->rect.w, font.height + 6,
|
||||||
1,
|
1,
|
||||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||||
xcb_screen->root_visual,
|
root_screen->root_visual,
|
||||||
mask,
|
mask,
|
||||||
values);
|
values);
|
||||||
|
|
||||||
/* The double-buffer we use to render stuff off-screen */
|
/* The double-buffer we use to render stuff off-screen */
|
||||||
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
||||||
xcb_screen->root_depth,
|
root_screen->root_depth,
|
||||||
walk->buffer,
|
walk->buffer,
|
||||||
walk->bar,
|
walk->bar,
|
||||||
walk->rect.w,
|
walk->rect.w,
|
||||||
|
@ -1382,7 +1378,7 @@ void reconfig_windows() {
|
||||||
|
|
||||||
DLOG("Recreating buffer for output %s", walk->name);
|
DLOG("Recreating buffer for output %s", walk->name);
|
||||||
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
||||||
xcb_screen->root_depth,
|
root_screen->root_depth,
|
||||||
walk->buffer,
|
walk->buffer,
|
||||||
walk->bar,
|
walk->bar,
|
||||||
walk->rect.w,
|
walk->rect.w,
|
||||||
|
@ -1402,7 +1398,7 @@ void reconfig_windows() {
|
||||||
* Render the bars, with buttons and statusline
|
* Render the bars, with buttons and statusline
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void draw_bars() {
|
void draw_bars(void) {
|
||||||
DLOG("Drawing Bars...\n");
|
DLOG("Drawing Bars...\n");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
@ -1464,8 +1460,11 @@ void draw_bars() {
|
||||||
}
|
}
|
||||||
|
|
||||||
i3_ws *ws_walk;
|
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) {
|
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);
|
DLOG("Drawing Button for WS %s at x = %d, len = %d\n", i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
|
||||||
uint32_t fg_color = colors.inactive_ws_fg;
|
uint32_t fg_color = colors.inactive_ws_fg;
|
||||||
uint32_t bg_color = colors.inactive_ws_bg;
|
uint32_t bg_color = colors.inactive_ws_bg;
|
||||||
uint32_t border_color = colors.inactive_ws_border;
|
uint32_t border_color = colors.inactive_ws_border;
|
||||||
|
@ -1478,13 +1477,20 @@ void draw_bars() {
|
||||||
fg_color = colors.focus_ws_fg;
|
fg_color = colors.focus_ws_fg;
|
||||||
bg_color = colors.focus_ws_bg;
|
bg_color = colors.focus_ws_bg;
|
||||||
border_color = colors.focus_ws_border;
|
border_color = colors.focus_ws_border;
|
||||||
|
if (last_urgent_ws && strcmp(i3string_as_utf8(ws_walk->name), last_urgent_ws) == 0)
|
||||||
|
walks_away = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ws_walk->urgent) {
|
if (ws_walk->urgent) {
|
||||||
DLOG("WS %s is urgent!\n", ws_walk->name);
|
DLOG("WS %s is urgent!\n", i3string_as_utf8(ws_walk->name));
|
||||||
fg_color = colors.urgent_ws_fg;
|
fg_color = colors.urgent_ws_fg;
|
||||||
bg_color = colors.urgent_ws_bg;
|
bg_color = colors.urgent_ws_bg;
|
||||||
border_color = colors.urgent_ws_border;
|
border_color = colors.urgent_ws_border;
|
||||||
|
has_urgent = true;
|
||||||
|
if (!ws_walk->focused) {
|
||||||
|
FREE(last_urgent_ws);
|
||||||
|
last_urgent_ws = sstrdup(i3string_as_utf8(ws_walk->name));
|
||||||
|
}
|
||||||
/* The urgent-hint should get noticed, so we unhide the bars shortly */
|
/* The urgent-hint should get noticed, so we unhide the bars shortly */
|
||||||
unhide_bars();
|
unhide_bars();
|
||||||
}
|
}
|
||||||
|
@ -1512,11 +1518,15 @@ void draw_bars() {
|
||||||
1,
|
1,
|
||||||
&rect);
|
&rect);
|
||||||
set_font_colors(outputs_walk->bargc, fg_color, bg_color);
|
set_font_colors(outputs_walk->bargc, fg_color, bg_color);
|
||||||
draw_text((char*)ws_walk->ucs2_name, ws_walk->name_glyphs, true,
|
draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width);
|
||||||
outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width);
|
|
||||||
i += 10 + ws_walk->name_width + 1;
|
i += 10 + ws_walk->name_width + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!has_urgent && !mod_pressed && walks_away) {
|
||||||
|
FREE(last_urgent_ws);
|
||||||
|
hide_bars();
|
||||||
|
}
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1527,7 +1537,7 @@ void draw_bars() {
|
||||||
* Redraw the bars, i.e. simply copy the buffer to the barwindow
|
* Redraw the bars, i.e. simply copy the buffer to the barwindow
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void redraw_bars() {
|
void redraw_bars(void) {
|
||||||
i3_output *outputs_walk;
|
i3_output *outputs_walk;
|
||||||
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||||
if (!outputs_walk->active) {
|
if (!outputs_walk->active) {
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#include "i3.h"
|
#include "i3.h"
|
||||||
#include "x.h"
|
#include "x.h"
|
||||||
#include "click.h"
|
#include "click.h"
|
||||||
|
#include "key_press.h"
|
||||||
#include "floating.h"
|
#include "floating.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "handlers.h"
|
#include "handlers.h"
|
||||||
|
@ -79,5 +80,6 @@
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
#include "commands_parser.h"
|
#include "commands_parser.h"
|
||||||
#include "fake_outputs.h"
|
#include "fake_outputs.h"
|
||||||
|
#include "display_version.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,3 +27,4 @@ xmacro(I3_SOCKET_PATH)
|
||||||
xmacro(I3_CONFIG_PATH)
|
xmacro(I3_CONFIG_PATH)
|
||||||
xmacro(I3_SYNC)
|
xmacro(I3_SYNC)
|
||||||
xmacro(I3_SHMLOG_PATH)
|
xmacro(I3_SHMLOG_PATH)
|
||||||
|
xmacro(I3_PID)
|
||||||
|
|
|
@ -200,11 +200,17 @@ void cmd_fullscreen(I3_CMD, char *fullscreen_mode);
|
||||||
void cmd_move_direction(I3_CMD, char *direction, char *move_px);
|
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);
|
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'.
|
* Implementaiton of 'exit'.
|
||||||
*
|
*
|
||||||
|
|
|
@ -221,6 +221,12 @@ Con *con_descend_direction(Con *con, direction_t direction);
|
||||||
*/
|
*/
|
||||||
Rect con_border_style_rect(Con *con);
|
Rect con_border_style_rect(Con *con);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns adjacent borders of the window. We need this if hide_edge_borders is
|
||||||
|
* enabled.
|
||||||
|
*/
|
||||||
|
adjacent_t con_adjacent_borders(Con *con);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this function to get a container’s border style. This is important
|
* Use this function to get a container’s border style. This is important
|
||||||
* because when inside a stack, the border style is always BS_NORMAL.
|
* because when inside a stack, the border style is always BS_NORMAL.
|
||||||
|
@ -248,6 +254,15 @@ void con_set_border_style(Con *con, int border_style);
|
||||||
*/
|
*/
|
||||||
void con_set_layout(Con *con, int layout);
|
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
|
* 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
|
* split/stacked/tabbed cons). Will be called when resizing floating cons
|
||||||
|
@ -255,4 +270,27 @@ void con_set_layout(Con *con, int layout);
|
||||||
*/
|
*/
|
||||||
Rect con_minimum_size(Con *con);
|
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
|
#endif
|
||||||
|
|
|
@ -108,6 +108,12 @@ struct Config {
|
||||||
* It is not planned to add any different focus models. */
|
* It is not planned to add any different focus models. */
|
||||||
bool disable_focus_follows_mouse;
|
bool disable_focus_follows_mouse;
|
||||||
|
|
||||||
|
/** Remove borders if they are adjacent to the screen edge.
|
||||||
|
* This is useful if you are reaching scrollbar on the edge of the
|
||||||
|
* screen or do not want to waste a single pixel of displayspace.
|
||||||
|
* By default, this is disabled. */
|
||||||
|
adjacent_t hide_edge_borders;
|
||||||
|
|
||||||
/** By default, a workspace bar is drawn at the bottom of the screen.
|
/** By default, a workspace bar is drawn at the bottom of the screen.
|
||||||
* If you want to have a more fancy bar, it is recommended to replace
|
* If you want to have a more fancy bar, it is recommended to replace
|
||||||
* the whole bar by dzen2, for example using the i3-wsbar script which
|
* the whole bar by dzen2, for example using the i3-wsbar script which
|
||||||
|
@ -306,7 +312,7 @@ void switch_mode(const char *new_mode);
|
||||||
* or NULL if no such binding exists.
|
* or NULL if no such binding exists.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode);
|
Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kills the configerror i3-nagbar process, if any.
|
* Kills the configerror i3-nagbar process, if any.
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <pcre.h>
|
#include <pcre.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "libi3.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -60,6 +61,13 @@ typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_1PIXEL = 2 } border_style_t;
|
||||||
* only this specific window or the whole X11 client */
|
* only this specific window or the whole X11 client */
|
||||||
typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_window_t;
|
typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_window_t;
|
||||||
|
|
||||||
|
/** describes if the window is adjacent to the output (physical screen) edges. */
|
||||||
|
typedef enum { ADJ_NONE = 0,
|
||||||
|
ADJ_LEFT_SCREEN_EDGE = (1 << 0),
|
||||||
|
ADJ_RIGHT_SCREEN_EDGE = (1 << 1),
|
||||||
|
ADJ_UPPER_SCREEN_EDGE = (1 << 2),
|
||||||
|
ADJ_LOWER_SCREEN_EDGE = (1 << 4)} adjacent_t;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
BIND_NONE = 0,
|
BIND_NONE = 0,
|
||||||
BIND_SHIFT = XCB_MOD_MASK_SHIFT, /* (1 << 0) */
|
BIND_SHIFT = XCB_MOD_MASK_SHIFT, /* (1 << 0) */
|
||||||
|
@ -160,6 +168,9 @@ struct Startup_Sequence {
|
||||||
char *workspace;
|
char *workspace;
|
||||||
/** libstartup-notification context for this launch */
|
/** libstartup-notification context for this launch */
|
||||||
SnLauncherContext *context;
|
SnLauncherContext *context;
|
||||||
|
/** time at which this sequence should be deleted (after it was marked as
|
||||||
|
* completed) */
|
||||||
|
time_t delete_at;
|
||||||
|
|
||||||
TAILQ_ENTRY(Startup_Sequence) sequences;
|
TAILQ_ENTRY(Startup_Sequence) sequences;
|
||||||
};
|
};
|
||||||
|
@ -189,6 +200,20 @@ struct regex {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct Binding {
|
struct Binding {
|
||||||
|
/** If true, the binding should be executed upon a KeyRelease event, not a
|
||||||
|
* KeyPress (the default). */
|
||||||
|
enum {
|
||||||
|
/* This binding will only be executed upon KeyPress events */
|
||||||
|
B_UPON_KEYPRESS = 0,
|
||||||
|
/* This binding will be executed either upon a KeyRelease event, or… */
|
||||||
|
B_UPON_KEYRELEASE = 1,
|
||||||
|
/* …upon a KeyRelease event, even if the modifiers don’t match. This
|
||||||
|
* state is triggered from get_binding() when the corresponding
|
||||||
|
* KeyPress (!) happens, so that users can release the modifier keys
|
||||||
|
* before releasing the actual key. */
|
||||||
|
B_UPON_KEYRELEASE_IGNORE_MODS = 2,
|
||||||
|
} release;
|
||||||
|
|
||||||
/** Symbol the user specified in configfile, if any. This needs to be
|
/** Symbol the user specified in configfile, if any. This needs to be
|
||||||
* stored with the binding to be able to re-convert it into a keycode
|
* stored with the binding to be able to re-convert it into a keycode
|
||||||
* if the keyboard mapping changes (using Xmodmap for example) */
|
* if the keyboard mapping changes (using Xmodmap for example) */
|
||||||
|
@ -280,9 +305,8 @@ struct Window {
|
||||||
char *class_class;
|
char *class_class;
|
||||||
char *class_instance;
|
char *class_instance;
|
||||||
|
|
||||||
/** The name of the window as it will be passed to X11 (in UCS2 if the
|
/** The name of the window. */
|
||||||
* application supports _NET_WM_NAME, in COMPOUND_TEXT otherwise). */
|
i3String *name;
|
||||||
char *name_x;
|
|
||||||
|
|
||||||
/** The WM_WINDOW_ROLE of this window (for example, the pidgin buddy window
|
/** The WM_WINDOW_ROLE of this window (for example, the pidgin buddy window
|
||||||
* sets "buddy list"). Useful to match specific windows in assignments or
|
* sets "buddy list"). Useful to match specific windows in assignments or
|
||||||
|
@ -292,13 +316,6 @@ struct Window {
|
||||||
/** Flag to force re-rendering the decoration upon changes */
|
/** Flag to force re-rendering the decoration upon changes */
|
||||||
bool name_x_changed;
|
bool name_x_changed;
|
||||||
|
|
||||||
/** The name of the window as used in JSON (in UTF-8 if the application
|
|
||||||
* supports _NET_WM_NAME, in COMPOUND_TEXT otherwise) */
|
|
||||||
char *name_json;
|
|
||||||
|
|
||||||
/** The length of the name in glyphs (not bytes) */
|
|
||||||
size_t name_len;
|
|
||||||
|
|
||||||
/** Whether the application used _NET_WM_NAME */
|
/** Whether the application used _NET_WM_NAME */
|
||||||
bool uses_net_wm_name;
|
bool uses_net_wm_name;
|
||||||
|
|
||||||
|
@ -423,6 +440,8 @@ struct Assignment {
|
||||||
*/
|
*/
|
||||||
struct Con {
|
struct Con {
|
||||||
bool mapped;
|
bool mapped;
|
||||||
|
/** whether this is a split container or not */
|
||||||
|
bool split;
|
||||||
enum {
|
enum {
|
||||||
CT_ROOT = 0,
|
CT_ROOT = 0,
|
||||||
CT_OUTPUT = 1,
|
CT_OUTPUT = 1,
|
||||||
|
@ -431,7 +450,6 @@ struct Con {
|
||||||
CT_WORKSPACE = 4,
|
CT_WORKSPACE = 4,
|
||||||
CT_DOCKAREA = 5
|
CT_DOCKAREA = 5
|
||||||
} type;
|
} type;
|
||||||
orientation_t orientation;
|
|
||||||
struct Con *parent;
|
struct Con *parent;
|
||||||
|
|
||||||
struct Rect rect;
|
struct Rect rect;
|
||||||
|
@ -496,7 +514,29 @@ struct Con {
|
||||||
TAILQ_HEAD(swallow_head, Match) swallow_head;
|
TAILQ_HEAD(swallow_head, Match) swallow_head;
|
||||||
|
|
||||||
enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode;
|
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;
|
/* layout is the layout of this container: one of split[v|h], stacked or
|
||||||
|
* tabbed. Special containers in the tree (above workspaces) have special
|
||||||
|
* layouts like dockarea or output.
|
||||||
|
*
|
||||||
|
* last_split_layout is one of splitv or splith to support the old "layout
|
||||||
|
* default" command which by now should be "layout splitv" or "layout
|
||||||
|
* splith" explicitly.
|
||||||
|
*
|
||||||
|
* workspace_layout is only for type == CT_WORKSPACE cons. When you change
|
||||||
|
* the layout of a workspace without any children, i3 cannot just set the
|
||||||
|
* layout (because workspaces need to be splitv/splith to allow focus
|
||||||
|
* parent and opening new containers). Instead, it stores the requested
|
||||||
|
* layout in workspace_layout and creates a new split container with that
|
||||||
|
* layout whenever a new container is attached to the workspace. */
|
||||||
|
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, workspace_layout;
|
||||||
border_style_t border_style;
|
border_style_t border_style;
|
||||||
/** floating? (= not in tiling layout) This cannot be simply a bool
|
/** 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
|
* because we want to keep track of whether the status was set by the
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* display_version.c: displays the running i3 version, runs as part of
|
||||||
|
* i3 --moreversion.
|
||||||
|
*/
|
||||||
|
#ifndef _DISPLAY_VERSION_H
|
||||||
|
#define _DISPLAY_VERSION_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to i3 to find out the currently running version. Useful since it
|
||||||
|
* might be different from the version compiled into this binary (maybe the
|
||||||
|
* user didn’t correctly install i3 or forgot te restart it).
|
||||||
|
*
|
||||||
|
* The output looks like this:
|
||||||
|
* Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804)
|
||||||
|
*
|
||||||
|
* The i3 binary you just called: /home/michael/i3/i3
|
||||||
|
* The i3 binary you are running: /home/michael/i3/i3
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void display_running_version(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,7 +2,7 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
* © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
|
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* This public header defines the different constants and message types to use
|
* This public header defines the different constants and message types to use
|
||||||
* for the IPC interface to i3 (see docs/ipc for more information).
|
* for the IPC interface to i3 (see docs/ipc for more information).
|
||||||
|
@ -40,6 +40,9 @@
|
||||||
/** Request the configuration for a specific 'bar' */
|
/** Request the configuration for a specific 'bar' */
|
||||||
#define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6
|
#define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6
|
||||||
|
|
||||||
|
/** Request the i3 version */
|
||||||
|
#define I3_IPC_MESSAGE_TYPE_GET_VERSION 7
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Messages from i3 to clients
|
* Messages from i3 to clients
|
||||||
*
|
*
|
||||||
|
@ -66,6 +69,9 @@
|
||||||
/** Bar config reply type */
|
/** Bar config reply type */
|
||||||
#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
|
#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
|
||||||
|
|
||||||
|
/** i3 version reply type */
|
||||||
|
#define I3_IPC_REPLY_TYPE_VERSION 7
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Events from i3 to clients. Events have the first bit set high.
|
* Events from i3 to clients. Events have the first bit set high.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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
|
140
include/libi3.h
140
include/libi3.h
|
@ -18,6 +18,16 @@
|
||||||
#include <xcb/xproto.h>
|
#include <xcb/xproto.h>
|
||||||
#include <xcb/xcb_keysyms.h>
|
#include <xcb/xcb_keysyms.h>
|
||||||
|
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
#include <pango/pango.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opaque data structure for storing strings.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct _i3String i3String;
|
||||||
|
|
||||||
typedef struct Font i3Font;
|
typedef struct Font i3Font;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,23 +37,44 @@ typedef struct Font i3Font;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct Font {
|
struct Font {
|
||||||
/** The xcb-id for the font */
|
/** The type of font */
|
||||||
xcb_font_t id;
|
enum {
|
||||||
|
FONT_TYPE_NONE = 0,
|
||||||
/** Font information gathered from the server */
|
FONT_TYPE_XCB,
|
||||||
xcb_query_font_reply_t *info;
|
FONT_TYPE_PANGO
|
||||||
|
} type;
|
||||||
/** Font table for this font (may be NULL) */
|
|
||||||
xcb_charinfo_t *table;
|
|
||||||
|
|
||||||
/** The height of the font, built from font_ascent + font_descent */
|
/** The height of the font, built from font_ascent + font_descent */
|
||||||
int height;
|
int height;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
/** The xcb-id for the font */
|
||||||
|
xcb_font_t id;
|
||||||
|
|
||||||
|
/** Font information gathered from the server */
|
||||||
|
xcb_query_font_reply_t *info;
|
||||||
|
|
||||||
|
/** Font table for this font (may be NULL) */
|
||||||
|
xcb_charinfo_t *table;
|
||||||
|
} xcb;
|
||||||
|
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
/** The pango font description */
|
||||||
|
PangoFontDescription *pango_desc;
|
||||||
|
#endif
|
||||||
|
} specific;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Since this file also gets included by utilities which don’t use the i3 log
|
/* Since this file also gets included by utilities which don’t use the i3 log
|
||||||
* infrastructure, we define a fallback. */
|
* infrastructure, we define a fallback. */
|
||||||
|
#if !defined(LOG)
|
||||||
|
void verboselog(char *fmt, ...);
|
||||||
|
#define LOG(fmt, ...) verboselog("[libi3] " __FILE__ " " fmt, ##__VA_ARGS__)
|
||||||
|
#endif
|
||||||
#if !defined(ELOG)
|
#if !defined(ELOG)
|
||||||
#define ELOG(fmt, ...) fprintf(stderr, "ERROR: " fmt, ##__VA_ARGS__)
|
void errorlog(char *fmt, ...);
|
||||||
|
#define ELOG(fmt, ...) errorlog("[libi3] ERROR: " fmt, ##__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,6 +122,71 @@ char *sstrdup(const char *str);
|
||||||
*/
|
*/
|
||||||
int sasprintf(char **strp, const char *fmt, ...);
|
int sasprintf(char **strp, const char *fmt, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an i3String from an UTF-8 encoded string.
|
||||||
|
* Returns the newly-allocated i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
i3String *i3string_from_utf8(const char *from_utf8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an i3String from an UTF-8 encoded string with fixed length.
|
||||||
|
* To be used when no proper NUL-terminaison is available.
|
||||||
|
* Returns the newly-allocated i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an i3String from an UCS-2 encoded string.
|
||||||
|
* Returns the newly-allocated i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free an i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void i3string_free(i3String *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Securely i3string_free by setting the pointer to NULL
|
||||||
|
* to prevent accidentally using freed memory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define I3STRING_FREE(str) \
|
||||||
|
do { \
|
||||||
|
if (str != NULL) { \
|
||||||
|
i3string_free(str); \
|
||||||
|
str = NULL; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the UTF-8 encoded version of the i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const char *i3string_as_utf8(i3String *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the UCS-2 encoded version of the i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const xcb_char2b_t *i3string_as_ucs2(i3String *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bytes (UTF-8 encoded) in an i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
size_t i3string_get_num_bytes(i3String *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of glyphs in an i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
size_t i3string_get_num_glyphs(i3String *str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to the i3 IPC socket and returns the file descriptor for the
|
* Connects to the i3 IPC socket and returns the file descriptor for the
|
||||||
* socket. die()s if anything goes wrong.
|
* socket. die()s if anything goes wrong.
|
||||||
|
@ -226,21 +322,31 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background
|
||||||
* specified coordinates (from the top left corner of the leftmost, uppermost
|
* specified coordinates (from the top left corner of the leftmost, uppermost
|
||||||
* glyph) and using the provided gc.
|
* glyph) and using the provided gc.
|
||||||
*
|
*
|
||||||
* Text can be specified as UCS-2 or UTF-8. If it's specified as UCS-2, then
|
* Text must be specified as an i3String.
|
||||||
* text_len must be the number of glyphs in the string. If it's specified as
|
|
||||||
* UTF-8, then text_len must be the number of bytes in the string (not counting
|
|
||||||
* the null terminator).
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void draw_text(char *text, size_t text_len, bool is_ucs2, xcb_drawable_t drawable,
|
void draw_text(i3String *text, xcb_drawable_t drawable,
|
||||||
xcb_gcontext_t gc, int x, int y, int max_width);
|
xcb_gcontext_t gc, int x, int y, int max_width);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Predict the text width in pixels for the given text. Text can be specified
|
* ASCII version of draw_text to print static strings.
|
||||||
* as UCS-2 or UTF-8.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int predict_text_width(char *text, size_t text_len, bool is_ucs2);
|
void draw_text_ascii(const char *text, xcb_drawable_t drawable,
|
||||||
|
xcb_gcontext_t gc, int x, int y, int max_width);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predict the text width in pixels for the given text. Text must be
|
||||||
|
* specified as an i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int predict_text_width(i3String *text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the visual type associated with the given screen.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
xcb_visualtype_t *get_visualtype(xcb_screen_t *screen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this version of i3 is a debug build (anything which is not a
|
* Returns true if this version of i3 is a debug build (anything which is not a
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* i3 - an improved dynamic tiling window manager
|
* i3 - an improved dynamic tiling window manager
|
||||||
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
*
|
*
|
||||||
* log.c: Setting of loglevels, logging functions.
|
* log.c: Logging functions.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _LOG_H
|
#ifndef _LOG_H
|
||||||
|
@ -13,13 +13,20 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* We will include libi3.h which define its own version of LOG, ELOG.
|
||||||
|
* We want *our* version, so we undef the libi3 one. */
|
||||||
|
#if defined(LOG)
|
||||||
|
#undef LOG
|
||||||
|
#endif
|
||||||
|
#if defined(ELOG)
|
||||||
|
#undef ELOG
|
||||||
|
#endif
|
||||||
/** ##__VA_ARGS__ means: leave out __VA_ARGS__ completely if it is empty, that
|
/** ##__VA_ARGS__ means: leave out __VA_ARGS__ completely if it is empty, that
|
||||||
is, delete the preceding comma */
|
is, delete the preceding comma */
|
||||||
#define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__)
|
#define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__)
|
||||||
#define ELOG(fmt, ...) errorlog("ERROR: " 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, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
extern char *loglevels[];
|
|
||||||
extern char *errorfilename;
|
extern char *errorfilename;
|
||||||
extern char *shmlogname;
|
extern char *shmlogname;
|
||||||
extern int shmlog_size;
|
extern int shmlog_size;
|
||||||
|
@ -32,10 +39,10 @@ extern int shmlog_size;
|
||||||
void init_logging(void);
|
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
|
* Set verbosity of i3. If verbose is set to true, informative messages will
|
||||||
|
@ -47,29 +54,32 @@ void set_verbosity(bool _verbose);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the given message to stdout while prefixing the current time to it,
|
* 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, ...)
|
||||||
|
__attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the given message to stdout while prefixing the current time to it.
|
* Logs the given message to stdout while prefixing the current time to it.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void errorlog(char *fmt, ...);
|
void errorlog(char *fmt, ...)
|
||||||
|
__attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the given message to stdout while prefixing the current time to it,
|
* Logs the given message to stdout while prefixing the current time to it,
|
||||||
* but only if verbose mode is activated.
|
* but only if verbose mode is activated.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void verboselog(char *fmt, ...);
|
void verboselog(char *fmt, ...)
|
||||||
|
__attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the given message to stdout while prefixing the current time to it.
|
* Deletes the unused log files. Useful if i3 exits immediately, eg.
|
||||||
* This is to be called by LOG() which includes filename/linenumber
|
* because --get-socketpath was called. We don't care for syscall
|
||||||
*
|
* failures. This function is invoked automatically when exiting.
|
||||||
*/
|
*/
|
||||||
void slog(char *fmt, va_list args);
|
void purge_zerobyte_logfile(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,7 +31,7 @@ void regex_free(struct regex *regex);
|
||||||
/**
|
/**
|
||||||
* Checks if the given regular expression matches the given input and returns
|
* 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
|
* 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);
|
bool regex_matches(struct regex *regex, const char *input);
|
||||||
|
|
|
@ -30,4 +30,14 @@ void scratchpad_move(Con *con);
|
||||||
*/
|
*/
|
||||||
void scratchpad_show(Con *con);
|
void scratchpad_show(Con *con);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When starting i3 initially (and after each change to the connected outputs),
|
||||||
|
* this function fixes the resolution of the __i3 pseudo-output. When that
|
||||||
|
* resolution is not set to a function which shares a common divisor with every
|
||||||
|
* active output’s resolution, floating point calculation errors will lead to
|
||||||
|
* the scratchpad window moving when shown repeatedly.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void scratchpad_fix_resolution(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,11 +12,33 @@
|
||||||
#define _I3_SHMLOG_H
|
#define _I3_SHMLOG_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Header of the shmlog file. Used by i3/src/log.c and i3/i3-dump-log/main.c.
|
||||||
|
*
|
||||||
|
*/
|
||||||
typedef struct i3_shmlog_header {
|
typedef struct i3_shmlog_header {
|
||||||
|
/* Byte offset where the next line will be written to. */
|
||||||
uint32_t offset_next_write;
|
uint32_t offset_next_write;
|
||||||
|
|
||||||
|
/* Byte offset where the last wrap occured. */
|
||||||
uint32_t offset_last_wrap;
|
uint32_t offset_last_wrap;
|
||||||
|
|
||||||
|
/* The size of the logfile in bytes. Since the size is limited to 25 MiB
|
||||||
|
* an uint32_t is sufficient. */
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
|
||||||
|
/* wrap counter. We need it to reliably signal to clients that we just
|
||||||
|
* wrapped (clients cannot use offset_last_wrap because that might
|
||||||
|
* coincidentally be exactly the same as previously). Overflows can happen
|
||||||
|
* and don’t matter — clients use an equality check (==). */
|
||||||
|
uint32_t wrap_count;
|
||||||
|
|
||||||
|
/* pthread condvar which will be broadcasted whenever there is a new
|
||||||
|
* message in the log. i3-dump-log uses this to implement -f (follow, like
|
||||||
|
* tail -f) in an efficient way. */
|
||||||
|
pthread_cond_t condvar;
|
||||||
} i3_shmlog_header;
|
} i3_shmlog_header;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -39,16 +39,16 @@ Con *tree_open_con(Con *con, i3Window *window);
|
||||||
void tree_split(Con *con, orientation_t orientation);
|
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
|
* Renders the tree, that is rendering all outputs using render_con() and
|
||||||
|
|
|
@ -1,26 +1,7 @@
|
||||||
# Default value so one can compile i3-msg standalone
|
all:
|
||||||
TOPDIR=..
|
$(MAKE) -C .. libi3.a
|
||||||
|
|
||||||
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}
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o libi3.a
|
$(MAKE) -C .. clean-libi3
|
||||||
|
|
||||||
distclean: clean
|
.PHONY: all clean
|
||||||
|
|
374
libi3/font.c
374
libi3/font.c
|
@ -12,11 +12,125 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
#include <cairo/cairo-xcb.h>
|
||||||
|
#include <pango/pangocairo.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "libi3.h"
|
#include "libi3.h"
|
||||||
|
|
||||||
extern xcb_connection_t *conn;
|
extern xcb_connection_t *conn;
|
||||||
|
extern xcb_screen_t *root_screen;
|
||||||
|
|
||||||
static const i3Font *savedFont = NULL;
|
static const i3Font *savedFont = NULL;
|
||||||
|
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
static xcb_visualtype_t *root_visual_type;
|
||||||
|
static double pango_font_red;
|
||||||
|
static double pango_font_green;
|
||||||
|
static double pango_font_blue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loads a Pango font description into an i3Font structure. Returns true
|
||||||
|
* on success, false otherwise.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static bool load_pango_font(i3Font *font, const char *desc) {
|
||||||
|
/* Load the font description */
|
||||||
|
font->specific.pango_desc = pango_font_description_from_string(desc);
|
||||||
|
if (!font->specific.pango_desc) {
|
||||||
|
ELOG("Could not open font %s with Pango, fallback to X font.\n", desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("Using Pango font %s, size %d\n",
|
||||||
|
pango_font_description_get_family(font->specific.pango_desc),
|
||||||
|
pango_font_description_get_size(font->specific.pango_desc) / PANGO_SCALE
|
||||||
|
);
|
||||||
|
|
||||||
|
/* We cache root_visual_type here, since you must call
|
||||||
|
* load_pango_font before any other pango function
|
||||||
|
* that would need root_visual_type */
|
||||||
|
root_visual_type = get_visualtype(root_screen);
|
||||||
|
|
||||||
|
/* Create a dummy Pango layout to compute the font height */
|
||||||
|
cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1);
|
||||||
|
cairo_t *cr = cairo_create(surface);
|
||||||
|
PangoLayout *layout = pango_cairo_create_layout(cr);
|
||||||
|
pango_layout_set_font_description(layout, font->specific.pango_desc);
|
||||||
|
|
||||||
|
/* Get the font height */
|
||||||
|
gint height;
|
||||||
|
pango_layout_get_pixel_size(layout, NULL, &height);
|
||||||
|
font->height = height;
|
||||||
|
|
||||||
|
/* Free resources */
|
||||||
|
g_object_unref(layout);
|
||||||
|
cairo_destroy(cr);
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
|
||||||
|
/* Set the font type and return successfully */
|
||||||
|
font->type = FONT_TYPE_PANGO;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Draws text using Pango rendering.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void draw_text_pango(const char *text, size_t text_len,
|
||||||
|
xcb_drawable_t drawable, int x, int y, int max_width) {
|
||||||
|
/* Create the Pango layout */
|
||||||
|
/* root_visual_type is cached in load_pango_font */
|
||||||
|
cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable,
|
||||||
|
root_visual_type, x + max_width, y + savedFont->height);
|
||||||
|
cairo_t *cr = cairo_create(surface);
|
||||||
|
PangoLayout *layout = pango_cairo_create_layout(cr);
|
||||||
|
pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
|
||||||
|
pango_layout_set_width(layout, max_width * PANGO_SCALE);
|
||||||
|
pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
|
||||||
|
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
|
||||||
|
|
||||||
|
/* Do the drawing */
|
||||||
|
cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue);
|
||||||
|
cairo_move_to(cr, x, y);
|
||||||
|
pango_layout_set_text(layout, text, text_len);
|
||||||
|
pango_cairo_update_layout(cr, layout);
|
||||||
|
pango_cairo_show_layout(cr, layout);
|
||||||
|
|
||||||
|
/* Free resources */
|
||||||
|
g_object_unref(layout);
|
||||||
|
cairo_destroy(cr);
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the text width using Pango rendering.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int predict_text_width_pango(const char *text, size_t text_len) {
|
||||||
|
/* Create a dummy Pango layout */
|
||||||
|
/* root_visual_type is cached in load_pango_font */
|
||||||
|
cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1);
|
||||||
|
cairo_t *cr = cairo_create(surface);
|
||||||
|
PangoLayout *layout = pango_cairo_create_layout(cr);
|
||||||
|
|
||||||
|
/* Get the font width */
|
||||||
|
gint width;
|
||||||
|
pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
|
||||||
|
pango_layout_set_text(layout, text, text_len);
|
||||||
|
pango_cairo_update_layout(cr, layout);
|
||||||
|
pango_layout_get_pixel_size(layout, &width, NULL);
|
||||||
|
|
||||||
|
/* Free resources */
|
||||||
|
g_object_unref(layout);
|
||||||
|
cairo_destroy(cr);
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
|
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loads a font for usage, also getting its metrics. If fallback is true,
|
* Loads a font for usage, also getting its metrics. If fallback is true,
|
||||||
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting.
|
* the fonts 'fixed' or '-misc-*' will be loaded instead of exiting.
|
||||||
|
@ -24,12 +138,22 @@ static const i3Font *savedFont = NULL;
|
||||||
*/
|
*/
|
||||||
i3Font load_font(const char *pattern, const bool fallback) {
|
i3Font load_font(const char *pattern, const bool fallback) {
|
||||||
i3Font font;
|
i3Font font;
|
||||||
|
font.type = FONT_TYPE_NONE;
|
||||||
|
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
/* Try to load a pango font if specified */
|
||||||
|
if (strlen(pattern) > strlen("xft:") && !strncmp(pattern, "xft:", strlen("xft:"))) {
|
||||||
|
pattern += strlen("xft:");
|
||||||
|
if (load_pango_font(&font, pattern))
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Send all our requests first */
|
/* Send all our requests first */
|
||||||
font.id = xcb_generate_id(conn);
|
font.specific.xcb.id = xcb_generate_id(conn);
|
||||||
xcb_void_cookie_t font_cookie = xcb_open_font_checked(conn, font.id,
|
xcb_void_cookie_t font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id,
|
||||||
strlen(pattern), pattern);
|
strlen(pattern), pattern);
|
||||||
xcb_query_font_cookie_t info_cookie = xcb_query_font(conn, font.id);
|
xcb_query_font_cookie_t info_cookie = xcb_query_font(conn, font.specific.xcb.id);
|
||||||
|
|
||||||
/* Check for errors. If errors, fall back to default font. */
|
/* Check for errors. If errors, fall back to default font. */
|
||||||
xcb_generic_error_t *error;
|
xcb_generic_error_t *error;
|
||||||
|
@ -40,8 +164,9 @@ i3Font load_font(const char *pattern, const bool fallback) {
|
||||||
ELOG("Could not open font %s (X error %d). Trying fallback to 'fixed'.\n",
|
ELOG("Could not open font %s (X error %d). Trying fallback to 'fixed'.\n",
|
||||||
pattern, error->error_code);
|
pattern, error->error_code);
|
||||||
pattern = "fixed";
|
pattern = "fixed";
|
||||||
font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern);
|
font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id,
|
||||||
info_cookie = xcb_query_font(conn, font.id);
|
strlen(pattern), pattern);
|
||||||
|
info_cookie = xcb_query_font(conn, font.specific.xcb.id);
|
||||||
|
|
||||||
/* Check if we managed to open 'fixed' */
|
/* Check if we managed to open 'fixed' */
|
||||||
error = xcb_request_check(conn, font_cookie);
|
error = xcb_request_check(conn, font_cookie);
|
||||||
|
@ -50,8 +175,9 @@ i3Font load_font(const char *pattern, const bool fallback) {
|
||||||
if (error != NULL) {
|
if (error != NULL) {
|
||||||
ELOG("Could not open fallback font 'fixed', trying with '-misc-*'.\n");
|
ELOG("Could not open fallback font 'fixed', trying with '-misc-*'.\n");
|
||||||
pattern = "-misc-*";
|
pattern = "-misc-*";
|
||||||
font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern);
|
font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id,
|
||||||
info_cookie = xcb_query_font(conn, font.id);
|
strlen(pattern), pattern);
|
||||||
|
info_cookie = xcb_query_font(conn, font.specific.xcb.id);
|
||||||
|
|
||||||
if ((error = xcb_request_check(conn, font_cookie)) != NULL)
|
if ((error = xcb_request_check(conn, font_cookie)) != NULL)
|
||||||
errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks "
|
errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks "
|
||||||
|
@ -59,19 +185,23 @@ i3Font load_font(const char *pattern, const bool fallback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG("Using X font %s\n", pattern);
|
||||||
|
|
||||||
/* Get information (height/name) for this font */
|
/* Get information (height/name) for this font */
|
||||||
if (!(font.info = xcb_query_font_reply(conn, info_cookie, NULL)))
|
if (!(font.specific.xcb.info = xcb_query_font_reply(conn, info_cookie, NULL)))
|
||||||
errx(EXIT_FAILURE, "Could not load font \"%s\"", pattern);
|
errx(EXIT_FAILURE, "Could not load font \"%s\"", pattern);
|
||||||
|
|
||||||
/* Get the font table, if possible */
|
/* Get the font table, if possible */
|
||||||
if (xcb_query_font_char_infos_length(font.info) == 0)
|
if (xcb_query_font_char_infos_length(font.specific.xcb.info) == 0)
|
||||||
font.table = NULL;
|
font.specific.xcb.table = NULL;
|
||||||
else
|
else
|
||||||
font.table = xcb_query_font_char_infos(font.info);
|
font.specific.xcb.table = xcb_query_font_char_infos(font.specific.xcb.info);
|
||||||
|
|
||||||
/* Calculate the font height */
|
/* Calculate the font height */
|
||||||
font.height = font.info->font_ascent + font.info->font_descent;
|
font.height = font.specific.xcb.info->font_ascent + font.specific.xcb.info->font_descent;
|
||||||
|
|
||||||
|
/* Set the font type and return successfully */
|
||||||
|
font.type = FONT_TYPE_XCB;
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,10 +218,27 @@ void set_font(i3Font *font) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void free_font(void) {
|
void free_font(void) {
|
||||||
/* Close the font and free the info */
|
switch (savedFont->type) {
|
||||||
xcb_close_font(conn, savedFont->id);
|
case FONT_TYPE_NONE:
|
||||||
if (savedFont->info)
|
/* Nothing to do */
|
||||||
free(savedFont->info);
|
break;
|
||||||
|
case FONT_TYPE_XCB: {
|
||||||
|
/* Close the font and free the info */
|
||||||
|
xcb_close_font(conn, savedFont->specific.xcb.id);
|
||||||
|
if (savedFont->specific.xcb.info)
|
||||||
|
free(savedFont->specific.xcb.info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
case FONT_TYPE_PANGO:
|
||||||
|
/* Free the font description */
|
||||||
|
pango_font_description_free(savedFont->specific.pango_desc);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -100,50 +247,49 @@ void free_font(void) {
|
||||||
*/
|
*/
|
||||||
void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background) {
|
void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background) {
|
||||||
assert(savedFont != NULL);
|
assert(savedFont != NULL);
|
||||||
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
|
|
||||||
uint32_t values[] = { foreground, background, savedFont->id };
|
switch (savedFont->type) {
|
||||||
xcb_change_gc(conn, gc, mask, values);
|
case FONT_TYPE_NONE:
|
||||||
|
/* Nothing to do */
|
||||||
|
break;
|
||||||
|
case FONT_TYPE_XCB: {
|
||||||
|
/* Change the font and colors in the GC */
|
||||||
|
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
|
||||||
|
uint32_t values[] = { foreground, background, savedFont->specific.xcb.id };
|
||||||
|
xcb_change_gc(conn, gc, mask, values);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
case FONT_TYPE_PANGO:
|
||||||
|
/* Save the foreground font */
|
||||||
|
pango_font_red = ((foreground >> 16) & 0xff) / 255.0;
|
||||||
|
pango_font_green = ((foreground >> 8) & 0xff) / 255.0;
|
||||||
|
pango_font_blue = (foreground & 0xff) / 255.0;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int predict_text_width_xcb(const xcb_char2b_t *text, size_t text_len);
|
||||||
* Draws text onto the specified X drawable (normally a pixmap) at the
|
|
||||||
* specified coordinates (from the top left corner of the leftmost, uppermost
|
static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawable_t drawable,
|
||||||
* glyph) and using the provided gc.
|
|
||||||
*
|
|
||||||
* Text can be specified as UCS-2 or UTF-8. If it's specified as UCS-2, then
|
|
||||||
* text_len must be the number of glyphs in the string. If it's specified as
|
|
||||||
* UTF-8, then text_len must be the number of bytes in the string (not counting
|
|
||||||
* the null terminator).
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void draw_text(char *text, size_t text_len, bool is_ucs2, xcb_drawable_t drawable,
|
|
||||||
xcb_gcontext_t gc, int x, int y, int max_width) {
|
xcb_gcontext_t gc, int x, int y, int max_width) {
|
||||||
assert(savedFont != NULL);
|
|
||||||
assert(text_len != 0);
|
|
||||||
|
|
||||||
/* X11 coordinates for fonts start at the baseline */
|
/* X11 coordinates for fonts start at the baseline */
|
||||||
int pos_y = y + savedFont->info->font_ascent;
|
int pos_y = y + savedFont->specific.xcb.info->font_ascent;
|
||||||
|
|
||||||
/* As an optimization, check if we can bypass conversion */
|
|
||||||
if (!is_ucs2 && text_len <= 255) {
|
|
||||||
xcb_image_text_8(conn, text_len, drawable, gc, x, pos_y, text);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert the text into UCS-2 so we can do basic pointer math */
|
|
||||||
char *input = (is_ucs2 ? text : (char*)convert_utf8_to_ucs2(text, &text_len));
|
|
||||||
|
|
||||||
/* The X11 protocol limits text drawing to 255 chars, so we may need
|
/* The X11 protocol limits text drawing to 255 chars, so we may need
|
||||||
* multiple calls */
|
* multiple calls */
|
||||||
int pos_x = x;
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Calculate the size of this chunk */
|
/* Calculate the size of this chunk */
|
||||||
int chunk_size = (text_len > 255 ? 255 : text_len);
|
int chunk_size = (text_len > 255 ? 255 : text_len);
|
||||||
xcb_char2b_t *chunk = (xcb_char2b_t*)input + offset;
|
const xcb_char2b_t *chunk = text + offset;
|
||||||
|
|
||||||
/* Draw it */
|
/* Draw it */
|
||||||
xcb_image_text_16(conn, chunk_size, drawable, gc, pos_x, pos_y, chunk);
|
xcb_image_text_16(conn, chunk_size, drawable, gc, x, pos_y, chunk);
|
||||||
|
|
||||||
/* Advance the offset and length of the text to draw */
|
/* Advance the offset and length of the text to draw */
|
||||||
offset += chunk_size;
|
offset += chunk_size;
|
||||||
|
@ -154,15 +300,83 @@ void draw_text(char *text, size_t text_len, bool is_ucs2, xcb_drawable_t drawabl
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Advance pos_x based on the predicted text width */
|
/* Advance pos_x based on the predicted text width */
|
||||||
pos_x += predict_text_width((char*)chunk, chunk_size, true);
|
x += predict_text_width_xcb(chunk, chunk_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we had to convert, free the converted string */
|
|
||||||
if (!is_ucs2)
|
|
||||||
free(input);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) {
|
/*
|
||||||
|
* Draws text onto the specified X drawable (normally a pixmap) at the
|
||||||
|
* specified coordinates (from the top left corner of the leftmost, uppermost
|
||||||
|
* glyph) and using the provided gc.
|
||||||
|
*
|
||||||
|
* Text must be specified as an i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void draw_text(i3String *text, xcb_drawable_t drawable,
|
||||||
|
xcb_gcontext_t gc, int x, int y, int max_width) {
|
||||||
|
assert(savedFont != NULL);
|
||||||
|
|
||||||
|
switch (savedFont->type) {
|
||||||
|
case FONT_TYPE_NONE:
|
||||||
|
/* Nothing to do */
|
||||||
|
return;
|
||||||
|
case FONT_TYPE_XCB:
|
||||||
|
draw_text_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text),
|
||||||
|
drawable, gc, x, y, max_width);
|
||||||
|
break;
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
case FONT_TYPE_PANGO:
|
||||||
|
/* Render the text using Pango */
|
||||||
|
draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
|
||||||
|
drawable, x, y, max_width);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ASCII version of draw_text to print static strings.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void draw_text_ascii(const char *text, xcb_drawable_t drawable,
|
||||||
|
xcb_gcontext_t gc, int x, int y, int max_width) {
|
||||||
|
assert(savedFont != NULL);
|
||||||
|
|
||||||
|
switch (savedFont->type) {
|
||||||
|
case FONT_TYPE_NONE:
|
||||||
|
/* Nothing to do */
|
||||||
|
return;
|
||||||
|
case FONT_TYPE_XCB:
|
||||||
|
{
|
||||||
|
size_t text_len = strlen(text);
|
||||||
|
if (text_len > 255) {
|
||||||
|
/* The text is too long to draw it directly to X */
|
||||||
|
i3String *str = i3string_from_utf8(text);
|
||||||
|
draw_text(str, drawable, gc, x, y, max_width);
|
||||||
|
i3string_free(str);
|
||||||
|
} else {
|
||||||
|
/* X11 coordinates for fonts start at the baseline */
|
||||||
|
int pos_y = y + savedFont->specific.xcb.info->font_ascent;
|
||||||
|
|
||||||
|
xcb_image_text_8(conn, text_len, drawable, gc, x, pos_y, text);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
case FONT_TYPE_PANGO:
|
||||||
|
/* Render the text using Pango */
|
||||||
|
draw_text_pango(text, strlen(text),
|
||||||
|
drawable, x, y, max_width);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xcb_query_text_width(const xcb_char2b_t *text, size_t text_len) {
|
||||||
/* Make the user know we’re using the slow path, but only once. */
|
/* Make the user know we’re using the slow path, but only once. */
|
||||||
static bool first_invocation = true;
|
static bool first_invocation = true;
|
||||||
if (first_invocation) {
|
if (first_invocation) {
|
||||||
|
@ -173,7 +387,7 @@ static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) {
|
||||||
/* Query the text width */
|
/* Query the text width */
|
||||||
xcb_generic_error_t *error;
|
xcb_generic_error_t *error;
|
||||||
xcb_query_text_extents_cookie_t cookie = xcb_query_text_extents(conn,
|
xcb_query_text_extents_cookie_t cookie = xcb_query_text_extents(conn,
|
||||||
savedFont->id, text_len, (xcb_char2b_t*)text);
|
savedFont->specific.xcb.id, text_len, (xcb_char2b_t*)text);
|
||||||
xcb_query_text_extents_reply_t *reply = xcb_query_text_extents_reply(conn,
|
xcb_query_text_extents_reply_t *reply = xcb_query_text_extents_reply(conn,
|
||||||
cookie, &error);
|
cookie, &error);
|
||||||
if (reply == NULL) {
|
if (reply == NULL) {
|
||||||
|
@ -181,7 +395,7 @@ static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) {
|
||||||
* a crash. Plus, the user will see the error in his log. */
|
* a crash. Plus, the user will see the error in his log. */
|
||||||
fprintf(stderr, "Could not get text extents (X error code %d)\n",
|
fprintf(stderr, "Could not get text extents (X error code %d)\n",
|
||||||
error->error_code);
|
error->error_code);
|
||||||
return savedFont->info->max_bounds.character_width * text_len;
|
return savedFont->specific.xcb.info->max_bounds.character_width * text_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
int width = reply->overall_width;
|
int width = reply->overall_width;
|
||||||
|
@ -189,27 +403,18 @@ static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int predict_text_width_xcb(const xcb_char2b_t *input, size_t text_len) {
|
||||||
* Predict the text width in pixels for the given text. Text can be specified
|
if (text_len == 0)
|
||||||
* as UCS-2 or UTF-8.
|
return 0;
|
||||||
*
|
|
||||||
*/
|
|
||||||
int predict_text_width(char *text, size_t text_len, bool is_ucs2) {
|
|
||||||
/* Convert the text into UTF-16 so we can do basic pointer math */
|
|
||||||
xcb_char2b_t *input;
|
|
||||||
if (is_ucs2)
|
|
||||||
input = (xcb_char2b_t*)text;
|
|
||||||
else
|
|
||||||
input = convert_utf8_to_ucs2(text, &text_len);
|
|
||||||
|
|
||||||
int width;
|
int width;
|
||||||
if (savedFont->table == NULL) {
|
if (savedFont->specific.xcb.table == NULL) {
|
||||||
/* If we don't have a font table, fall back to querying the server */
|
/* If we don't have a font table, fall back to querying the server */
|
||||||
width = xcb_query_text_width(input, text_len);
|
width = xcb_query_text_width(input, text_len);
|
||||||
} else {
|
} else {
|
||||||
/* Save some pointers for convenience */
|
/* Save some pointers for convenience */
|
||||||
xcb_query_font_reply_t *font_info = savedFont->info;
|
xcb_query_font_reply_t *font_info = savedFont->specific.xcb.info;
|
||||||
xcb_charinfo_t *font_table = savedFont->table;
|
xcb_charinfo_t *font_table = savedFont->specific.xcb.table;
|
||||||
|
|
||||||
/* Calculate the width using the font table */
|
/* Calculate the width using the font table */
|
||||||
width = 0;
|
width = 0;
|
||||||
|
@ -239,9 +444,30 @@ int predict_text_width(char *text, size_t text_len, bool is_ucs2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we had to convert, free the converted string */
|
|
||||||
if (!is_ucs2)
|
|
||||||
free(input);
|
|
||||||
|
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Predict the text width in pixels for the given text. Text must be
|
||||||
|
* specified as an i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int predict_text_width(i3String *text) {
|
||||||
|
assert(savedFont != NULL);
|
||||||
|
|
||||||
|
switch (savedFont->type) {
|
||||||
|
case FONT_TYPE_NONE:
|
||||||
|
/* Nothing to do */
|
||||||
|
return 0;
|
||||||
|
case FONT_TYPE_XCB:
|
||||||
|
return predict_text_width_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text));
|
||||||
|
#if PANGO_SUPPORT
|
||||||
|
case FONT_TYPE_PANGO:
|
||||||
|
/* Calculate extents using Pango */
|
||||||
|
return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text));
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "libi3.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the visual type associated with the given screen.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
xcb_visualtype_t *get_visualtype(xcb_screen_t *screen) {
|
||||||
|
xcb_depth_iterator_t depth_iter;
|
||||||
|
for (depth_iter = xcb_screen_allowed_depths_iterator(screen);
|
||||||
|
depth_iter.rem;
|
||||||
|
xcb_depth_next(&depth_iter)) {
|
||||||
|
xcb_visualtype_iterator_t visual_iter;
|
||||||
|
for (visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
|
||||||
|
visual_iter.rem;
|
||||||
|
xcb_visualtype_next(&visual_iter)) {
|
||||||
|
if (screen->root_visual == visual_iter.data->visual_id)
|
||||||
|
return visual_iter.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
CLEAN_TARGETS += clean-libi3
|
||||||
|
|
||||||
|
libi3_SOURCES := $(wildcard libi3/*.c)
|
||||||
|
libi3_HEADERS := $(wildcard libi3/*.h)
|
||||||
|
libi3_CFLAGS = $(PANGO_CFLAGS)
|
||||||
|
libi3_LIBS =
|
||||||
|
|
||||||
|
libi3_OBJECTS := $(libi3_SOURCES:.c=.o)
|
||||||
|
|
||||||
|
|
||||||
|
libi3/%.o: libi3/%.c $(libi3_HEADERS)
|
||||||
|
echo "[libi3] CC $<"
|
||||||
|
$(CC) $(I3_CPPFLAGS) $(XCB_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/libi3.a
|
|
@ -28,7 +28,7 @@ char *root_atom_contents(const char *atomname) {
|
||||||
xcb_intern_atom_cookie_t atom_cookie;
|
xcb_intern_atom_cookie_t atom_cookie;
|
||||||
xcb_intern_atom_reply_t *atom_reply;
|
xcb_intern_atom_reply_t *atom_reply;
|
||||||
int screen;
|
int screen;
|
||||||
char *socket_path;
|
char *content;
|
||||||
|
|
||||||
if ((conn = xcb_connect(NULL, &screen)) == NULL ||
|
if ((conn = xcb_connect(NULL, &screen)) == NULL ||
|
||||||
xcb_connection_has_error(conn))
|
xcb_connection_has_error(conn))
|
||||||
|
@ -50,10 +50,17 @@ char *root_atom_contents(const char *atomname) {
|
||||||
prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
|
prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
|
||||||
if (prop_reply == NULL || xcb_get_property_value_length(prop_reply) == 0)
|
if (prop_reply == NULL || xcb_get_property_value_length(prop_reply) == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (asprintf(&socket_path, "%.*s", xcb_get_property_value_length(prop_reply),
|
if (prop_reply->type == XCB_ATOM_CARDINAL) {
|
||||||
(char*)xcb_get_property_value(prop_reply)) == -1)
|
/* We treat a CARDINAL as a >= 32-bit unsigned int. The only CARDINAL
|
||||||
return NULL;
|
* we query is I3_PID, which is 32-bit. */
|
||||||
|
if (asprintf(&content, "%u", *((unsigned int*)xcb_get_property_value(prop_reply))) == -1)
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
if (asprintf(&content, "%.*s", xcb_get_property_value_length(prop_reply),
|
||||||
|
(char*)xcb_get_property_value(prop_reply)) == -1)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
xcb_disconnect(conn);
|
xcb_disconnect(conn);
|
||||||
return socket_path;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* string.c: Define an i3String type to automagically handle UTF-8/UCS-2
|
||||||
|
* conversions. Some font backends need UCS-2 (X core fonts),
|
||||||
|
* others want UTF-8 (Pango).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "libi3.h"
|
||||||
|
|
||||||
|
struct _i3String {
|
||||||
|
char *utf8;
|
||||||
|
xcb_char2b_t *ucs2;
|
||||||
|
size_t num_glyphs;
|
||||||
|
size_t num_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build an i3String from an UTF-8 encoded string.
|
||||||
|
* Returns the newly-allocated i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
i3String *i3string_from_utf8(const char *from_utf8) {
|
||||||
|
i3String *str = scalloc(sizeof(i3String));
|
||||||
|
|
||||||
|
/* Get the text */
|
||||||
|
str->utf8 = sstrdup(from_utf8);
|
||||||
|
|
||||||
|
/* Compute and store the length */
|
||||||
|
str->num_bytes = strlen(str->utf8);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build an i3String from an UTF-8 encoded string with fixed length.
|
||||||
|
* To be used when no proper NUL-terminaison is available.
|
||||||
|
* Returns the newly-allocated i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes) {
|
||||||
|
i3String *str = scalloc(sizeof(i3String));
|
||||||
|
|
||||||
|
/* Copy the actual text to our i3String */
|
||||||
|
str->utf8 = scalloc(sizeof(char) * (num_bytes + 1));
|
||||||
|
strncpy(str->utf8, from_utf8, num_bytes);
|
||||||
|
str->utf8[num_bytes] = '\0';
|
||||||
|
|
||||||
|
/* Store the length */
|
||||||
|
str->num_bytes = num_bytes;
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build an i3String from an UCS-2 encoded string.
|
||||||
|
* Returns the newly-allocated i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs) {
|
||||||
|
i3String *str = scalloc(sizeof(i3String));
|
||||||
|
|
||||||
|
/* Copy the actual text to our i3String */
|
||||||
|
size_t num_bytes = num_glyphs * sizeof(xcb_char2b_t);
|
||||||
|
str->ucs2 = scalloc(num_bytes);
|
||||||
|
memcpy(str->ucs2, from_ucs2, num_bytes);
|
||||||
|
|
||||||
|
/* Store the length */
|
||||||
|
str->num_glyphs = num_glyphs;
|
||||||
|
|
||||||
|
str->utf8 = NULL;
|
||||||
|
str->num_bytes = 0;
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free an i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void i3string_free(i3String *str) {
|
||||||
|
if (str == NULL)
|
||||||
|
return;
|
||||||
|
free(str->utf8);
|
||||||
|
free(str->ucs2);
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i3string_ensure_utf8(i3String *str) {
|
||||||
|
if (str->utf8 != NULL)
|
||||||
|
return;
|
||||||
|
if ((str->utf8 = convert_ucs2_to_utf8(str->ucs2, str->num_glyphs)) != NULL)
|
||||||
|
str->num_bytes = strlen(str->utf8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i3string_ensure_ucs2(i3String *str) {
|
||||||
|
if (str->ucs2 != NULL)
|
||||||
|
return;
|
||||||
|
str->ucs2 = convert_utf8_to_ucs2(str->utf8, &str->num_glyphs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the UTF-8 encoded version of the i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const char *i3string_as_utf8(i3String *str) {
|
||||||
|
i3string_ensure_utf8(str);
|
||||||
|
return str->utf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the UCS-2 encoded version of the i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const xcb_char2b_t *i3string_as_ucs2(i3String *str) {
|
||||||
|
i3string_ensure_ucs2(str);
|
||||||
|
return str->ucs2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of bytes (UTF-8 encoded) in an i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
size_t i3string_get_num_bytes(i3String *str) {
|
||||||
|
i3string_ensure_utf8(str);
|
||||||
|
return str->num_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of glyphs in an i3String.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
size_t i3string_get_num_glyphs(i3String *str) {
|
||||||
|
i3string_ensure_ucs2(str);
|
||||||
|
return str->num_glyphs;
|
||||||
|
}
|
16
man/Makefile
16
man/Makefile
|
@ -1,15 +1,7 @@
|
||||||
A2M:=a2x -f manpage --asciidoc-opts="-f asciidoc.conf"
|
all:
|
||||||
|
$(MAKE) -C .. mans
|
||||||
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} $<
|
|
||||||
|
|
||||||
clean:
|
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); \
|
$(MAKE) -C .. clean-mans
|
||||||
do \
|
|
||||||
rm -f $${file}.1 $${file}.html $${file}.xml; \
|
|
||||||
done
|
|
||||||
|
|
||||||
distclean: clean
|
.PHONY: all clean
|
||||||
rm -f *.1
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ template::[header-declarations]
|
||||||
<refentrytitle>{mantitle}</refentrytitle>
|
<refentrytitle>{mantitle}</refentrytitle>
|
||||||
<manvolnum>{manvolnum}</manvolnum>
|
<manvolnum>{manvolnum}</manvolnum>
|
||||||
<refmiscinfo class="source">i3</refmiscinfo>
|
<refmiscinfo class="source">i3</refmiscinfo>
|
||||||
<refmiscinfo class="version">4.1.2</refmiscinfo>
|
<refmiscinfo class="version">4.3</refmiscinfo>
|
||||||
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
||||||
</refmeta>
|
</refmeta>
|
||||||
<refnamediv>
|
<refnamediv>
|
||||||
|
|
|
@ -14,7 +14,7 @@ i3-dump-log [-s <socketpath>]
|
||||||
== DESCRIPTION
|
== DESCRIPTION
|
||||||
|
|
||||||
Debug versions of i3 automatically use 1% of your RAM (but 25 MiB max) to store
|
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.
|
figuring out what is going on, without permanently logging to a file.
|
||||||
|
|
||||||
With i3-dump-log, you can dump the SHM log to stdout.
|
With i3-dump-log, you can dump the SHM log to stdout.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
i3-msg(1)
|
i3-msg(1)
|
||||||
=========
|
=========
|
||||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
v4.2, January 2012
|
v4.2, August 2012
|
||||||
|
|
||||||
== NAME
|
== NAME
|
||||||
|
|
||||||
|
@ -38,6 +38,9 @@ get_bar_config::
|
||||||
Gets the configuration (as JSON map) of the workspace bar with the given ID. If
|
Gets the configuration (as JSON map) of the workspace bar with the given ID. If
|
||||||
no ID is provided, an array with all configured bar IDs is returned instead.
|
no ID is provided, an array with all configured bar IDs is returned instead.
|
||||||
|
|
||||||
|
get_version::
|
||||||
|
Gets the version of i3. The reply will be a JSON-encoded dictionary with the
|
||||||
|
major, minor, patch and human-readable version.
|
||||||
|
|
||||||
== DESCRIPTION
|
== DESCRIPTION
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
i3-sensible-terminal(1)
|
i3-sensible-terminal(1)
|
||||||
=======================
|
=======================
|
||||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
v4.1, November 2011
|
v4.2, August 2012
|
||||||
|
|
||||||
== NAME
|
== NAME
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ It tries to start one of the following (in that order):
|
||||||
* xterm
|
* xterm
|
||||||
* gnome-terminal
|
* gnome-terminal
|
||||||
* roxterm
|
* roxterm
|
||||||
|
* xfce4-terminal
|
||||||
|
|
||||||
Please don’t complain about the order: If the user has any preference, he will
|
Please don’t complain about the order: If the user has any preference, he will
|
||||||
have $TERMINAL set or modified his i3 configuration file.
|
have $TERMINAL set or modified his i3 configuration file.
|
||||||
|
|
55
man/i3.man
55
man/i3.man
|
@ -1,7 +1,7 @@
|
||||||
i3(1)
|
i3(1)
|
||||||
=====
|
=====
|
||||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
v4.0, July 2011
|
v4.3, September 2012
|
||||||
|
|
||||||
== NAME
|
== NAME
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ i3 - an improved dynamic, tiling window manager
|
||||||
|
|
||||||
== SYNOPSIS
|
== SYNOPSIS
|
||||||
|
|
||||||
i3 [-a] [-c configfile] [-C] [-d <loglevel>] [-v] [-V]
|
i3 [-a] [-c configfile] [-C] [-d all] [-v] [-V]
|
||||||
|
|
||||||
== OPTIONS
|
== OPTIONS
|
||||||
|
|
||||||
|
@ -22,8 +22,9 @@ Specifies an alternate configuration file path.
|
||||||
-C::
|
-C::
|
||||||
Check the configuration file for validity and exit.
|
Check the configuration file for validity and exit.
|
||||||
|
|
||||||
-d::
|
-d all::
|
||||||
Specifies the debug loglevel. To see the most output, use -d all.
|
Enables debug logging.
|
||||||
|
The 'all' parameter is present for historical reasons.
|
||||||
|
|
||||||
-v::
|
-v::
|
||||||
Display version number (and date of the last commit).
|
Display version number (and date of the last commit).
|
||||||
|
@ -31,6 +32,18 @@ Display version number (and date of the last commit).
|
||||||
-V::
|
-V::
|
||||||
Be verbose.
|
Be verbose.
|
||||||
|
|
||||||
|
--force-xinerama::
|
||||||
|
Use Xinerama instead of RandR. This option should only be used if you are stuck
|
||||||
|
with the old nVidia closed source driver (older than 302.17) which does not
|
||||||
|
support RandR.
|
||||||
|
|
||||||
|
--get-socketpath::
|
||||||
|
Retrieve the i3 IPC socket path from X11, print it, then exit.
|
||||||
|
|
||||||
|
--shmlog-size <limit>::
|
||||||
|
Limits the size of the i3 SHM log to <limit> bytes. Setting this to 0 disables
|
||||||
|
SHM logging entirely. The default is 0 bytes.
|
||||||
|
|
||||||
== DESCRIPTION
|
== DESCRIPTION
|
||||||
|
|
||||||
=== INTRODUCTION
|
=== INTRODUCTION
|
||||||
|
@ -47,8 +60,8 @@ Please be aware that i3 is primarily targeted at advanced users and developers.
|
||||||
=== IMPORTANT NOTE TO nVidia BINARY DRIVER USERS
|
=== IMPORTANT NOTE TO nVidia BINARY DRIVER USERS
|
||||||
|
|
||||||
If you are using the nVidia binary graphics driver (also known as 'blob')
|
If you are using the nVidia binary graphics driver (also known as 'blob')
|
||||||
you need to use the +--force-xinerama+ flag (in your ~/.xsession) when starting
|
before version 302.17, you need to use the +--force-xinerama+ flag (in your
|
||||||
i3, like so:
|
~/.xsession) when starting i3, like so:
|
||||||
|
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
exec i3 --force-xinerama -V >>~/.i3/i3log 2>&1
|
exec i3 --force-xinerama -V >>~/.i3/i3log 2>&1
|
||||||
|
@ -91,6 +104,12 @@ are connected to these outputs.
|
||||||
|
|
||||||
Here is a short overview of the default keybindings:
|
Here is a short overview of the default keybindings:
|
||||||
|
|
||||||
|
Mod1+Enter::
|
||||||
|
Open a new terminal emulator window.
|
||||||
|
|
||||||
|
Mod1+d::
|
||||||
|
Open dmenu for starting any application by typing (part of) its name.
|
||||||
|
|
||||||
j/k/l/;::
|
j/k/l/;::
|
||||||
Direction keys (left, down, up, right). They are on your homerow (see the mark
|
Direction keys (left, down, up, right). They are on your homerow (see the mark
|
||||||
on your "j" key). Alternatively, you can use the cursor keys.
|
on your "j" key). Alternatively, you can use the cursor keys.
|
||||||
|
@ -261,19 +280,15 @@ xset -b
|
||||||
# Enable zapping (C-A-<Bksp> kills X)
|
# Enable zapping (C-A-<Bksp> kills X)
|
||||||
setxkbmap -option terminate:ctrl_alt_bksp
|
setxkbmap -option terminate:ctrl_alt_bksp
|
||||||
|
|
||||||
# Enforce correct locales from the beginning
|
# Enforce correct locales from the beginning:
|
||||||
unset LC_COLLATE
|
# LC_ALL is unset since it overwrites everything
|
||||||
export LC_CTYPE=de_DE.UTF-8
|
# LANG=de_DE.UTF-8 is used, except for:
|
||||||
export LC_TIME=de_DE.UTF-8
|
# LC_MESSAGES=C never translates program output
|
||||||
export LC_NUMERIC=de_DE.UTF-8
|
# LC_TIME=en_DK leads to yyyy-mm-dd hh:mm date/time output
|
||||||
export LC_MONETARY=de_DE.UTF-8
|
unset LC_ALL
|
||||||
|
export LANG=de_DE.UTF-8
|
||||||
export LC_MESSAGES=C
|
export LC_MESSAGES=C
|
||||||
export LC_PAPER=de_DE.UTF-8
|
export LC_TIME=en_DK.UTF-8
|
||||||
export LC_NAME=de_DE.UTF-8
|
|
||||||
export LC_ADDRESS=de_DE.UTF-8
|
|
||||||
export LC_TELEPHONE=de_DE.UTF-8
|
|
||||||
export LC_MEASUREMENT=de_DE.UTF-8
|
|
||||||
export LC_IDENTIFICATION=de_DE.UTF-8
|
|
||||||
|
|
||||||
# Use XToolkit in java applications
|
# Use XToolkit in java applications
|
||||||
export AWT_TOOLKIT=XToolkit
|
export AWT_TOOLKIT=XToolkit
|
||||||
|
@ -313,7 +328,7 @@ and the "how to hack" guide. If you are building from source, run:
|
||||||
|
|
||||||
You can also access these documents online at http://i3wm.org/
|
You can also access these documents online at http://i3wm.org/
|
||||||
|
|
||||||
i3-input(1), i3-msg(1), i3-wsbar(1), i3-nagbar(1), i3-config-wizard(1),
|
i3-input(1), i3-msg(1), i3bar(1), i3-nagbar(1), i3-config-wizard(1),
|
||||||
i3-migrate-config-to-v4(1)
|
i3-migrate-config-to-v4(1)
|
||||||
|
|
||||||
== AUTHOR
|
== AUTHOR
|
||||||
|
|
|
@ -39,9 +39,8 @@ Display a short help-message and exit
|
||||||
workspace switching buttons and a statusline generated by i3status(1) or
|
workspace switching buttons and a statusline generated by i3status(1) or
|
||||||
similar. It is automatically invoked (and configured through) i3.
|
similar. It is automatically invoked (and configured through) i3.
|
||||||
|
|
||||||
i3bar does not support any color or other markups, so stdin should be plain
|
i3bar supports colors via a JSON protocol starting from v4.2, see
|
||||||
utf8, one line at a time. If you use *i3status*(1), you therefore should
|
http://i3wm.org/docs/i3bar-protocol.html
|
||||||
specify 'output_format = none' in the general section of its config file.
|
|
||||||
|
|
||||||
== ENVIRONMENT
|
== ENVIRONMENT
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ Instead, see the i3 documentation, especially the User’s Guide.
|
||||||
|
|
||||||
== SEE ALSO
|
== SEE ALSO
|
||||||
|
|
||||||
+i3status(1)+ or +conky(1)+ for programs generating a statusline.
|
+i3status(1)+, +j4status(1)+ or +conky(1)+ for programs generating a statusline.
|
||||||
|
|
||||||
+dzen2(1)+ or +xmobar(1)+ for similar programs to i3bar.
|
+dzen2(1)+ or +xmobar(1)+ for similar programs to i3bar.
|
||||||
|
|
|
@ -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
|
|
@ -66,10 +66,20 @@ state BORDER:
|
||||||
border_style = 'normal', 'none', '1pixel', 'toggle'
|
border_style = 'normal', 'none', '1pixel', 'toggle'
|
||||||
-> call cmd_border($border_style)
|
-> call cmd_border($border_style)
|
||||||
|
|
||||||
# layout default|stacked|stacking|tabbed
|
# layout default|stacked|stacking|tabbed|splitv|splith
|
||||||
|
# layout toggle [split|all]
|
||||||
state LAYOUT:
|
state LAYOUT:
|
||||||
layout_mode = 'default', 'stacked', 'stacking', 'tabbed'
|
layout_mode = 'default', 'stacked', 'stacking', 'tabbed', 'splitv', 'splith'
|
||||||
-> call cmd_layout($layout_mode)
|
-> 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>
|
# append_layout <path>
|
||||||
state APPEND_LAYOUT:
|
state APPEND_LAYOUT:
|
||||||
|
@ -190,7 +200,7 @@ state RENAME_WORKSPACE_TO:
|
||||||
-> call cmd_rename_workspace($old_name, $new_name)
|
-> call cmd_rename_workspace($old_name, $new_name)
|
||||||
|
|
||||||
# move <direction> [<pixels> [px]]
|
# 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] output <str>
|
||||||
# move [window|container] [to] scratchpad
|
# move [window|container] [to] scratchpad
|
||||||
# move workspace to [output] <str>
|
# move workspace to [output] <str>
|
||||||
|
@ -231,7 +241,7 @@ state MOVE_DIRECTION_PX:
|
||||||
state MOVE_WORKSPACE:
|
state MOVE_WORKSPACE:
|
||||||
'to'
|
'to'
|
||||||
-> MOVE_WORKSPACE_TO_OUTPUT
|
-> 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)
|
-> call cmd_move_con_to_workspace($workspace)
|
||||||
'number'
|
'number'
|
||||||
-> MOVE_WORKSPACE_NUMBER
|
-> MOVE_WORKSPACE_NUMBER
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
all:
|
||||||
|
$(MAKE) -C .. i3
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(MAKE) -C .. install-i3
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(MAKE) -C .. clean-i3
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
|
@ -1,3 +1,5 @@
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "assignments.c"
|
||||||
/*
|
/*
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
|
|
|
@ -66,6 +66,7 @@ EOL (\r?\n)
|
||||||
%x BAR_COLOR
|
%x BAR_COLOR
|
||||||
|
|
||||||
%x EXEC
|
%x EXEC
|
||||||
|
%x OPTRELEASE
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
@ -159,7 +160,7 @@ EOL (\r?\n)
|
||||||
return STR;
|
return STR;
|
||||||
}
|
}
|
||||||
<WANT_STRING>[^\n]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return STR; }
|
<WANT_STRING>[^\n]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return STR; }
|
||||||
<OUTPUT_COND>[a-zA-Z0-9_-]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return OUTPUT; }
|
<OUTPUT_COND>[a-zA-Z0-9\/_-]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return OUTPUT; }
|
||||||
^[ \t]*#[^\n]* { return TOKCOMMENT; }
|
^[ \t]*#[^\n]* { return TOKCOMMENT; }
|
||||||
<COLOR_COND>#[0-9a-fA-F]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return HEXCOLOR; }
|
<COLOR_COND>#[0-9a-fA-F]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return HEXCOLOR; }
|
||||||
<COLOR_COND>{EOL} {
|
<COLOR_COND>{EOL} {
|
||||||
|
@ -172,12 +173,14 @@ EOL (\r?\n)
|
||||||
<ASSIGN_TARGET_COND>[ \t]+ { BEGIN(WANT_STRING); }
|
<ASSIGN_TARGET_COND>[ \t]+ { BEGIN(WANT_STRING); }
|
||||||
<EXEC>--no-startup-id { printf("no startup id\n"); yy_pop_state(); return TOK_NO_STARTUP_ID; }
|
<EXEC>--no-startup-id { printf("no startup id\n"); yy_pop_state(); return TOK_NO_STARTUP_ID; }
|
||||||
<EXEC>. { printf("anything else: *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); }
|
<EXEC>. { printf("anything else: *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); }
|
||||||
|
<OPTRELEASE>--release { printf("--release\n"); yy_pop_state(); return TOK_RELEASE; }
|
||||||
|
<OPTRELEASE>. { printf("anything else (optrelease): *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); }
|
||||||
[0-9-]+ { yylval.number = atoi(yytext); return NUMBER; }
|
[0-9-]+ { yylval.number = atoi(yytext); return NUMBER; }
|
||||||
bar { yy_push_state(BAR); return TOK_BAR; }
|
bar { yy_push_state(BAR); return TOK_BAR; }
|
||||||
mode { return TOKMODE; }
|
mode { return TOKMODE; }
|
||||||
bind { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
|
bind { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
|
||||||
bindcode { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
|
bindcode { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); yy_push_state(OPTRELEASE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
|
||||||
bindsym { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; }
|
bindsym { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); yy_push_state(OPTRELEASE); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; }
|
||||||
floating_maximum_size { return TOKFLOATING_MAXIMUM_SIZE; }
|
floating_maximum_size { return TOKFLOATING_MAXIMUM_SIZE; }
|
||||||
floating_minimum_size { return TOKFLOATING_MINIMUM_SIZE; }
|
floating_minimum_size { return TOKFLOATING_MINIMUM_SIZE; }
|
||||||
floating_modifier { return TOKFLOATING_MODIFIER; }
|
floating_modifier { return TOKFLOATING_MODIFIER; }
|
||||||
|
@ -200,6 +203,8 @@ new_float { return TOKNEWFLOAT; }
|
||||||
normal { return TOK_NORMAL; }
|
normal { return TOK_NORMAL; }
|
||||||
none { return TOK_NONE; }
|
none { return TOK_NONE; }
|
||||||
1pixel { return TOK_1PIXEL; }
|
1pixel { return TOK_1PIXEL; }
|
||||||
|
hide_edge_borders { return TOK_HIDE_EDGE_BORDERS; }
|
||||||
|
both { return TOK_BOTH; }
|
||||||
focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; }
|
focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; }
|
||||||
force_focus_wrapping { return TOK_FORCE_FOCUS_WRAPPING; }
|
force_focus_wrapping { return TOK_FORCE_FOCUS_WRAPPING; }
|
||||||
force_xinerama { return TOK_FORCE_XINERAMA; }
|
force_xinerama { return TOK_FORCE_XINERAMA; }
|
||||||
|
|
105
src/cfgparse.y
105
src/cfgparse.y
|
@ -3,6 +3,8 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "cfgparse.y"
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
@ -19,6 +21,9 @@ static Barconfig current_bar;
|
||||||
* store this in a separate variable because in the i3 config struct we just
|
* store this in a separate variable because in the i3 config struct we just
|
||||||
* store the i3Font. */
|
* store the i3Font. */
|
||||||
static char *font_pattern;
|
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;
|
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||||
extern int yylex(struct context *context);
|
extern int yylex(struct context *context);
|
||||||
|
@ -233,6 +238,12 @@ static char *migrate_config(char *input, off_t size) {
|
||||||
*/
|
*/
|
||||||
static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
|
static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
|
||||||
ev_child_stop(EV_A_ watcher);
|
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)) {
|
if (!WIFEXITED(watcher->rstatus)) {
|
||||||
fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
|
fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
|
||||||
return;
|
return;
|
||||||
|
@ -264,6 +275,23 @@ static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
* 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
|
* file contains one or more errors. Also offers two buttons: One to launch an
|
||||||
|
@ -276,6 +304,18 @@ static void start_configerror_nagbar(const char *config_path) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fprintf(stderr, "Starting i3-nagbar due to configuration errors\n");
|
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();
|
configerror_pid = fork();
|
||||||
if (configerror_pid == -1) {
|
if (configerror_pid == -1) {
|
||||||
warn("Could not fork()");
|
warn("Could not fork()");
|
||||||
|
@ -284,10 +324,17 @@ static void start_configerror_nagbar(const char *config_path) {
|
||||||
|
|
||||||
/* child */
|
/* child */
|
||||||
if (configerror_pid == 0) {
|
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,
|
char *editaction,
|
||||||
*pageraction;
|
*pageraction;
|
||||||
sasprintf(&editaction, "i3-sensible-terminal -e sh -c \"i3-sensible-editor \\\"%s\\\" && i3-msg reload\"", config_path);
|
sasprintf(&editaction, "i3-sensible-terminal -e \"%s\"", edit_script_path);
|
||||||
sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename);
|
sasprintf(&pageraction, "i3-sensible-terminal -e \"%s\"", pager_script_path);
|
||||||
char *argv[] = {
|
char *argv[] = {
|
||||||
NULL, /* will be replaced by the executable path */
|
NULL, /* will be replaced by the executable path */
|
||||||
"-t",
|
"-t",
|
||||||
|
@ -384,8 +431,10 @@ static void check_for_duplicate_bindings(struct context *context) {
|
||||||
/* Check if the keycodes or modifiers are different. If so, they
|
/* Check if the keycodes or modifiers are different. If so, they
|
||||||
* can't be duplicate */
|
* can't be duplicate */
|
||||||
if (bind->keycode != current->keycode ||
|
if (bind->keycode != current->keycode ||
|
||||||
bind->mods != current->mods)
|
bind->mods != current->mods ||
|
||||||
|
bind->release != current->release)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
context->has_errors = true;
|
context->has_errors = true;
|
||||||
if (current->keycode != 0) {
|
if (current->keycode != 0) {
|
||||||
ELOG("Duplicate keybinding in config file:\n modmask %d with keycode %d, command \"%s\"\n",
|
ELOG("Duplicate keybinding in config file:\n modmask %d with keycode %d, command \"%s\"\n",
|
||||||
|
@ -690,6 +739,8 @@ void parse_file(const char *f) {
|
||||||
%token TOK_NORMAL "normal"
|
%token TOK_NORMAL "normal"
|
||||||
%token TOK_NONE "none"
|
%token TOK_NONE "none"
|
||||||
%token TOK_1PIXEL "1pixel"
|
%token TOK_1PIXEL "1pixel"
|
||||||
|
%token TOK_HIDE_EDGE_BORDERS "hide_edge_borders"
|
||||||
|
%token TOK_BOTH "both"
|
||||||
%token TOKFOCUSFOLLOWSMOUSE "focus_follows_mouse"
|
%token TOKFOCUSFOLLOWSMOUSE "focus_follows_mouse"
|
||||||
%token TOK_FORCE_FOCUS_WRAPPING "force_focus_wrapping"
|
%token TOK_FORCE_FOCUS_WRAPPING "force_focus_wrapping"
|
||||||
%token TOK_FORCE_XINERAMA "force_xinerama"
|
%token TOK_FORCE_XINERAMA "force_xinerama"
|
||||||
|
@ -735,6 +786,7 @@ void parse_file(const char *f) {
|
||||||
%token TOK_BAR_COLOR_INACTIVE_WORKSPACE "inactive_workspace"
|
%token TOK_BAR_COLOR_INACTIVE_WORKSPACE "inactive_workspace"
|
||||||
%token TOK_BAR_COLOR_URGENT_WORKSPACE "urgent_workspace"
|
%token TOK_BAR_COLOR_URGENT_WORKSPACE "urgent_workspace"
|
||||||
%token TOK_NO_STARTUP_ID "--no-startup-id"
|
%token TOK_NO_STARTUP_ID "--no-startup-id"
|
||||||
|
%token TOK_RELEASE "--release"
|
||||||
|
|
||||||
%token TOK_MARK "mark"
|
%token TOK_MARK "mark"
|
||||||
%token TOK_CLASS "class"
|
%token TOK_CLASS "class"
|
||||||
|
@ -754,6 +806,8 @@ void parse_file(const char *f) {
|
||||||
%type <number> layout_mode
|
%type <number> layout_mode
|
||||||
%type <number> border_style
|
%type <number> border_style
|
||||||
%type <number> new_window
|
%type <number> new_window
|
||||||
|
%type <number> hide_edge_borders
|
||||||
|
%type <number> edge_hiding_mode
|
||||||
%type <number> new_float
|
%type <number> new_float
|
||||||
%type <number> colorpixel
|
%type <number> colorpixel
|
||||||
%type <number> bool
|
%type <number> bool
|
||||||
|
@ -762,6 +816,7 @@ void parse_file(const char *f) {
|
||||||
%type <number> bar_mode_mode
|
%type <number> bar_mode_mode
|
||||||
%type <number> bar_modifier_modifier
|
%type <number> bar_modifier_modifier
|
||||||
%type <number> optional_no_startup_id
|
%type <number> optional_no_startup_id
|
||||||
|
%type <number> optional_release
|
||||||
%type <string> command
|
%type <string> command
|
||||||
%type <string> word_or_number
|
%type <string> word_or_number
|
||||||
%type <string> qstring_or_number
|
%type <string> qstring_or_number
|
||||||
|
@ -788,6 +843,7 @@ line:
|
||||||
| workspace_layout
|
| workspace_layout
|
||||||
| new_window
|
| new_window
|
||||||
| new_float
|
| new_float
|
||||||
|
| hide_edge_borders
|
||||||
| focus_follows_mouse
|
| focus_follows_mouse
|
||||||
| force_focus_wrapping
|
| force_focus_wrapping
|
||||||
| force_xinerama
|
| force_xinerama
|
||||||
|
@ -829,33 +885,40 @@ binding:
|
||||||
;
|
;
|
||||||
|
|
||||||
bindcode:
|
bindcode:
|
||||||
binding_modifiers NUMBER command
|
optional_release binding_modifiers NUMBER command
|
||||||
{
|
{
|
||||||
printf("\tFound keycode binding mod%d with key %d and command %s\n", $1, $2, $3);
|
DLOG("bindcode: release = %d, mod = %d, key = %d, command = %s\n", $1, $2, $3, $4);
|
||||||
Binding *new = scalloc(sizeof(Binding));
|
Binding *new = scalloc(sizeof(Binding));
|
||||||
|
|
||||||
new->keycode = $2;
|
new->release = $1;
|
||||||
new->mods = $1;
|
new->keycode = $3;
|
||||||
new->command = $3;
|
new->mods = $2;
|
||||||
|
new->command = $4;
|
||||||
|
|
||||||
$$ = new;
|
$$ = new;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
bindsym:
|
bindsym:
|
||||||
binding_modifiers word_or_number command
|
optional_release binding_modifiers word_or_number command
|
||||||
{
|
{
|
||||||
printf("\tFound keysym binding mod%d with key %s and command %s\n", $1, $2, $3);
|
DLOG("bindsym: release = %d, mod = %d, key = %s, command = %s\n", $1, $2, $3, $4);
|
||||||
Binding *new = scalloc(sizeof(Binding));
|
Binding *new = scalloc(sizeof(Binding));
|
||||||
|
|
||||||
new->symbol = $2;
|
new->release = $1;
|
||||||
new->mods = $1;
|
new->symbol = $3;
|
||||||
new->command = $3;
|
new->mods = $2;
|
||||||
|
new->command = $4;
|
||||||
|
|
||||||
$$ = new;
|
$$ = new;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
optional_release:
|
||||||
|
/* empty */ { $$ = B_UPON_KEYPRESS; }
|
||||||
|
| TOK_RELEASE { $$ = B_UPON_KEYRELEASE; }
|
||||||
|
;
|
||||||
|
|
||||||
for_window:
|
for_window:
|
||||||
TOK_FOR_WINDOW match command
|
TOK_FOR_WINDOW match command
|
||||||
{
|
{
|
||||||
|
@ -1429,6 +1492,22 @@ bool:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
hide_edge_borders:
|
||||||
|
TOK_HIDE_EDGE_BORDERS edge_hiding_mode
|
||||||
|
{
|
||||||
|
DLOG("hide edge borders = %d\n", $2);
|
||||||
|
config.hide_edge_borders = $2;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
edge_hiding_mode:
|
||||||
|
TOK_NONE { $$ = ADJ_NONE; }
|
||||||
|
| TOK_VERT { $$ = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; }
|
||||||
|
| TOK_HORIZ { $$ = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; }
|
||||||
|
| TOK_BOTH { $$ = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE | ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; }
|
||||||
|
| bool { $$ = ($1 ? ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE : ADJ_NONE); }
|
||||||
|
;
|
||||||
|
|
||||||
focus_follows_mouse:
|
focus_follows_mouse:
|
||||||
TOKFOCUSFOLLOWSMOUSE bool
|
TOKFOCUSFOLLOWSMOUSE bool
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "click.c"
|
||||||
/*
|
/*
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
|
@ -35,13 +37,13 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press
|
||||||
Con *resize_con = con;
|
Con *resize_con = con;
|
||||||
while (resize_con->type != CT_WORKSPACE &&
|
while (resize_con->type != CT_WORKSPACE &&
|
||||||
resize_con->type != CT_FLOATING_CON &&
|
resize_con->type != CT_FLOATING_CON &&
|
||||||
resize_con->parent->orientation != orientation)
|
con_orientation(resize_con->parent) != orientation)
|
||||||
resize_con = resize_con->parent;
|
resize_con = resize_con->parent;
|
||||||
|
|
||||||
DLOG("resize_con = %p\n", resize_con);
|
DLOG("resize_con = %p\n", resize_con);
|
||||||
if (resize_con->type != CT_WORKSPACE &&
|
if (resize_con->type != CT_WORKSPACE &&
|
||||||
resize_con->type != CT_FLOATING_CON &&
|
resize_con->type != CT_FLOATING_CON &&
|
||||||
resize_con->parent->orientation == orientation) {
|
con_orientation(resize_con->parent) == orientation) {
|
||||||
first = resize_con;
|
first = resize_con;
|
||||||
second = (way == 'n') ? TAILQ_NEXT(first, nodes) : TAILQ_PREV(first, nodes_head, nodes);
|
second = (way == 'n') ? TAILQ_NEXT(first, nodes) : TAILQ_PREV(first, nodes_head, nodes);
|
||||||
if (second == TAILQ_END(&(first->nodes_head))) {
|
if (second == TAILQ_END(&(first->nodes_head))) {
|
||||||
|
@ -145,7 +147,7 @@ static bool tiling_resize(Con *con, xcb_button_press_event_t *event, const click
|
||||||
|
|
||||||
if ((check_con->layout == L_STACKED ||
|
if ((check_con->layout == L_STACKED ||
|
||||||
check_con->layout == L_TABBED ||
|
check_con->layout == L_TABBED ||
|
||||||
check_con->orientation == HORIZ) &&
|
con_orientation(check_con) == HORIZ) &&
|
||||||
con_num_children(check_con) > 1) {
|
con_num_children(check_con) > 1) {
|
||||||
DLOG("Not handling this resize, this container has > 1 child.\n");
|
DLOG("Not handling this resize, this container has > 1 child.\n");
|
||||||
return false;
|
return false;
|
||||||
|
|
229
src/commands.c
229
src/commands.c
|
@ -1,3 +1,5 @@
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "commands.c"
|
||||||
/*
|
/*
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
|
@ -23,7 +25,7 @@
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/** When the command did not include match criteria (!), we use the currently
|
/** 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
|
* criteria but which did not match any windows. This macro has to be called in
|
||||||
* every command.
|
* every command.
|
||||||
*/
|
*/
|
||||||
|
@ -351,7 +353,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of 'move [window|container] [to] workspace
|
* 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) {
|
void cmd_move_con_to_workspace(I3_CMD, char *which) {
|
||||||
|
@ -359,6 +361,15 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
|
||||||
|
|
||||||
DLOG("which=%s\n", 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;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
/* get the workspace */
|
/* get the workspace */
|
||||||
|
@ -371,6 +382,8 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
|
||||||
ws = workspace_next_on_output();
|
ws = workspace_next_on_output();
|
||||||
else if (strcmp(which, "prev_on_output") == 0)
|
else if (strcmp(which, "prev_on_output") == 0)
|
||||||
ws = workspace_prev_on_output();
|
ws = workspace_prev_on_output();
|
||||||
|
else if (strcmp(which, "current") == 0)
|
||||||
|
ws = con_get_workspace(focused);
|
||||||
else {
|
else {
|
||||||
ELOG("BUG: called with which=%s\n", which);
|
ELOG("BUG: called with which=%s\n", which);
|
||||||
ysuccess(false);
|
ysuccess(false);
|
||||||
|
@ -400,9 +413,17 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
|
||||||
|
|
||||||
owindow *current;
|
owindow *current;
|
||||||
|
|
||||||
/* Error out early to not create a non-existing workspace (in
|
/* We have nothing to move:
|
||||||
* workspace_get()) if we are not actually able to move anything. */
|
* 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)) {
|
||||||
|
ELOG("No windows match your criteria, cannot move.\n");
|
||||||
|
ysuccess(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
|
if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
|
||||||
|
ELOG("No window to move, you have focused a workspace.\n");
|
||||||
ysuccess(false);
|
ysuccess(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -430,14 +451,16 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
|
||||||
void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
||||||
owindow *current;
|
owindow *current;
|
||||||
|
|
||||||
/* Error out early to not create a non-existing workspace (in
|
/* We have nothing to move:
|
||||||
* workspace_get()) if we are not actually able to move anything. */
|
* when criteria was specified but didn't match any window or
|
||||||
if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
|
* 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);
|
ysuccess(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("should move window to workspace with number %d\n", which);
|
LOG("should move window to workspace %s\n", which);
|
||||||
/* get the workspace */
|
/* get the workspace */
|
||||||
Con *output, *workspace = NULL;
|
Con *output, *workspace = NULL;
|
||||||
|
|
||||||
|
@ -463,14 +486,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
||||||
child->num == parsed_num);
|
child->num == parsed_num);
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
y(map_open);
|
workspace = workspace_get(which, NULL);
|
||||||
ystr("success");
|
|
||||||
y(bool, false);
|
|
||||||
ystr("error");
|
|
||||||
// TODO: better error message
|
|
||||||
ystr("No such workspace");
|
|
||||||
y(map_close);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
@ -504,6 +520,8 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
|
||||||
LOG("tiling resize\n");
|
LOG("tiling resize\n");
|
||||||
/* get the appropriate current container (skip stacked/tabbed cons) */
|
/* get the appropriate current container (skip stacked/tabbed cons) */
|
||||||
Con *current = focused;
|
Con *current = focused;
|
||||||
|
Con *other = NULL;
|
||||||
|
double percentage = 0;
|
||||||
while (current->parent->layout == L_STACKED ||
|
while (current->parent->layout == L_STACKED ||
|
||||||
current->parent->layout == L_TABBED)
|
current->parent->layout == L_TABBED)
|
||||||
current = current->parent;
|
current = current->parent;
|
||||||
|
@ -512,40 +530,50 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
|
||||||
orientation_t search_orientation =
|
orientation_t search_orientation =
|
||||||
(strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT);
|
(strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT);
|
||||||
|
|
||||||
while (current->type != CT_WORKSPACE &&
|
do {
|
||||||
current->type != CT_FLOATING_CON &&
|
if (con_orientation(current->parent) != search_orientation) {
|
||||||
current->parent->orientation != search_orientation)
|
current = current->parent;
|
||||||
current = current->parent;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* get the default percentage */
|
/* get the default percentage */
|
||||||
int children = con_num_children(current->parent);
|
int children = con_num_children(current->parent);
|
||||||
Con *other;
|
LOG("ins. %d children\n", children);
|
||||||
LOG("ins. %d children\n", children);
|
percentage = 1.0 / children;
|
||||||
double percentage = 1.0 / children;
|
LOG("default percentage = %f\n", percentage);
|
||||||
LOG("default percentage = %f\n", percentage);
|
|
||||||
|
|
||||||
orientation_t orientation = current->parent->orientation;
|
orientation_t orientation = con_orientation(current->parent);
|
||||||
|
|
||||||
if ((orientation == HORIZ &&
|
if ((orientation == HORIZ &&
|
||||||
(strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
|
(strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
|
||||||
(orientation == VERT &&
|
(orientation == VERT &&
|
||||||
(strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
|
(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",
|
LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
|
||||||
(orientation == HORIZ ? "horizontal" : "vertical"));
|
(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, 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);
|
ysuccess(false);
|
||||||
return 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");
|
|
||||||
ysuccess(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOG("other->percent = %f\n", other->percent);
|
LOG("other->percent = %f\n", other->percent);
|
||||||
LOG("current->percent before = %f\n", current->percent);
|
LOG("current->percent before = %f\n", current->percent);
|
||||||
if (current->percent == 0.0)
|
if (current->percent == 0.0)
|
||||||
|
@ -585,7 +613,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
|
||||||
|
|
||||||
while (current->type != CT_WORKSPACE &&
|
while (current->type != CT_WORKSPACE &&
|
||||||
current->type != CT_FLOATING_CON &&
|
current->type != CT_FLOATING_CON &&
|
||||||
current->parent->orientation != search_orientation)
|
con_orientation(current->parent) != search_orientation)
|
||||||
current = current->parent;
|
current = current->parent;
|
||||||
|
|
||||||
/* get the default percentage */
|
/* get the default percentage */
|
||||||
|
@ -594,7 +622,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
|
||||||
double percentage = 1.0 / children;
|
double percentage = 1.0 / children;
|
||||||
LOG("default percentage = %f\n", percentage);
|
LOG("default percentage = %f\n", percentage);
|
||||||
|
|
||||||
orientation_t orientation = current->parent->orientation;
|
orientation_t orientation = con_orientation(current->parent);
|
||||||
|
|
||||||
if ((orientation == HORIZ &&
|
if ((orientation == HORIZ &&
|
||||||
strcmp(direction, "height") == 0) ||
|
strcmp(direction, "height") == 0) ||
|
||||||
|
@ -808,17 +836,15 @@ void cmd_workspace_number(I3_CMD, char *which) {
|
||||||
child->num == parsed_num);
|
child->num == parsed_num);
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
LOG("There is no workspace with number %d, creating a new one.\n", parsed_num);
|
LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
|
||||||
ysuccess(true);
|
ysuccess(true);
|
||||||
/* terminate the which string after the endposition of the number */
|
/* terminate the which string after the endposition of the number */
|
||||||
*endptr = '\0';
|
*endptr = '\0';
|
||||||
if (maybe_back_and_forth(cmd_output, which))
|
|
||||||
return;
|
|
||||||
workspace_show_by_name(which);
|
workspace_show_by_name(which);
|
||||||
cmd_output->needs_tree_render = true;
|
cmd_output->needs_tree_render = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (maybe_back_and_forth(cmd_output, which))
|
if (maybe_back_and_forth(cmd_output, workspace->name))
|
||||||
return;
|
return;
|
||||||
workspace_show(workspace);
|
workspace_show(workspace);
|
||||||
|
|
||||||
|
@ -1088,9 +1114,17 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_split(I3_CMD, char *direction) {
|
void cmd_split(I3_CMD, char *direction) {
|
||||||
|
owindow *current;
|
||||||
/* TODO: use matches */
|
/* TODO: use matches */
|
||||||
LOG("splitting in direction %c\n", direction[0]);
|
LOG("splitting in direction %c\n", direction[0]);
|
||||||
tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
|
if (match_is_empty(current_match))
|
||||||
|
tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
|
||||||
|
else {
|
||||||
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
|
tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cmd_output->needs_tree_render = true;
|
cmd_output->needs_tree_render = true;
|
||||||
// XXX: default reply for now, make this a better reply
|
// XXX: default reply for now, make this a better reply
|
||||||
|
@ -1226,23 +1260,26 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_focus_level(I3_CMD, char *level) {
|
void cmd_focus_level(I3_CMD, char *level) {
|
||||||
if (focused &&
|
DLOG("level = %s\n", level);
|
||||||
focused->type != CT_WORKSPACE &&
|
bool success = false;
|
||||||
focused->fullscreen_mode != CF_NONE) {
|
|
||||||
LOG("Cannot change focus while in fullscreen mode.\n");
|
/* Focusing the parent can only be allowed if the newly
|
||||||
ysuccess(false);
|
* focused container won't escape the fullscreen container. */
|
||||||
return;
|
if (strcmp(level, "parent") == 0) {
|
||||||
|
if (focused && focused->parent) {
|
||||||
|
if (con_fullscreen_permits_focusing(focused->parent))
|
||||||
|
success = level_up();
|
||||||
|
else
|
||||||
|
ELOG("'focus parent': Currently in fullscreen, not going up\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DLOG("level = %s\n", level);
|
/* Focusing a child should always be allowed. */
|
||||||
|
else success = level_down();
|
||||||
|
|
||||||
if (strcmp(level, "parent") == 0)
|
cmd_output->needs_tree_render = success;
|
||||||
level_up();
|
|
||||||
else level_down();
|
|
||||||
|
|
||||||
cmd_output->needs_tree_render = true;
|
|
||||||
// XXX: default reply for now, make this a better reply
|
// XXX: default reply for now, make this a better reply
|
||||||
ysuccess(true);
|
ysuccess(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1275,13 +1312,9 @@ void cmd_focus(I3_CMD) {
|
||||||
if (!ws)
|
if (!ws)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Don't allow the focus switch if the focused and current
|
/* Check the fullscreen focus constraints. */
|
||||||
* containers are in the same workspace. */
|
if (!con_fullscreen_permits_focusing(current->con)) {
|
||||||
if (focused &&
|
LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n");
|
||||||
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");
|
|
||||||
ysuccess(false);
|
ysuccess(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1376,21 +1409,35 @@ 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) {
|
void cmd_layout(I3_CMD, char *layout_str) {
|
||||||
if (strcmp(layout_str, "stacking") == 0)
|
if (strcmp(layout_str, "stacking") == 0)
|
||||||
layout_str = "stacked";
|
layout_str = "stacked";
|
||||||
DLOG("changing layout to %s\n", layout_str);
|
|
||||||
owindow *current;
|
owindow *current;
|
||||||
int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT :
|
int layout;
|
||||||
(strcmp(layout_str, "stacked") == 0 ? L_STACKED :
|
/* default is a special case which will be handled in con_set_layout(). */
|
||||||
L_TABBED));
|
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;
|
||||||
|
else {
|
||||||
|
ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLOG("changing layout to %s (%d)\n", layout_str, layout);
|
||||||
|
|
||||||
/* check if the match is empty, not if the result is empty */
|
/* check if the match is empty, not if the result is empty */
|
||||||
if (match_is_empty(current_match))
|
if (match_is_empty(current_match))
|
||||||
con_set_layout(focused->parent, layout);
|
con_set_layout(focused, layout);
|
||||||
else {
|
else {
|
||||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
|
@ -1403,12 +1450,40 @@ void cmd_layout(I3_CMD, char *layout_str) {
|
||||||
ysuccess(true);
|
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, 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'.
|
* Implementaiton of 'exit'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_exit(I3_CMD) {
|
void cmd_exit(I3_CMD) {
|
||||||
LOG("Exiting due to user command.\n");
|
LOG("Exiting due to user command.\n");
|
||||||
|
xcb_disconnect(conn);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
/* unreached */
|
/* unreached */
|
||||||
|
@ -1421,6 +1496,7 @@ void cmd_exit(I3_CMD) {
|
||||||
void cmd_reload(I3_CMD) {
|
void cmd_reload(I3_CMD) {
|
||||||
LOG("reloading\n");
|
LOG("reloading\n");
|
||||||
kill_configerror_nagbar(false);
|
kill_configerror_nagbar(false);
|
||||||
|
kill_commanderror_nagbar(false);
|
||||||
load_configuration(conn, NULL, true);
|
load_configuration(conn, NULL, true);
|
||||||
x_set_i3_atoms();
|
x_set_i3_atoms();
|
||||||
/* Send an IPC event just in case the ws names have changed */
|
/* Send an IPC event just in case the ws names have changed */
|
||||||
|
@ -1449,6 +1525,7 @@ void cmd_restart(I3_CMD) {
|
||||||
void cmd_open(I3_CMD) {
|
void cmd_open(I3_CMD) {
|
||||||
LOG("opening new container\n");
|
LOG("opening new container\n");
|
||||||
Con *con = tree_open_con(NULL, NULL);
|
Con *con = tree_open_con(NULL, NULL);
|
||||||
|
con->layout = L_SPLITH;
|
||||||
con_focus(con);
|
con_focus(con);
|
||||||
|
|
||||||
y(map_open);
|
y(map_open);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "commands_parser.c"
|
||||||
/*
|
/*
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
|
@ -104,7 +106,6 @@ static void push_string(const char *identifier, char *str) {
|
||||||
// XXX: ideally, this would be const char. need to check if that works with all
|
// XXX: ideally, this would be const char. need to check if that works with all
|
||||||
// called functions.
|
// called functions.
|
||||||
static char *get_string(const char *identifier) {
|
static char *get_string(const char *identifier) {
|
||||||
DLOG("Getting string %s from stack...\n", identifier);
|
|
||||||
for (int c = 0; c < 10; c++) {
|
for (int c = 0; c < 10; c++) {
|
||||||
if (stack[c].identifier == NULL)
|
if (stack[c].identifier == NULL)
|
||||||
break;
|
break;
|
||||||
|
@ -115,7 +116,6 @@ static char *get_string(const char *identifier) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_stack(void) {
|
static void clear_stack(void) {
|
||||||
DLOG("clearing stack.\n");
|
|
||||||
for (int c = 0; c < 10; c++) {
|
for (int c = 0; c < 10; c++) {
|
||||||
if (stack[c].str != NULL)
|
if (stack[c].str != NULL)
|
||||||
free(stack[c].str);
|
free(stack[c].str);
|
||||||
|
@ -187,8 +187,6 @@ static struct CommandResult command_output;
|
||||||
|
|
||||||
static void next_state(const cmdp_token *token) {
|
static void next_state(const cmdp_token *token) {
|
||||||
if (token->next_state == __CALL) {
|
if (token->next_state == __CALL) {
|
||||||
DLOG("should call stuff, yay. call_id = %d\n",
|
|
||||||
token->extra.call_identifier);
|
|
||||||
subcommand_output.json_gen = command_output.json_gen;
|
subcommand_output.json_gen = command_output.json_gen;
|
||||||
subcommand_output.needs_tree_render = false;
|
subcommand_output.needs_tree_render = false;
|
||||||
GENERATED_call(token->extra.call_identifier, &subcommand_output);
|
GENERATED_call(token->extra.call_identifier, &subcommand_output);
|
||||||
|
@ -206,9 +204,8 @@ static void next_state(const cmdp_token *token) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Return parsing errors via JSON. */
|
|
||||||
struct CommandResult *parse_command(const char *input) {
|
struct CommandResult *parse_command(const char *input) {
|
||||||
DLOG("new parser handling: %s\n", input);
|
DLOG("COMMAND: *%s*\n", input);
|
||||||
state = INITIAL;
|
state = INITIAL;
|
||||||
|
|
||||||
/* A YAJL JSON generator used for formatting replies. */
|
/* A YAJL JSON generator used for formatting replies. */
|
||||||
|
@ -240,19 +237,14 @@ struct CommandResult *parse_command(const char *input) {
|
||||||
*walk == '\r' || *walk == '\n') && *walk != '\0')
|
*walk == '\r' || *walk == '\n') && *walk != '\0')
|
||||||
walk++;
|
walk++;
|
||||||
|
|
||||||
DLOG("remaining input = %s\n", walk);
|
|
||||||
|
|
||||||
cmdp_token_ptr *ptr = &(tokens[state]);
|
cmdp_token_ptr *ptr = &(tokens[state]);
|
||||||
token_handled = false;
|
token_handled = false;
|
||||||
for (c = 0; c < ptr->n; c++) {
|
for (c = 0; c < ptr->n; c++) {
|
||||||
token = &(ptr->array[c]);
|
token = &(ptr->array[c]);
|
||||||
DLOG("trying token %d = %s\n", c, token->name);
|
|
||||||
|
|
||||||
/* A literal. */
|
/* A literal. */
|
||||||
if (token->name[0] == '\'') {
|
if (token->name[0] == '\'') {
|
||||||
DLOG("literal\n");
|
|
||||||
if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
|
if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
|
||||||
DLOG("found literal, moving to next state\n");
|
|
||||||
if (token->identifier != NULL)
|
if (token->identifier != NULL)
|
||||||
push_string(token->identifier, sstrdup(token->name + 1));
|
push_string(token->identifier, sstrdup(token->name + 1));
|
||||||
walk += strlen(token->name) - 1;
|
walk += strlen(token->name) - 1;
|
||||||
|
@ -265,7 +257,6 @@ struct CommandResult *parse_command(const char *input) {
|
||||||
|
|
||||||
if (strcmp(token->name, "string") == 0 ||
|
if (strcmp(token->name, "string") == 0 ||
|
||||||
strcmp(token->name, "word") == 0) {
|
strcmp(token->name, "word") == 0) {
|
||||||
DLOG("parsing this as a string\n");
|
|
||||||
const char *beginning = walk;
|
const char *beginning = walk;
|
||||||
/* Handle quoted strings (or words). */
|
/* Handle quoted strings (or words). */
|
||||||
if (*walk == '"') {
|
if (*walk == '"') {
|
||||||
|
@ -310,7 +301,6 @@ struct CommandResult *parse_command(const char *input) {
|
||||||
}
|
}
|
||||||
if (token->identifier)
|
if (token->identifier)
|
||||||
push_string(token->identifier, str);
|
push_string(token->identifier, str);
|
||||||
DLOG("str is \"%s\"\n", str);
|
|
||||||
/* If we are at the end of a quoted string, skip the ending
|
/* If we are at the end of a quoted string, skip the ending
|
||||||
* double quote. */
|
* double quote. */
|
||||||
if (*walk == '"')
|
if (*walk == '"')
|
||||||
|
@ -322,9 +312,7 @@ struct CommandResult *parse_command(const char *input) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(token->name, "end") == 0) {
|
if (strcmp(token->name, "end") == 0) {
|
||||||
DLOG("checking for the end token.\n");
|
|
||||||
if (*walk == '\0' || *walk == ',' || *walk == ';') {
|
if (*walk == '\0' || *walk == ',' || *walk == ';') {
|
||||||
DLOG("yes, indeed. end\n");
|
|
||||||
next_state(token);
|
next_state(token);
|
||||||
token_handled = true;
|
token_handled = true;
|
||||||
/* To make sure we start with an appropriate matching
|
/* To make sure we start with an appropriate matching
|
||||||
|
@ -389,14 +377,19 @@ struct CommandResult *parse_command(const char *input) {
|
||||||
position[(copywalk - input)] = (copywalk >= walk ? '^' : ' ');
|
position[(copywalk - input)] = (copywalk >= walk ? '^' : ' ');
|
||||||
position[len] = '\0';
|
position[len] = '\0';
|
||||||
|
|
||||||
printf("%s\n", errormessage);
|
ELOG("%s\n", errormessage);
|
||||||
printf("Your command: %s\n", input);
|
ELOG("Your command: %s\n", input);
|
||||||
printf(" %s\n", position);
|
ELOG(" %s\n", position);
|
||||||
|
|
||||||
/* Format this error message as a JSON reply. */
|
/* Format this error message as a JSON reply. */
|
||||||
y(map_open);
|
y(map_open);
|
||||||
ystr("success");
|
ystr("success");
|
||||||
y(bool, false);
|
y(bool, false);
|
||||||
|
/* We set parse_error to true to distinguish this from other
|
||||||
|
* errors. i3-nagbar is spawned upon keypresses only for parser
|
||||||
|
* errors. */
|
||||||
|
ystr("parse_error");
|
||||||
|
y(bool, true);
|
||||||
ystr("error");
|
ystr("error");
|
||||||
ystr(errormessage);
|
ystr(errormessage);
|
||||||
ystr("input");
|
ystr("input");
|
||||||
|
@ -414,7 +407,6 @@ struct CommandResult *parse_command(const char *input) {
|
||||||
|
|
||||||
y(array_close);
|
y(array_close);
|
||||||
|
|
||||||
DLOG("command_output.needs_tree_render = %d\n", command_output.needs_tree_render);
|
|
||||||
return &command_output;
|
return &command_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,15 +419,23 @@ struct CommandResult *parse_command(const char *input) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Logs the given message to stdout while prefixing the current time to it,
|
* 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
|
* 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_list args;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
fprintf(stderr, "# ");
|
|
||||||
vfprintf(stderr, fmt, args);
|
vfprintf(stderr, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
355
src/con.c
355
src/con.c
|
@ -1,3 +1,5 @@
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "con.c"
|
||||||
/*
|
/*
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
|
@ -133,7 +135,7 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) {
|
||||||
*/
|
*/
|
||||||
if (con->window != NULL &&
|
if (con->window != NULL &&
|
||||||
parent->type == CT_WORKSPACE &&
|
parent->type == CT_WORKSPACE &&
|
||||||
config.default_layout != L_DEFAULT) {
|
parent->workspace_layout != L_DEFAULT) {
|
||||||
DLOG("Parent is a workspace. Applying default layout...\n");
|
DLOG("Parent is a workspace. Applying default layout...\n");
|
||||||
Con *target = workspace_attach_to(parent);
|
Con *target = workspace_attach_to(parent);
|
||||||
|
|
||||||
|
@ -217,8 +219,8 @@ bool con_accepts_window(Con *con) {
|
||||||
if (con->type == CT_WORKSPACE)
|
if (con->type == CT_WORKSPACE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (con->orientation != NO_ORIENTATION) {
|
if (con->split) {
|
||||||
DLOG("container %p does not accepts windows, orientation != NO_ORIENTATION\n", con);
|
DLOG("container %p does not accept windows, it is a split container.\n", con);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,8 +267,11 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation) {
|
||||||
while (con_orientation(parent) != orientation) {
|
while (con_orientation(parent) != orientation) {
|
||||||
DLOG("Need to go one level further up\n");
|
DLOG("Need to go one level further up\n");
|
||||||
parent = parent->parent;
|
parent = parent->parent;
|
||||||
/* Abort when we reach a floating con */
|
/* Abort when we reach a floating con, or an output con */
|
||||||
if (parent && parent->type == CT_FLOATING_CON)
|
if (parent &&
|
||||||
|
(parent->type == CT_FLOATING_CON ||
|
||||||
|
parent->type == CT_OUTPUT ||
|
||||||
|
(parent->parent && parent->parent->type == CT_OUTPUT)))
|
||||||
parent = NULL;
|
parent = NULL;
|
||||||
if (parent == NULL)
|
if (parent == NULL)
|
||||||
break;
|
break;
|
||||||
|
@ -576,11 +581,27 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
||||||
return;
|
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)) {
|
if (con_is_floating(con)) {
|
||||||
DLOG("Using FLOATINGCON instead\n");
|
DLOG("Using FLOATINGCON instead\n");
|
||||||
con = con->parent;
|
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),
|
Con *source_output = con_get_output(con),
|
||||||
*dest_output = con_get_output(workspace);
|
*dest_output = con_get_output(workspace);
|
||||||
|
|
||||||
|
@ -665,8 +686,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
|
/* Descend focus stack in case focus_next is a workspace which can
|
||||||
* occur if we move to the same workspace. Also show current workspace
|
* occur if we move to the same workspace. Also show current workspace
|
||||||
* to ensure it is focused. */
|
* to ensure it is focused. */
|
||||||
workspace_show(con_get_workspace(focus_next));
|
workspace_show(current_ws);
|
||||||
con_focus(con_descend_focused(focus_next));
|
|
||||||
|
/* 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);
|
CALL(parent, on_remove_child);
|
||||||
|
@ -679,14 +704,32 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int con_orientation(Con *con) {
|
int con_orientation(Con *con) {
|
||||||
/* stacking containers behave like they are in vertical orientation */
|
switch (con->layout) {
|
||||||
if (con->layout == L_STACKED)
|
case L_SPLITV:
|
||||||
return VERT;
|
/* stacking containers behave like they are in vertical orientation */
|
||||||
|
case L_STACKED:
|
||||||
|
return VERT;
|
||||||
|
|
||||||
if (con->layout == L_TABBED)
|
case L_SPLITH:
|
||||||
return HORIZ;
|
/* tabbed containers behave like they are in vertical orientation */
|
||||||
|
case L_TABBED:
|
||||||
|
return HORIZ;
|
||||||
|
|
||||||
return con->orientation;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -895,12 +938,46 @@ Con *con_descend_direction(Con *con, direction_t direction) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Rect con_border_style_rect(Con *con) {
|
Rect con_border_style_rect(Con *con) {
|
||||||
switch (con_border_style(con)) {
|
adjacent_t borders_to_hide = ADJ_NONE;
|
||||||
|
Rect result;
|
||||||
|
/* Shortcut to avoid calling con_adjacent_borders() on dock containers. */
|
||||||
|
int border_style = con_border_style(con);
|
||||||
|
if (border_style == BS_NONE)
|
||||||
|
return (Rect){ 0, 0, 0, 0 };
|
||||||
|
borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
|
||||||
|
switch (border_style) {
|
||||||
case BS_NORMAL:
|
case BS_NORMAL:
|
||||||
return (Rect){2, 0, -(2 * 2), -2};
|
result = (Rect){2, 0, -(2 * 2), -2};
|
||||||
|
if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
|
||||||
|
result.x -= 2;
|
||||||
|
result.width += 2;
|
||||||
|
}
|
||||||
|
if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
|
||||||
|
result.width += 2;
|
||||||
|
}
|
||||||
|
/* With normal borders we never hide the upper border */
|
||||||
|
if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
|
||||||
|
result.height += 2;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
case BS_1PIXEL:
|
case BS_1PIXEL:
|
||||||
return (Rect){1, 1, -2, -2};
|
result = (Rect){1, 1, -2, -2};
|
||||||
|
if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
|
||||||
|
result.x -= 1;
|
||||||
|
result.width += 1;
|
||||||
|
}
|
||||||
|
if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
|
||||||
|
result.width += 1;
|
||||||
|
}
|
||||||
|
if (borders_to_hide & ADJ_UPPER_SCREEN_EDGE) {
|
||||||
|
result.y -= 1;
|
||||||
|
result.height += 1;
|
||||||
|
}
|
||||||
|
if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
|
||||||
|
result.height += 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
case BS_NONE:
|
case BS_NONE:
|
||||||
return (Rect){0, 0, 0, 0};
|
return (Rect){0, 0, 0, 0};
|
||||||
|
@ -910,6 +987,24 @@ Rect con_border_style_rect(Con *con) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns adjacent borders of the window. We need this if hide_edge_borders is
|
||||||
|
* enabled.
|
||||||
|
*/
|
||||||
|
adjacent_t con_adjacent_borders(Con *con) {
|
||||||
|
adjacent_t result = ADJ_NONE;
|
||||||
|
Con *workspace = con_get_workspace(con);
|
||||||
|
if (con->rect.x == workspace->rect.x)
|
||||||
|
result |= ADJ_LEFT_SCREEN_EDGE;
|
||||||
|
if (con->rect.x + con->rect.width == workspace->rect.x + workspace->rect.width)
|
||||||
|
result |= ADJ_RIGHT_SCREEN_EDGE;
|
||||||
|
if (con->rect.y == workspace->rect.y)
|
||||||
|
result |= ADJ_UPPER_SCREEN_EDGE;
|
||||||
|
if (con->rect.y + con->rect.height == workspace->rect.y + workspace->rect.height)
|
||||||
|
result |= ADJ_LOWER_SCREEN_EDGE;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use this function to get a container’s border style. This is important
|
* Use this function to get a container’s border style. This is important
|
||||||
* because when inside a stack, the border style is always BS_NORMAL.
|
* because when inside a stack, the border style is always BS_NORMAL.
|
||||||
|
@ -989,54 +1084,133 @@ void con_set_border_style(Con *con, int border_style) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void con_set_layout(Con *con, int layout) {
|
void con_set_layout(Con *con, int layout) {
|
||||||
|
DLOG("con_set_layout(%p, %d), con->type = %d\n",
|
||||||
|
con, layout, con->type);
|
||||||
|
|
||||||
|
/* Users can focus workspaces, but not any higher in the hierarchy.
|
||||||
|
* Focus on the workspace is a special case, since in every other case, the
|
||||||
|
* user means "change the layout of the parent split container". */
|
||||||
|
if (con->type != CT_WORKSPACE)
|
||||||
|
con = con->parent;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
/* When the container type is CT_WORKSPACE, the user wants to change the
|
/* When the container type is CT_WORKSPACE, the user wants to change the
|
||||||
* whole workspace into stacked/tabbed mode. To do this and still allow
|
* whole workspace into stacked/tabbed mode. To do this and still allow
|
||||||
* intuitive operations (like level-up and then opening a new window), we
|
* intuitive operations (like level-up and then opening a new window), we
|
||||||
* need to create a new split container. */
|
* need to create a new split container. */
|
||||||
if (con->type == CT_WORKSPACE) {
|
if (con->type == CT_WORKSPACE &&
|
||||||
DLOG("Creating new split container\n");
|
(layout == L_STACKED || layout == L_TABBED)) {
|
||||||
/* 1: create a new split container */
|
if (con_num_children(con) == 0) {
|
||||||
Con *new = con_new(NULL, NULL);
|
DLOG("Setting workspace_layout to %d\n", layout);
|
||||||
new->parent = con;
|
con->workspace_layout = layout;
|
||||||
|
|
||||||
/* 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 {
|
} else {
|
||||||
new->orientation = config.default_orientation;
|
DLOG("Creating new split container\n");
|
||||||
|
/* 1: create a new split container */
|
||||||
|
Con *new = con_new(NULL, NULL);
|
||||||
|
new->parent = con;
|
||||||
|
|
||||||
|
/* 2: Set the requested layout on the split container and mark it as
|
||||||
|
* split. */
|
||||||
|
new->layout = layout;
|
||||||
|
new->last_split_layout = con->last_split_layout;
|
||||||
|
new->split = true;
|
||||||
|
|
||||||
|
Con *old_focused = TAILQ_FIRST(&(con->focus_head));
|
||||||
|
if (old_focused == TAILQ_END(&(con->focus_head)))
|
||||||
|
old_focused = NULL;
|
||||||
|
|
||||||
|
/* 3: move the existing cons of this workspace below the new con */
|
||||||
|
DLOG("Moving cons\n");
|
||||||
|
Con *child;
|
||||||
|
while (!TAILQ_EMPTY(&(con->nodes_head))) {
|
||||||
|
child = TAILQ_FIRST(&(con->nodes_head));
|
||||||
|
con_detach(child);
|
||||||
|
con_attach(child, new, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4: attach the new split container to the workspace */
|
||||||
|
DLOG("Attaching new split to ws\n");
|
||||||
|
con_attach(new, con, false);
|
||||||
|
|
||||||
|
if (old_focused)
|
||||||
|
con_focus(old_focused);
|
||||||
|
|
||||||
|
tree_flatten(croot);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 */
|
|
||||||
DLOG("Moving cons\n");
|
|
||||||
Con *child;
|
|
||||||
while (!TAILQ_EMPTY(&(con->nodes_head))) {
|
|
||||||
child = TAILQ_FIRST(&(con->nodes_head));
|
|
||||||
con_detach(child);
|
|
||||||
con_attach(child, new, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 4: attach the new split container to the workspace */
|
|
||||||
DLOG("Attaching new split to ws\n");
|
|
||||||
con_attach(new, con, false);
|
|
||||||
|
|
||||||
if (old_focused)
|
|
||||||
con_focus(old_focused);
|
|
||||||
|
|
||||||
tree_flatten(croot);
|
|
||||||
|
|
||||||
return;
|
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 {
|
||||||
|
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) {
|
||||||
|
Con *parent = con;
|
||||||
|
/* Users can focus workspaces, but not any higher in the hierarchy.
|
||||||
|
* Focus on the workspace is a special case, since in every other case, the
|
||||||
|
* user means "change the layout of the parent split container". */
|
||||||
|
if (con->type != CT_WORKSPACE)
|
||||||
|
parent = con->parent;
|
||||||
|
DLOG("con_toggle_layout(%p, %s), parent = %p\n", con, toggle_mode, parent);
|
||||||
|
|
||||||
|
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 (parent->layout != L_SPLITH && parent->layout != L_SPLITV)
|
||||||
|
con_set_layout(con, parent->last_split_layout);
|
||||||
|
else {
|
||||||
|
if (parent->layout == L_SPLITH)
|
||||||
|
con_set_layout(con, L_SPLITV);
|
||||||
|
else con_set_layout(con, L_SPLITH);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (parent->layout == L_STACKED)
|
||||||
|
con_set_layout(con, L_TABBED);
|
||||||
|
else if (parent->layout == L_TABBED) {
|
||||||
|
if (strcmp(toggle_mode, "all") == 0)
|
||||||
|
con_set_layout(con, L_SPLITH);
|
||||||
|
else con_set_layout(con, parent->last_split_layout);
|
||||||
|
} else if (parent->layout == L_SPLITH || parent->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 (parent->layout == L_SPLITH)
|
||||||
|
con_set_layout(con, L_SPLITV);
|
||||||
|
else con_set_layout(con, L_STACKED);
|
||||||
|
} else {
|
||||||
|
con_set_layout(con, L_STACKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1113,12 +1287,12 @@ Rect con_minimum_size(Con *con) {
|
||||||
/* For horizontal/vertical split containers we sum up the width (h-split)
|
/* 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
|
* or height (v-split) and use the maximum of the height (h-split) or width
|
||||||
* (v-split) as minimum size. */
|
* (v-split) as minimum size. */
|
||||||
if (con->orientation == HORIZ || con->orientation == VERT) {
|
if (con->split) {
|
||||||
uint32_t width = 0, height = 0;
|
uint32_t width = 0, height = 0;
|
||||||
Con *child;
|
Con *child;
|
||||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
Rect min = con_minimum_size(child);
|
Rect min = con_minimum_size(child);
|
||||||
if (con->orientation == HORIZ) {
|
if (con->layout == L_SPLITH) {
|
||||||
width += min.width;
|
width += min.width;
|
||||||
height = max(height, min.height);
|
height = max(height, min.height);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1130,7 +1304,70 @@ Rect con_minimum_size(Con *con) {
|
||||||
return (Rect){ 0, 0, width, height };
|
return (Rect){ 0, 0, width, height };
|
||||||
}
|
}
|
||||||
|
|
||||||
ELOG("Unhandled case, type = %d, layout = %d, orientation = %d\n",
|
ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
|
||||||
con->type, con->layout, con->orientation);
|
con->type, con->layout, con->split);
|
||||||
assert(false);
|
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;
|
||||||
|
|
||||||
|
/* fs must be non-NULL since the workspace con doesn’t have CF_NONE and
|
||||||
|
* there always has to be a workspace con in the hierarchy. */
|
||||||
|
assert(fs != NULL);
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
156
src/config.c
156
src/config.c
|
@ -1,3 +1,5 @@
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "config.c"
|
||||||
/*
|
/*
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
|
@ -24,27 +26,27 @@ struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void ungrab_all_keys(xcb_connection_t *conn) {
|
void ungrab_all_keys(xcb_connection_t *conn) {
|
||||||
DLOG("Ungrabbing all keys\n");
|
DLOG("Ungrabbing all keys\n");
|
||||||
xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
|
xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) {
|
static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) {
|
||||||
DLOG("Grabbing %d\n", keycode);
|
DLOG("Grabbing %d\n", keycode);
|
||||||
/* Grab the key in all combinations */
|
/* Grab the key in all combinations */
|
||||||
#define GRAB_KEY(modifier) \
|
#define GRAB_KEY(modifier) \
|
||||||
do { \
|
do { \
|
||||||
xcb_grab_key(conn, 0, root, modifier, keycode, \
|
xcb_grab_key(conn, 0, root, modifier, keycode, \
|
||||||
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \
|
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \
|
||||||
} while (0)
|
} while (0)
|
||||||
int mods = bind->mods;
|
int mods = bind->mods;
|
||||||
if ((bind->mods & BIND_MODE_SWITCH) != 0) {
|
if ((bind->mods & BIND_MODE_SWITCH) != 0) {
|
||||||
mods &= ~BIND_MODE_SWITCH;
|
mods &= ~BIND_MODE_SWITCH;
|
||||||
if (mods == 0)
|
if (mods == 0)
|
||||||
mods = XCB_MOD_MASK_ANY;
|
mods = XCB_MOD_MASK_ANY;
|
||||||
}
|
}
|
||||||
GRAB_KEY(mods);
|
GRAB_KEY(mods);
|
||||||
GRAB_KEY(mods | xcb_numlock_mask);
|
GRAB_KEY(mods | xcb_numlock_mask);
|
||||||
GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -52,29 +54,55 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint
|
||||||
* or NULL if no such binding exists.
|
* or NULL if no such binding exists.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode) {
|
Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode) {
|
||||||
Binding *bind;
|
Binding *bind;
|
||||||
|
|
||||||
|
if (!key_release) {
|
||||||
|
/* On a KeyPress event, we first reset all
|
||||||
|
* B_UPON_KEYRELEASE_IGNORE_MODS bindings back to B_UPON_KEYRELEASE */
|
||||||
TAILQ_FOREACH(bind, bindings, bindings) {
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
/* First compare the modifiers */
|
if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
|
||||||
if (bind->mods != modifiers)
|
bind->release = B_UPON_KEYRELEASE;
|
||||||
continue;
|
|
||||||
|
|
||||||
/* If a symbol was specified by the user, we need to look in
|
|
||||||
* the array of translated keycodes for the event’s keycode */
|
|
||||||
if (bind->symbol != NULL) {
|
|
||||||
if (memmem(bind->translated_to,
|
|
||||||
bind->number_keycodes * sizeof(xcb_keycode_t),
|
|
||||||
&keycode, sizeof(xcb_keycode_t)) != NULL)
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
/* This case is easier: The user specified a keycode */
|
|
||||||
if (bind->keycode == keycode)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (bind == TAILQ_END(bindings) ? NULL : bind);
|
/* Then we transition the KeyRelease bindings into a state where the
|
||||||
|
* modifiers no longer matter for the KeyRelease event so that users
|
||||||
|
* can release the modifier key before releasing the actual key. */
|
||||||
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
|
if (bind->release == B_UPON_KEYRELEASE && !key_release)
|
||||||
|
bind->release = B_UPON_KEYRELEASE_IGNORE_MODS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
|
/* First compare the modifiers (unless this is a
|
||||||
|
* B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
|
||||||
|
* event) */
|
||||||
|
if (bind->mods != modifiers &&
|
||||||
|
(bind->release != B_UPON_KEYRELEASE_IGNORE_MODS ||
|
||||||
|
!key_release))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Check if the binding is for a KeyPress or a KeyRelease event */
|
||||||
|
if ((bind->release == B_UPON_KEYPRESS && key_release) ||
|
||||||
|
(bind->release >= B_UPON_KEYRELEASE && !key_release))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* If a symbol was specified by the user, we need to look in
|
||||||
|
* the array of translated keycodes for the event’s keycode */
|
||||||
|
if (bind->symbol != NULL) {
|
||||||
|
if (memmem(bind->translated_to,
|
||||||
|
bind->number_keycodes * sizeof(xcb_keycode_t),
|
||||||
|
&keycode, sizeof(xcb_keycode_t)) != NULL)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* This case is easier: The user specified a keycode */
|
||||||
|
if (bind->keycode == keycode)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bind == TAILQ_END(bindings) ? NULL : bind);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -131,22 +159,22 @@ void translate_keysyms(void) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
|
void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
|
||||||
Binding *bind;
|
Binding *bind;
|
||||||
TAILQ_FOREACH(bind, bindings, bindings) {
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
if ((bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
|
if ((bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
|
||||||
(!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0))
|
(!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* The easy case: the user specified a keycode directly. */
|
/* The easy case: the user specified a keycode directly. */
|
||||||
if (bind->keycode > 0) {
|
if (bind->keycode > 0) {
|
||||||
grab_keycode_for_binding(conn, bind, bind->keycode);
|
grab_keycode_for_binding(conn, bind, bind->keycode);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
xcb_keycode_t *walk = bind->translated_to;
|
|
||||||
for (int i = 0; i < bind->number_keycodes; i++)
|
|
||||||
grab_keycode_for_binding(conn, bind, *walk++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xcb_keycode_t *walk = bind->translated_to;
|
||||||
|
for (int i = 0; i < bind->number_keycodes; i++)
|
||||||
|
grab_keycode_for_binding(conn, bind, *walk++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -154,22 +182,22 @@ void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void switch_mode(const char *new_mode) {
|
void switch_mode(const char *new_mode) {
|
||||||
struct Mode *mode;
|
struct Mode *mode;
|
||||||
|
|
||||||
LOG("Switching to mode %s\n", new_mode);
|
LOG("Switching to mode %s\n", new_mode);
|
||||||
|
|
||||||
SLIST_FOREACH(mode, &modes, modes) {
|
SLIST_FOREACH(mode, &modes, modes) {
|
||||||
if (strcasecmp(mode->name, new_mode) != 0)
|
if (strcasecmp(mode->name, new_mode) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ungrab_all_keys(conn);
|
ungrab_all_keys(conn);
|
||||||
bindings = mode->bindings;
|
bindings = mode->bindings;
|
||||||
translate_keysyms();
|
translate_keysyms();
|
||||||
grab_all_keys(conn, false);
|
grab_all_keys(conn, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ELOG("ERROR: Mode not found\n");
|
ELOG("ERROR: Mode not found\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -392,7 +420,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
||||||
grab_all_keys(conn, false);
|
grab_all_keys(conn, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.font.id == 0) {
|
if (config.font.type == FONT_TYPE_NONE) {
|
||||||
ELOG("You did not specify required configuration option \"font\"\n");
|
ELOG("You did not specify required configuration option \"font\"\n");
|
||||||
config.font = load_font("fixed", true);
|
config.font = load_font("fixed", true);
|
||||||
set_font(&config.font);
|
set_font(&config.font);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "debug.c"
|
||||||
/*
|
/*
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "key_press.c"
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* display_version.c: displays the running i3 version, runs as part of
|
||||||
|
* i3 --moreversion.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "all.h"
|
||||||
|
|
||||||
|
static bool human_readable_key;
|
||||||
|
static char *human_readable_version;
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int version_string(void *ctx, const unsigned char *val, size_t len) {
|
||||||
|
#else
|
||||||
|
static int version_string(void *ctx, const unsigned char *val, unsigned int len) {
|
||||||
|
#endif
|
||||||
|
if (human_readable_key)
|
||||||
|
sasprintf(&human_readable_version, "%.*s", (int)len, val);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
|
||||||
|
#else
|
||||||
|
static int version_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
|
||||||
|
#endif
|
||||||
|
human_readable_key = (stringlen == strlen("human_readable") &&
|
||||||
|
strncmp((const char*)stringval, "human_readable", strlen("human_readable")) == 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static yajl_callbacks version_callbacks = {
|
||||||
|
NULL, /* null */
|
||||||
|
NULL, /* boolean */
|
||||||
|
NULL, /* integer */
|
||||||
|
NULL, /* double */
|
||||||
|
NULL, /* number */
|
||||||
|
&version_string,
|
||||||
|
NULL, /* start_map */
|
||||||
|
&version_map_key,
|
||||||
|
NULL, /* end_map */
|
||||||
|
NULL, /* start_array */
|
||||||
|
NULL /* end_array */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connects to i3 to find out the currently running version. Useful since it
|
||||||
|
* might be different from the version compiled into this binary (maybe the
|
||||||
|
* user didn’t correctly install i3 or forgot te restart it).
|
||||||
|
*
|
||||||
|
* The output looks like this:
|
||||||
|
* Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804)
|
||||||
|
*
|
||||||
|
* The i3 binary you just called: /home/michael/i3/i3
|
||||||
|
* The i3 binary you are running: /home/michael/i3/i3
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void display_running_version(void) {
|
||||||
|
char *socket_path = root_atom_contents("I3_SOCKET_PATH");
|
||||||
|
if (socket_path == NULL)
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
char *pid_from_atom = root_atom_contents("I3_PID");
|
||||||
|
if (pid_from_atom == NULL) {
|
||||||
|
/* If I3_PID is not set, the running version is older than 4.2-200. */
|
||||||
|
printf("\nRunning version: < 4.2-200\n");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inform the user of what we are doing. While a single IPC request is
|
||||||
|
* really fast normally, in case i3 hangs, this will not terminate. */
|
||||||
|
printf("(Getting version from running i3, press ctrl-c to abort…)");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
/* TODO: refactor this with the code for sending commands */
|
||||||
|
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||||
|
if (sockfd == -1)
|
||||||
|
err(EXIT_FAILURE, "Could not create socket");
|
||||||
|
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||||
|
addr.sun_family = AF_LOCAL;
|
||||||
|
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
||||||
|
if (connect(sockfd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0)
|
||||||
|
err(EXIT_FAILURE, "Could not connect to i3");
|
||||||
|
|
||||||
|
if (ipc_send_message(sockfd, 0, I3_IPC_MESSAGE_TYPE_GET_VERSION,
|
||||||
|
(uint8_t*)"") == -1)
|
||||||
|
err(EXIT_FAILURE, "IPC: write()");
|
||||||
|
|
||||||
|
uint32_t reply_length;
|
||||||
|
uint8_t *reply;
|
||||||
|
int ret;
|
||||||
|
if ((ret = ipc_recv_message(sockfd, I3_IPC_MESSAGE_TYPE_GET_VERSION,
|
||||||
|
&reply_length, &reply)) != 0) {
|
||||||
|
if (ret == -1)
|
||||||
|
err(EXIT_FAILURE, "IPC: read()");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL);
|
||||||
|
#else
|
||||||
|
yajl_parser_config parse_conf = { 0, 0 };
|
||||||
|
|
||||||
|
yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
yajl_status state = yajl_parse(handle, (const unsigned char*)reply, (int)reply_length);
|
||||||
|
if (state != yajl_status_ok)
|
||||||
|
errx(EXIT_FAILURE, "Could not parse my own reply. That's weird. reply is %.*s", (int)reply_length, reply);
|
||||||
|
|
||||||
|
printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
char exepath[PATH_MAX],
|
||||||
|
destpath[PATH_MAX];
|
||||||
|
ssize_t linksize;
|
||||||
|
|
||||||
|
snprintf(exepath, sizeof(exepath), "/proc/%d/exe", getpid());
|
||||||
|
|
||||||
|
if ((linksize = readlink(exepath, destpath, sizeof(destpath))) == -1)
|
||||||
|
err(EXIT_FAILURE, "readlink(%s)", exepath);
|
||||||
|
|
||||||
|
/* readlink() does not NULL-terminate strings, so we have to. */
|
||||||
|
destpath[linksize] = '\0';
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("The i3 binary you just called: %s\n", destpath);
|
||||||
|
|
||||||
|
snprintf(exepath, sizeof(exepath), "/proc/%s/exe", pid_from_atom);
|
||||||
|
|
||||||
|
if ((linksize = readlink(exepath, destpath, sizeof(destpath))) == -1)
|
||||||
|
err(EXIT_FAILURE, "readlink(%s)", exepath);
|
||||||
|
|
||||||
|
/* readlink() does not NULL-terminate strings, so we have to. */
|
||||||
|
destpath[linksize] = '\0';
|
||||||
|
|
||||||
|
/* Check if "(deleted)" is the readlink result. If so, the running version
|
||||||
|
* does not match the file on disk. */
|
||||||
|
if (strstr(destpath, "(deleted)") != NULL)
|
||||||
|
printf("RUNNING BINARY DIFFERENT FROM BINARY ON DISK!\n");
|
||||||
|
|
||||||
|
/* Since readlink() might put a "(deleted)" somewhere in the buffer and
|
||||||
|
* stripping that out seems hackish and ugly, we read the process’s argv[0]
|
||||||
|
* instead. */
|
||||||
|
snprintf(exepath, sizeof(exepath), "/proc/%s/cmdline", pid_from_atom);
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
if ((fd = open(exepath, O_RDONLY)) == -1)
|
||||||
|
err(EXIT_FAILURE, "open(%s)", exepath);
|
||||||
|
if (read(fd, destpath, sizeof(destpath)) == -1)
|
||||||
|
err(EXIT_FAILURE, "read(%s)", exepath);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
printf("The i3 binary you are running: %s\n", destpath);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
yajl_free(handle);
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "ewmh.c"
|
||||||
/*
|
/*
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue