Merge branch 'next'

This commit is contained in:
Michael Stapelberg 2012-09-19 18:12:41 +02:00
commit 9c3ece56d2
237 changed files with 7177 additions and 1840 deletions

30
.gitignore vendored
View File

@ -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

View File

@ -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
View File

@ -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

186
RELEASE-NOTES-4.3 Normal file
View File

@ -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 its 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 its unneeded (after urgency hints)
• i3-config-wizard: use the level 0 keysym whenever its 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 doesnt 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 wont 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, dont 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)
• Dont allow ConfigureRequests while in fullscreen (fixes a compatibility
issue with gnome-terminal and xfces terminal)
• Fix flickering with 1pixel border tabbed layouts
• Use _exit() instead of exit() when i3 utility programs cannot be executed
• Dont 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
View File

@ -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 dont 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 dont 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) doesnt 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 systems iconv over GNU libiconv # We need -idirafter instead of -I to prefer the systems 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) doesnt 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)
# Dont print command lines which are run # Dont 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

29
contrib/trivial-bar-script.sh Executable file
View File

@ -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 i3statuss 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

24
debian/control vendored
View File

@ -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).

2
debian/i3-wm.docs vendored
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

46
docs/docs.mk Normal file
View File

@ -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)

View File

@ -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.

90
docs/i3-pod2html Executable file
View File

@ -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 "Couldnt open $ARGV[0] for reading: $!\n";
open(my $out, '>', $ARGV[1]) or die "Couldnt 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);

View File

@ -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::

View File

@ -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
containers border style. containers 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)

View File

@ -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

View File

@ -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 i3s 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.

View File

@ -1,11 +1,11 @@
i3 Users Guide i3 Users 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:
Lets assume you have two terminals on a workspace (with horizontal Lets 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 dont switch layouts, and want a clean and simple config file, use If you dont 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 windows actual class, instance and details about the matching process and the windows 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 dont want to assign all instances of it permanently, you
can make use of i3s 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 i3s 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 containers nothing will happen. If you use a different orientation, the split containers
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 workspaces name it has. This is useful in case you are changing the workspaces 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

View File

@ -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;";

View File

@ -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

View File

@ -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 dont need yydebug for now, as we got decent error messages using /* We dont 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 doesnt make it ambiguous (think of a keyboard layout
* having '1' on two different keys, but '!' only on keycode 10),
* well 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);
} }

View File

@ -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}

View File

@ -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)))

View File

@ -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

View File

@ -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

View File

@ -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 (thats 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;

View File

@ -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

28
i3-input/i3-input.mk Normal file
View File

@ -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

View File

@ -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(

View File

@ -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',

View File

@ -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

28
i3-msg/i3-msg.mk Normal file
View File

@ -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

View File

@ -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') {

View File

@ -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

28
i3-nagbar/i3-nagbar.mk Normal file
View File

@ -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

View File

@ -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;
} }

View File

@ -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

View File

@ -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" {

View File

@ -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" {

View File

@ -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

View File

@ -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

28
i3bar/i3bar.mk Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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. Its 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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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 */

View File

@ -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

View File

@ -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 doesnt crash and the user gets an annoying message. */ * i3bar doesnt 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 dont 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 dont 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);
} }

View File

@ -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. Its 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;
}

View File

@ -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 {

View File

@ -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 ~
* *

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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 were looping through the name anyway, we can save one /* While were 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) {

View File

@ -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

View File

@ -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)

View File

@ -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'.
* *

View File

@ -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 containers border style. This is important * Use this function to get a containers 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

View File

@ -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.

View File

@ -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 dont 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

27
include/display_version.h Normal file
View File

@ -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 didnt 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

View File

@ -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.
* *

32
include/key_press.h Normal file
View File

@ -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

View File

@ -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 dont use the i3 log /* Since this file also gets included by utilities which dont 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

View File

@ -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

View File

@ -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);

View File

@ -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 outputs resolution, floating point calculation errors will lead to
* the scratchpad window moving when shown repeatedly.
*
*/
void scratchpad_fix_resolution(void);
#endif #endif

View File

@ -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 dont 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

View File

@ -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

View File

@ -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

View File

@ -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 were using the slow path, but only once. */ /* Make the user know were 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;
}
}

28
libi3/get_visualtype.c Normal file
View File

@ -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;
}

21
libi3/libi3.mk Normal file
View File

@ -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

View File

@ -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;
} }

143
libi3/string.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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.

View File

@ -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

View File

@ -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 dont complain about the order: If the user has any preference, he will Please dont 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.

View File

@ -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

View File

@ -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 Users 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.

32
man/man.mk Normal file
View File

@ -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

View File

@ -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

10
src/Makefile Normal file
View File

@ -0,0 +1,10 @@
all:
$(MAKE) -C .. i3
install:
$(MAKE) -C .. install-i3
clean:
$(MAKE) -C .. clean-i3
.PHONY: all install clean

View File

@ -1,3 +1,5 @@
#undef I3__FILE__
#define I3__FILE__ "assignments.c"
/* /*
* vim:ts=4:sw=4:expandtab * vim:ts=4:sw=4:expandtab
* *

View File

@ -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; }

View File

@ -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, dont 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
{ {

View File

@ -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;

View File

@ -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);

View File

@ -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
View File

@ -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 containers border style. This is important * Use this function to get a containers 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 dont 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 doesnt 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;
}

View File

@ -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 events 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 events 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);

View File

@ -1,3 +1,5 @@
#undef I3__FILE__
#define I3__FILE__ "debug.c"
/* /*
* vim:ts=4:sw=4:expandtab * vim:ts=4:sw=4:expandtab
* *

172
src/display_version.c Normal file
View File

@ -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 didnt 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 processs 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);
}

View File

@ -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