Merge branch 'next'
This commit is contained in:
commit
9c3ece56d2
|
@ -1,33 +1,25 @@
|
|||
*.o
|
||||
tags
|
||||
include/loglevels.h
|
||||
include/GENERATED_*.h
|
||||
loglevels.tmp
|
||||
include/all.h.pch
|
||||
*.swp
|
||||
*.gcda
|
||||
*.gcno
|
||||
testcases/testsuite-*
|
||||
testcases/latest
|
||||
testcases/Makefile
|
||||
testcases/Makefile.old
|
||||
testcases/.last_run_timings.json
|
||||
testcases/_Inline
|
||||
testcases/inc
|
||||
testcases/META.yml
|
||||
test.commands_parser
|
||||
*.output
|
||||
*.tab.*
|
||||
*.yy.c
|
||||
man/i3-msg.1
|
||||
man/i3-msg.xml
|
||||
man/i3-msg.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
|
||||
man/*.1
|
||||
man/*.xml
|
||||
man/*.html
|
||||
*.tar.bz2*
|
||||
i3
|
||||
i3-input/i3-input
|
||||
|
@ -35,5 +27,7 @@ i3-nagbar/i3-nagbar
|
|||
i3-msg/i3-msg
|
||||
i3-config-wizard/i3-config-wizard
|
||||
i3-dump-log/i3-dump-log
|
||||
libi3/libi3.a
|
||||
libi3.a
|
||||
docs/*.pdf
|
||||
docs/*.html
|
||||
!/docs/refcard.html
|
||||
|
|
6
DEPENDS
6
DEPENDS
|
@ -8,7 +8,6 @@
|
|||
│ dependency │ min. │ lkgv │ URL │
|
||||
├─────────────┼────────┼────────┼────────────────────────────────────────┤
|
||||
│ 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/ │
|
||||
│ xcb-util │ 0.3.3 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │
|
||||
│ 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/ │
|
||||
│ 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/ │
|
||||
│ 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/ │
|
||||
│ 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/ │
|
||||
│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │
|
||||
│ 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
|
||||
² 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
|
||||
new dependencies.
|
||||
|
|
147
Makefile
147
Makefile
|
@ -2,151 +2,58 @@ TOPDIR=$(shell pwd)
|
|||
|
||||
include $(TOPDIR)/common.mk
|
||||
|
||||
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||
AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c
|
||||
FILES:=$(filter-out $(AUTOGENERATED),$(wildcard src/*.c))
|
||||
FILES:=$(FILES:.c=.o)
|
||||
HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h))
|
||||
CMDPARSE_HEADERS:=include/GENERATED_call.h include/GENERATED_enums.h include/GENERATED_tokens.h
|
||||
SUBDIRS:=
|
||||
|
||||
# Recursively generate loglevels.h by explicitly calling make
|
||||
# We need this step because we need to ensure that loglevels.h will be
|
||||
# updated if necessary, but we also want to save rebuilds of the object
|
||||
# files, so we cannot let the object files depend on loglevels.h.
|
||||
ifeq ($(MAKECMDGOALS),loglevels.h)
|
||||
#UNUSED:=$(warning Generating loglevels.h)
|
||||
else
|
||||
UNUSED:=$(shell $(MAKE) loglevels.h)
|
||||
endif
|
||||
ALL_TARGETS =
|
||||
INSTALL_TARGETS =
|
||||
CLEAN_TARGETS =
|
||||
DISTCLEAN_TARGETS =
|
||||
|
||||
SUBDIRS:=i3-msg i3-input i3-nagbar i3-config-wizard i3bar i3-dump-log
|
||||
all: real-all
|
||||
|
||||
# Depend on the specific file (.c for each .o) and on all headers
|
||||
src/%.o: src/%.c ${HEADERS}
|
||||
echo "[i3] CC $<"
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -c -o $@ $<
|
||||
include libi3/libi3.mk
|
||||
include src/i3.mk
|
||||
include i3-config-wizard/i3-config-wizard.mk
|
||||
include i3-msg/i3-msg.mk
|
||||
include i3-input/i3-input.mk
|
||||
include i3-nagbar/i3-nagbar.mk
|
||||
include i3bar/i3bar.mk
|
||||
include i3-dump-log/i3-dump-log.mk
|
||||
include docs/docs.mk
|
||||
include man/man.mk
|
||||
|
||||
all: i3 subdirs
|
||||
real-all: $(ALL_TARGETS)
|
||||
|
||||
i3: libi3/libi3.a src/cfgparse.y.o src/cfgparse.yy.o ${FILES}
|
||||
echo "[i3] LINK i3"
|
||||
$(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
|
||||
|
||||
libi3/%.a: libi3/*.c
|
||||
$(MAKE) -C libi3
|
||||
|
||||
subdirs:
|
||||
for dir in $(SUBDIRS); do \
|
||||
echo ""; \
|
||||
echo "MAKE $$dir"; \
|
||||
$(MAKE) -C $$dir; \
|
||||
done
|
||||
|
||||
loglevels.h:
|
||||
echo "[i3] LOGLEVELS"
|
||||
for file in $$(ls src/*.c src/*.y src/*.l | grep -v 'cfgparse.\(tab\|yy\).c'); \
|
||||
do \
|
||||
echo $$(basename $$file .c); \
|
||||
done > loglevels.tmp
|
||||
(echo "char *loglevels[] = {"; for file in $$(cat loglevels.tmp); \
|
||||
do \
|
||||
echo "\"$$file\", "; \
|
||||
done; \
|
||||
echo "};") > include/loglevels.h;
|
||||
|
||||
# The GENERATED_* files are actually all created from a single pass, so all
|
||||
# files just depend on the first one.
|
||||
include/GENERATED_call.h: generate-command-parser.pl parser-specs/commands.spec
|
||||
echo "[i3] Generating command parser"
|
||||
(cd include; ../generate-command-parser.pl)
|
||||
include/GENERATED_enums.h: include/GENERATED_call.h
|
||||
include/GENERATED_tokens.h: include/GENERATED_call.h
|
||||
|
||||
# This target compiles the command parser twice:
|
||||
# Once with -DTEST_PARSER, creating a stand-alone executable used for tests,
|
||||
# and once as an object file for i3.
|
||||
src/commands_parser.o: src/commands_parser.c ${HEADERS} ${CMDPARSE_HEADERS}
|
||||
echo "[i3] CC $<"
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DTEST_PARSER -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -o test.commands_parser $< $(LIBS)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="((uint64_t)1 << $(shell awk '/$(shell basename $< .c)/ { print NR; exit 0; }' loglevels.tmp))" -c -o $@ $<
|
||||
|
||||
src/cfgparse.yy.o: src/cfgparse.l src/cfgparse.y.o ${HEADERS}
|
||||
echo "[i3] LEX $<"
|
||||
$(FLEX) -i -o$(@:.o=.c) $<
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.l/ { print NR }' loglevels.tmp))" -c -o $@ $(@:.o=.c)
|
||||
|
||||
|
||||
src/cfgparse.y.o: src/cfgparse.y ${HEADERS}
|
||||
echo "[i3] YACC $<"
|
||||
$(BISON) --debug --verbose -b $(basename $< .y) -d $<
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.y/ { print NR }' loglevels.tmp))" -c -o $@ $(<:.y=.tab.c)
|
||||
|
||||
|
||||
install: all
|
||||
echo "[i3] INSTALL"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(SYSCONFDIR)/i3
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/include/i3
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/xsessions
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/applications
|
||||
$(INSTALL) -m 0755 i3 $(DESTDIR)$(PREFIX)/bin/
|
||||
$(INSTALL) -m 0755 i3-migrate-config-to-v4 $(DESTDIR)$(PREFIX)/bin/
|
||||
$(INSTALL) -m 0755 i3-sensible-editor $(DESTDIR)$(PREFIX)/bin/
|
||||
$(INSTALL) -m 0755 i3-sensible-pager $(DESTDIR)$(PREFIX)/bin/
|
||||
$(INSTALL) -m 0755 i3-sensible-terminal $(DESTDIR)$(PREFIX)/bin/
|
||||
test -e $(DESTDIR)$(SYSCONFDIR)/i3/config || $(INSTALL) -m 0644 i3.config $(DESTDIR)$(SYSCONFDIR)/i3/config
|
||||
test -e $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes || $(INSTALL) -m 0644 i3.config.keycodes $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes
|
||||
$(INSTALL) -m 0644 i3.xsession.desktop $(DESTDIR)$(PREFIX)/share/xsessions/i3.desktop
|
||||
$(INSTALL) -m 0644 i3.applications.desktop $(DESTDIR)$(PREFIX)/share/applications/i3.desktop
|
||||
$(INSTALL) -m 0644 include/i3/ipc.h $(DESTDIR)$(PREFIX)/include/i3/
|
||||
for dir in $(SUBDIRS); do \
|
||||
$(MAKE) -C $$dir install; \
|
||||
done
|
||||
install: $(INSTALL_TARGETS)
|
||||
|
||||
dist: distclean
|
||||
[ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION}
|
||||
[ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2
|
||||
mkdir i3-${VERSION}
|
||||
cp i3-migrate-config-to-v4 generate-command-parser.pl i3-sensible-* i3.config.keycodes DEPENDS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.xsession.desktop i3.applications.desktop pseudo-doc.doxygen Makefile i3-${VERSION}
|
||||
cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man parser-specs 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 testcases i3-${VERSION}
|
||||
# Only copy toplevel documentation (important stuff)
|
||||
mkdir i3-${VERSION}/docs
|
||||
# Pre-generate documentation
|
||||
$(MAKE) -C docs
|
||||
$(MAKE) -C i3bar/doc
|
||||
$(MAKE) docs
|
||||
# Cleanup τεχ output files
|
||||
find docs -regex ".*\.\(aux\|out\|log\|toc\|bm\|dvi\|log\)" -exec rm '{}' \;
|
||||
find docs -maxdepth 1 -type f ! \( -name "*.xcf" -or -name "*.svg" \) -exec cp '{}' i3-${VERSION}/docs \;
|
||||
# Only copy source code from i3-input
|
||||
mkdir i3-${VERSION}/i3-input
|
||||
find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \;
|
||||
sed -e 's/^GIT_VERSION:=\(.*\)/GIT_VERSION:=$(shell /bin/echo '${GIT_VERSION}' | sed 's/\\/\\\\/g')/g;s/^VERSION:=\(.*\)/VERSION:=${VERSION}/g' common.mk > i3-${VERSION}/common.mk
|
||||
find i3-input -maxdepth 1 -type f \( -name "*.c" -or -name "*.mk" -or -name "*.h" -or -name "Makefile" \) -exec cp '{}' i3-${VERSION}/i3-input \;
|
||||
echo -n ${I3_VERSION} > i3-${VERSION}/I3_VERSION
|
||||
echo -n ${VERSION} > i3-${VERSION}/VERSION
|
||||
# Pre-generate a manpage to allow distributors to skip this step and save some dependencies
|
||||
$(MAKE) -C man
|
||||
$(MAKE) mans
|
||||
cp man/*.1 i3-${VERSION}/man/
|
||||
cp i3bar/doc/*.1 i3-${VERSION}/i3bar/doc/
|
||||
tar cfj i3-${VERSION}.tar.bz2 i3-${VERSION}
|
||||
rm -rf i3-${VERSION}
|
||||
|
||||
clean:
|
||||
rm -f src/*.o src/*.gcno src/cmdparse.* src/cfgparse.tab.{c,h} src/cfgparse.yy.c src/cfgparse.{output,dot} loglevels.tmp include/loglevels.h include/GENERATED_*
|
||||
clean: $(CLEAN_TARGETS)
|
||||
(which lcov >/dev/null 2>&1 && lcov -d . --zerocounters) || true
|
||||
$(MAKE) -C libi3 clean
|
||||
$(MAKE) -C docs clean
|
||||
$(MAKE) -C man clean
|
||||
for dir in $(SUBDIRS); do \
|
||||
echo ""; \
|
||||
echo "CLEAN $$dir"; \
|
||||
$(MAKE) TOPDIR=$(TOPDIR) -C $$dir distclean; \
|
||||
done
|
||||
|
||||
distclean: clean
|
||||
rm -f i3
|
||||
for dir in $(SUBDIRS); do \
|
||||
echo ""; \
|
||||
echo "DISTCLEAN $$dir"; \
|
||||
$(MAKE) TOPDIR=$(TOPDIR) -C $$dir distclean; \
|
||||
done
|
||||
distclean: clean $(DISTCLEAN_TARGETS)
|
||||
|
||||
coverage:
|
||||
rm -f /tmp/i3-coverage.info
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
|
||||
┌──────────────────────────────┐
|
||||
│ Release notes for i3 v4.3 │
|
||||
└──────────────────────────────┘
|
||||
|
||||
This is the i3 v4.3. This version is considered stable. All users of i3 are
|
||||
strongly encouraged to upgrade.
|
||||
|
||||
One rather visible change is that commands which could not be parsed properly
|
||||
will now spawn i3-nagbar. In case you used "bindsym $mod+x firefox" (and
|
||||
forgot the "exec" keyword) or you made a typo in your config, you will now
|
||||
notice that :).
|
||||
|
||||
|
||||
We also made the orientation (horizontal/vertical) part of the layout
|
||||
mechanism: Before, we got the default layout and you could change
|
||||
orientation. Now, there are two new layouts "splitv" and "splith", which
|
||||
replace the default layout. The "split h" and "split v" commands continue to
|
||||
work as before, they split the current container and you will end up in a
|
||||
split container with layout splith (after "split h") or splitv (after
|
||||
"split v").
|
||||
|
||||
To change a splith container into a splitv container, use either "layout
|
||||
splitv" or "layout toggle split". The latter command is used in the
|
||||
default config as mod+l (formerly "layout default"). In case you have
|
||||
"layout default" in your config file, it is recommended to just replace
|
||||
it by "layout toggle split", which will work as "layout default" did
|
||||
before when pressing it once, but toggle between horizontal/vertical
|
||||
when pressing it repeatedly.
|
||||
|
||||
The rationale behind this change is that it’s cleaner to have all
|
||||
parameters that influence how windows are rendered in the layout itself
|
||||
rather than having one special layout in combination with an additional
|
||||
orientation. This enables us to change existing split containers in all
|
||||
cases without breaking existing features (see ticket #464). Also, users
|
||||
should feel more confident about whether they are actually splitting or
|
||||
just changing an existing split container now.
|
||||
|
||||
As a nice side-effect, this commit brings back the "layout toggle"
|
||||
feature we once had in i3 v3 (see the userguide).
|
||||
|
||||
|
||||
Another very important change is that we now support pango for rendering text.
|
||||
The default is still to use misc-fixed (X core fonts), but you can use a font
|
||||
specification starting with "xft:" now, such as "xft:DejaVu Sans Mono 10" and
|
||||
i3 will use pango. The sole motivation for this is NOT to have fancier window
|
||||
decorations, but to support fonts which have more glyphs (think Japanese for
|
||||
example) and to support right-to-left rendering (open http://www.ftpal.net/
|
||||
for an example). Supporting users from all over the planet is important, and
|
||||
as such I would strongly advise distribution packagers to leave pango support
|
||||
enabled. In case you are working on a very low-spec embedded device, it is
|
||||
easy enough to disable pango support, see common.mk.
|
||||
|
||||
|
||||
Also, the 'layout' command now always works on the parent split container. This
|
||||
allows you to do things like this:
|
||||
|
||||
for_window [class="XTerm"] layout tabbed
|
||||
|
||||
When you now open XTerm on an empty workspace, the whole workspace will be
|
||||
set to tabbed. In case you want to open XTerm in its own tabbed split
|
||||
container, you need to split before:
|
||||
|
||||
for_window [class="XTerm"] split v, layout tabbed
|
||||
|
||||
|
||||
Furthermore, we decided to entirely ignore resize increment size hints for
|
||||
tiling windows. These are set by terminal emulators (such as urxvt,
|
||||
gnome-terminal, …) and specify that the window may only be resized in
|
||||
multiples of the specified size. All terminal emulators cope with the window
|
||||
manager ignoring these hints and by doing so, they can decide what to do with
|
||||
the lost space (that is, pseudo-transparency now works without black bars in
|
||||
urxvt).
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Changes in v4.3 │
|
||||
└────────────────────────────┘
|
||||
|
||||
• docs: there now is documentation about lib::i3test and lib::i3test::Test,
|
||||
the main Perl modules used by our testsuite.
|
||||
• docs/refcard: update for v4
|
||||
• docs/userguide: clarify the default for focus_follows_mouse and new_window
|
||||
• docs/userguide: add section about implicit containers
|
||||
• docs/userguide: give 'move <container|workspace> to output' its own section
|
||||
• docs/ipc: document the 'window' field in the GET_TREE reply
|
||||
• docs/ipc: update links to ipc libraries
|
||||
• docs/ipc: make the reply sections consistent
|
||||
• docs/i3bar-protocol: add example (illustration-only!) shell script
|
||||
• man/i3bar.man: reference i3bar-protocol
|
||||
• IPC: Commands now lead to proper error messages in general. If we forgot
|
||||
about a specific one, please open a ticket.
|
||||
• IPC: implement GET_VERSION to find out the i3 version
|
||||
• i3-dump-log now comes with a massively more helpful error message that
|
||||
should cover all the use cases.
|
||||
• 'workspace number <number>' now opens a new workspace
|
||||
• 'workspace number <number>' now works with the back_and_forth option
|
||||
• Allow focus with target (criteria) when in fullscreen mode in some cases
|
||||
• Allow focus child/parent when in fullscreen mode
|
||||
• Restrict directional focus when in fullscreen mode
|
||||
• Prevent moving out of fullscreen containers
|
||||
• Add 'move to workspace current' (useful when used with criteria)
|
||||
• replace loglevels by a global debug logging
|
||||
• make: new makefile layout
|
||||
• make: canonicalize path when compiling. This leads to sth like
|
||||
../i3-4.2/src/main.c in backtraces, clearly identifying i3 code.
|
||||
• automatically hide i3bar when it’s unneeded (after urgency hints)
|
||||
• i3-config-wizard: use the level 0 keysym whenever it’s unambiguous
|
||||
• i3-nagbar: use custom scripts to work around different terminal emulators
|
||||
using different ways of interpreting the arguments to -e
|
||||
• i3-sensible-terminal: add xfce4-terminal
|
||||
• default config: require confirmation when exiting i3
|
||||
• Display i3-nagbar when a command leads to an error.
|
||||
• testcases: complete-run now supports --xtrace
|
||||
• testcases: handle EAGAIN (fixes hangs)
|
||||
• testcases: handle test bailouts
|
||||
• Introduce splith/splitv layouts, remove orientation
|
||||
• Implement hide_edge_borders option
|
||||
• Support _NET_ACTIVE_WINDOW ClientMessages
|
||||
• Set I3_PID atom on the X11 root window
|
||||
• Implement i3 --moreversion, handy for figuring out whether you run the
|
||||
latest binary which is installed.
|
||||
• i3bar: be less strict about the {"version":1} JSON header
|
||||
• shm-logging: implement i3-dump-log -f (follow)
|
||||
• Implement pango support
|
||||
• 'move workspace number n' will now create the workspace if it doesn’t exist
|
||||
• Accept slashes in RandR output names
|
||||
• Keep startup-notification sequences around for 30s after completion
|
||||
• Introduce bindsym --release, which will trigger the binding not on the
|
||||
KeyPress event, but on the KeyRelease event (useful for import(1) or
|
||||
xdotool(1)).
|
||||
• The signalhandler does not offer you to exit i3 anymore. Instead, there is
|
||||
'b' for writing a backtrace to a file in /tmp (if gdb is installed)
|
||||
• Remove support for resize increment hints for tiling windows
|
||||
• Exit fullscreen mode when 'scratchpad show' is executed while in fullscreen
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Bugfixes │
|
||||
└────────────────────────────┘
|
||||
|
||||
• Fix floating precision bug when floating windows are moved between outputs.
|
||||
• i3bar won’t crash when full_text is missing or null in the JSON input
|
||||
• When having "workspace number 1" in your config, there will no longer be a
|
||||
stray workspace "number 1".
|
||||
• i3.config.keycodes used bindsym instead of bindcode for the arrow key
|
||||
resizing bindings by mistake
|
||||
• Fix 'move to workspace' when used with criteria
|
||||
• Handle clicks to the very left edge of i3bar
|
||||
• When using i3 -C, don’t send remaining arguments as an IPC command
|
||||
• Fix reload crashes in rare cases
|
||||
• i3bar: inform all clients of new tray selection owner (fixes tray problems
|
||||
with X-Chat and possibly others)
|
||||
• resizing: traverse containers up properly (fixes non-working resizing when
|
||||
having a h-split within a h-split for example)
|
||||
• Fix floating coordinates when moving assigned workspaces
|
||||
• Properly fix floating coordinates when disabling outputs
|
||||
• floating_fix_coordinates: properly deal with negative positions
|
||||
• floating windows: add deco_height only when in normal border mode (fixes
|
||||
initial floating window position/size when using a different default border
|
||||
setting).
|
||||
• Fix moving scratchpad window
|
||||
• Cleanup zero-byte logfile on immediate exit (they are created by i3
|
||||
--get-socketpath for example).
|
||||
• Fix resizing floating windows by height
|
||||
• Fix back_and_forth in 'workspace number' for named workspaces
|
||||
• Grab server and process pending events before managing existing windows
|
||||
(fixes problems with GIMP windows not being managed after an in-place
|
||||
restart)
|
||||
• Don’t allow ConfigureRequests while in fullscreen (fixes a compatibility
|
||||
issue with gnome-terminal and xfce’s terminal)
|
||||
• Fix flickering with 1pixel border tabbed layouts
|
||||
• Use _exit() instead of exit() when i3 utility programs cannot be executed
|
||||
• Don’t focus the wrong workspace when moving to scratchpad
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Thanks! │
|
||||
└────────────────────────────┘
|
||||
|
||||
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
|
||||
|
||||
aksr, Axel Wagner, darkraven, David Coppa, eeemsi, Felicitus, Fernando Tarlá
|
||||
Cardoso Lemos, Iakov Davydov, jh, Joel Stemmer, Julius Plenz, loblik, Marcel
|
||||
Hellwig, Marcus, mloskot, Moritz Bandemer, oblique, Ondrej Grover, Pavel
|
||||
Löbl, Philipp Middendorf, prg, Quentin Glidic, Sebastian Ullrich, Simon
|
||||
Elsbrock, somelauw, stfn, tucos, TunnelWicht, Valentin Haenel
|
||||
|
||||
-- Michael Stapelberg, 2012-09-19
|
197
common.mk
197
common.mk
|
@ -14,9 +14,52 @@ ifndef SYSCONFDIR
|
|||
SYSCONFDIR=$(PREFIX)/etc
|
||||
endif
|
||||
endif
|
||||
# The escaping is absurd, but we need to escape for shell, sed, make, define
|
||||
GIT_VERSION:="$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch $(shell [ -f $(TOPDIR)/.git/HEAD ] && sed 's/ref: refs\/heads\/\(.*\)/\\\\\\"\1\\\\\\"/g' $(TOPDIR)/.git/HEAD || echo 'unknown'))"
|
||||
VERSION:=$(shell git describe --tags --abbrev=0)
|
||||
|
||||
# In dist tarballs, the version is stored in the I3_VERSION and VERSION files.
|
||||
I3_VERSION := '$(shell [ -f $(TOPDIR)/I3_VERSION ] && cat $(TOPDIR)/I3_VERSION)'
|
||||
VERSION := '$(shell [ -f $(TOPDIR)/VERSION ] && cat $(TOPDIR)/VERSION)'
|
||||
ifeq ('',$(I3_VERSION))
|
||||
VERSION := $(shell git describe --tags --abbrev=0)
|
||||
I3_VERSION := '$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch \"$(shell git describe --tags --always --all | sed s:heads/::)\")'
|
||||
endif
|
||||
|
||||
MAJOR_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 1)
|
||||
MINOR_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 2)
|
||||
PATCH_VERSION := $(shell echo ${VERSION} | cut -d '.' -f 3)
|
||||
ifeq (${PATCH_VERSION},)
|
||||
PATCH_VERSION := 0
|
||||
endif
|
||||
|
||||
## Generic flags
|
||||
|
||||
# Default CFLAGS that users should be able to override
|
||||
ifeq ($(DEBUG),1)
|
||||
# Extended debugging flags, macros shall be available in gcc
|
||||
CFLAGS ?= -pipe -gdwarf-2 -g3
|
||||
else
|
||||
CFLAGS ?= -pipe -O2 -freorder-blocks-and-partition
|
||||
endif
|
||||
|
||||
# Default LDFLAGS that users should be able to override
|
||||
LDFLAGS ?= $(as_needed_LDFLAG)
|
||||
|
||||
# Common CFLAGS for all i3 related binaries
|
||||
I3_CFLAGS = -std=c99
|
||||
I3_CFLAGS += -Wall
|
||||
# unused-function, unused-label, unused-variable are turned on by -Wall
|
||||
# We don’t want unused-parameter because of the use of many callbacks
|
||||
I3_CFLAGS += -Wunused-value
|
||||
I3_CFLAGS += -Iinclude
|
||||
|
||||
I3_CPPFLAGS = -DI3_VERSION=\"${I3_VERSION}\"
|
||||
I3_CPPFLAGS += -DMAJOR_VERSION=${MAJOR_VERSION}
|
||||
I3_CPPFLAGS += -DMINOR_VERSION=${MINOR_VERSION}
|
||||
I3_CPPFLAGS += -DPATCH_VERSION=${PATCH_VERSION}
|
||||
I3_CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\"
|
||||
I3_CPPFLAGS += -DI3__FILE__=__FILE__
|
||||
|
||||
|
||||
## Libraries flags
|
||||
|
||||
ifeq ($(shell which pkg-config 2>/dev/null 1>/dev/null || echo 1),1)
|
||||
$(error "pkg-config was not found")
|
||||
|
@ -35,79 +78,92 @@ endif
|
|||
cflags_for_lib = $(shell pkg-config --silence-errors --cflags $(1) 2>/dev/null)
|
||||
ldflags_for_lib = $(shell pkg-config --exists 2>/dev/null $(1) && pkg-config --libs $(1) 2>/dev/null || echo -l$(2))
|
||||
|
||||
CFLAGS += -std=c99
|
||||
CFLAGS += -pipe
|
||||
CFLAGS += -Wall
|
||||
# unused-function, unused-label, unused-variable are turned on by -Wall
|
||||
# We don’t want unused-parameter because of the use of many callbacks
|
||||
CFLAGS += -Wunused-value
|
||||
CFLAGS += -Iinclude
|
||||
CFLAGS += $(call cflags_for_lib, xcb-keysyms)
|
||||
# XCB common stuff
|
||||
XCB_CFLAGS := $(call cflags_for_lib, xcb)
|
||||
XCB_CFLAGS += $(call cflags_for_lib, xcb-event)
|
||||
XCB_LIBS := $(call ldflags_for_lib, xcb,xcb)
|
||||
XCB_LIBS += $(call ldflags_for_lib, xcb-event,xcb-event)
|
||||
ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1)
|
||||
CPPFLAGS += -DXCB_COMPAT
|
||||
CFLAGS += $(call cflags_for_lib, xcb-atom)
|
||||
CFLAGS += $(call cflags_for_lib, xcb-aux)
|
||||
XCB_CFLAGS += $(call cflags_for_lib, xcb-atom)
|
||||
XCB_CFLAGS += $(call cflags_for_lib, xcb-aux)
|
||||
XCB_LIBS += $(call ldflags_for_lib, xcb-atom,xcb-atom)
|
||||
XCB_LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux)
|
||||
XCB_CPPFLAGS+= -DXCB_COMPAT
|
||||
else
|
||||
CFLAGS += $(call cflags_for_lib, xcb-util)
|
||||
XCB_CFLAGS += $(call cflags_for_lib, xcb-util)
|
||||
XCB_LIBS += $(call ldflags_for_lib, xcb-util)
|
||||
endif
|
||||
CFLAGS += $(call cflags_for_lib, xcb-icccm)
|
||||
CFLAGS += $(call cflags_for_lib, xcb-xinerama)
|
||||
CFLAGS += $(call cflags_for_lib, xcb-randr)
|
||||
CFLAGS += $(call cflags_for_lib, xcb)
|
||||
CFLAGS += $(call cflags_for_lib, xcursor)
|
||||
CFLAGS += $(call cflags_for_lib, x11)
|
||||
CFLAGS += $(call cflags_for_lib, yajl)
|
||||
CFLAGS += $(call cflags_for_lib, libev)
|
||||
CFLAGS += $(call cflags_for_lib, libpcre)
|
||||
CFLAGS += $(call cflags_for_lib, libstartup-notification-1.0)
|
||||
CPPFLAGS += -DI3_VERSION=\"${GIT_VERSION}\"
|
||||
CPPFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\"
|
||||
|
||||
# XCB keyboard stuff
|
||||
XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms)
|
||||
XCB_KBD_LIBS := $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms)
|
||||
|
||||
# XCB WM stuff
|
||||
XCB_WM_CFLAGS := $(call cflags_for_lib, xcb-icccm)
|
||||
XCB_WM_CFLAGS += $(call cflags_for_lib, xcb-xinerama)
|
||||
XCB_WM_CFLAGS += $(call cflags_for_lib, xcb-randr)
|
||||
XCB_WM_LIBS := $(call ldflags_for_lib, xcb-icccm,xcb-icccm)
|
||||
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama)
|
||||
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
|
||||
|
||||
# Xlib
|
||||
X11_CFLAGS := $(call cflags_for_lib, x11)
|
||||
X11_LIBS := $(call ldflags_for_lib, x11,X11)
|
||||
|
||||
# Xcursor
|
||||
XCURSOR_CFLAGS := $(call cflags_for_lib, xcursor)
|
||||
XCURSOR_LIBS := $(call ldflags_for_lib, xcursor,Xcursor)
|
||||
|
||||
# yajl
|
||||
YAJL_CFLAGS := $(call cflags_for_lib, yajl)
|
||||
# Fallback for libyajl 1 which did not include yajl_version.h. We need
|
||||
# YAJL_MAJOR from that file to decide which code path should be used.
|
||||
YAJL_CFLAGS += -idirafter $(TOPDIR)/yajl-fallback
|
||||
YAJL_LIBS := $(call ldflags_for_lib, yajl,yajl)
|
||||
|
||||
#libev
|
||||
LIBEV_CFLAGS := $(call cflags_for_lib, libev)
|
||||
LIBEV_LIBS := $(call ldflags_for_lib, libev,ev)
|
||||
|
||||
# libpcre
|
||||
PCRE_CFLAGS := $(call cflags_for_lib, libpcre)
|
||||
ifeq ($(shell pkg-config --atleast-version=8.10 libpcre 2>/dev/null && echo 1),1)
|
||||
CPPFLAGS += -DPCRE_HAS_UCP=1
|
||||
I3_CPPFLAGS += -DPCRE_HAS_UCP=1
|
||||
endif
|
||||
PCRE_LIBS := $(call ldflags_for_lib, libpcre,pcre)
|
||||
|
||||
LIBS += -lm
|
||||
# Darwin (Mac OS X) doesn’t have librt
|
||||
ifneq ($(UNAME),Darwin)
|
||||
LIBS += -lrt
|
||||
endif
|
||||
LIBS += -L $(TOPDIR)/libi3 -li3
|
||||
LIBS += $(call ldflags_for_lib, xcb-event,xcb-event)
|
||||
LIBS += $(call ldflags_for_lib, xcb-keysyms,xcb-keysyms)
|
||||
ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1)
|
||||
LIBS += $(call ldflags_for_lib, xcb-atom,xcb-atom)
|
||||
LIBS += $(call ldflags_for_lib, xcb-aux,xcb-aux)
|
||||
else
|
||||
LIBS += $(call ldflags_for_lib, xcb-util)
|
||||
endif
|
||||
LIBS += $(call ldflags_for_lib, xcb-icccm,xcb-icccm)
|
||||
LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama)
|
||||
LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
|
||||
LIBS += $(call ldflags_for_lib, xcb,xcb)
|
||||
LIBS += $(call ldflags_for_lib, xcursor,Xcursor)
|
||||
LIBS += $(call ldflags_for_lib, x11,X11)
|
||||
LIBS += $(call ldflags_for_lib, yajl,yajl)
|
||||
LIBS += $(call ldflags_for_lib, libev,ev)
|
||||
LIBS += $(call ldflags_for_lib, libpcre,pcre)
|
||||
LIBS += $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1)
|
||||
# startup-notification
|
||||
LIBSN_CFLAGS := $(call cflags_for_lib, libstartup-notification-1.0)
|
||||
LIBSN_LIBS := $(call ldflags_for_lib, libstartup-notification-1.0,startup-notification-1)
|
||||
|
||||
# Pango
|
||||
PANGO_CFLAGS := $(call cflags_for_lib, cairo)
|
||||
PANGO_CFLAGS += $(call cflags_for_lib, pangocairo)
|
||||
I3_CPPFLAGS += -DPANGO_SUPPORT=1
|
||||
PANGO_LIBS := $(call ldflags_for_lib, cairo)
|
||||
PANGO_LIBS += $(call ldflags_for_lib, pangocairo)
|
||||
|
||||
# libi3
|
||||
LIBS = -L$(TOPDIR) -li3
|
||||
|
||||
## Platform-specific flags
|
||||
|
||||
# Please test if -Wl,--as-needed works on your platform and send me a patch.
|
||||
# it is known not to work on Darwin (Mac OS X)
|
||||
ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
|
||||
LDFLAGS += -Wl,--as-needed
|
||||
as_needed_LDFLAG = -Wl,--as-needed
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME),NetBSD)
|
||||
# We need -idirafter instead of -I to prefer the system’s iconv over GNU libiconv
|
||||
CFLAGS += -idirafter /usr/pkg/include
|
||||
LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib
|
||||
I3_CFLAGS += -idirafter /usr/pkg/include
|
||||
I3_LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME),OpenBSD)
|
||||
CFLAGS += -I${X11BASE}/include
|
||||
I3_CFLAGS += -I${X11BASE}/include
|
||||
LIBS += -liconv
|
||||
LDFLAGS += -L${X11BASE}/lib
|
||||
I3_LDFLAGS += -L${X11BASE}/lib
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME),FreeBSD)
|
||||
|
@ -116,33 +172,32 @@ endif
|
|||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
LIBS += -liconv
|
||||
else
|
||||
# Darwin (Mac OS X) doesn’t have librt
|
||||
LIBS += -lrt
|
||||
endif
|
||||
|
||||
# Fallback for libyajl 1 which did not include yajl_version.h. We need
|
||||
# YAJL_MAJOR from that file to decide which code path should be used.
|
||||
CFLAGS += -idirafter $(TOPDIR)/yajl-fallback
|
||||
|
||||
ifneq (,$(filter Linux GNU GNU/%, $(UNAME)))
|
||||
CPPFLAGS += -D_GNU_SOURCE
|
||||
I3_CPPFLAGS += -D_GNU_SOURCE
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
# Extended debugging flags, macros shall be available in gcc
|
||||
CFLAGS += -gdwarf-2
|
||||
CFLAGS += -g3
|
||||
else
|
||||
CFLAGS += -O2
|
||||
CFLAGS += -freorder-blocks-and-partition
|
||||
endif
|
||||
|
||||
ifeq ($(COVERAGE),1)
|
||||
CFLAGS += -fprofile-arcs -ftest-coverage
|
||||
I3_CFLAGS += -fprofile-arcs -ftest-coverage
|
||||
LIBS += -lgcov
|
||||
endif
|
||||
|
||||
V ?= 0
|
||||
ifeq ($(V),0)
|
||||
# Don’t print command lines which are run
|
||||
.SILENT:
|
||||
|
||||
# echo-ing vars
|
||||
V_ASCIIDOC = echo ASCIIDOC $@;
|
||||
V_POD2HTML = echo POD2HTML $@;
|
||||
V_A2X = echo A2X $@;
|
||||
endif
|
||||
|
||||
# Always remake the following targets
|
||||
.PHONY: install clean dist distclean
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/sh
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
# © 2012 Michael Stapelberg, Public Domain
|
||||
|
||||
# This script is a trivial shell script to send your own output to i3bar while
|
||||
# using the JSON protocol.
|
||||
#
|
||||
# It is ugly and that is inherent to using JSON with shell scripts. You
|
||||
# _really_ should not do that. See i3status or i3status’s contrib/ directory
|
||||
# for examples of how to handle the output in higher-level languages.
|
||||
#
|
||||
# This example is purely for illustration of the protocol. DO NOT USE IT IN THE
|
||||
# REAL WORLD.
|
||||
|
||||
# Send the header so that i3bar knows we want to use JSON:
|
||||
echo '{ "version": 1 }'
|
||||
|
||||
# Begin the endless array.
|
||||
echo '['
|
||||
|
||||
# We send an empty first array of blocks to make the loop simpler:
|
||||
echo '[]'
|
||||
|
||||
# Now send blocks with information forever:
|
||||
while :;
|
||||
do
|
||||
echo ",[{\"name\":\"time\",\"full_text\":\"$(date)\"}]"
|
||||
sleep 1
|
||||
done
|
|
@ -2,14 +2,34 @@ Source: i3-wm
|
|||
Section: x11
|
||||
Priority: extra
|
||||
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
|
||||
Homepage: http://i3wm.org/
|
||||
|
||||
Package: i3
|
||||
Architecture: any
|
||||
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)
|
||||
This metapackage installs the i3 window manager (i3-wm), the i3lock screen
|
||||
locker, i3status (for system information) and suckless-tools (for dmenu).
|
||||
|
|
|
@ -28,3 +28,5 @@ docs/tree-shot4.png
|
|||
docs/refcard.html
|
||||
docs/refcard_style.css
|
||||
docs/logo-30.png
|
||||
docs/lib-i3test.html
|
||||
docs/lib-i3test-test.html
|
||||
|
|
|
@ -8,4 +8,4 @@ man/i3-migrate-config-to-v4.1
|
|||
man/i3-sensible-pager.1
|
||||
man/i3-sensible-editor.1
|
||||
man/i3-sensible-terminal.1
|
||||
i3bar/doc/i3bar.1
|
||||
man/i3bar.1
|
||||
|
|
|
@ -1,36 +1,7 @@
|
|||
# To pass additional parameters for asciidoc
|
||||
ASCIIDOC=asciidoc
|
||||
|
||||
ASCIIDOC_TARGETS:=hacking-howto.html debugging.html debugging-release-version.html userguide.html ipc.html multi-monitor.html wsbar.html testsuite.html i3bar-protocol.html
|
||||
|
||||
all: ${ASCIIDOC_TARGETS}
|
||||
|
||||
hacking-howto.html: hacking-howto
|
||||
$(ASCIIDOC) -a toc -n $<
|
||||
|
||||
i3bar-protocol.html: i3bar-protocol
|
||||
$(ASCIIDOC) -a toc -n $<
|
||||
|
||||
debugging.html: debugging
|
||||
$(ASCIIDOC) -n $<
|
||||
|
||||
debugging-release-version.html: debugging-release-version
|
||||
$(ASCIIDOC) -n $<
|
||||
|
||||
userguide.html: userguide
|
||||
$(ASCIIDOC) -a toc -n $<
|
||||
|
||||
testsuite.html: testsuite
|
||||
$(ASCIIDOC) -a toc -n $<
|
||||
|
||||
ipc.html: ipc
|
||||
$(ASCIIDOC) -a toc -n $<
|
||||
|
||||
multi-monitor.html: multi-monitor
|
||||
$(ASCIIDOC) -a toc -n $<
|
||||
|
||||
wsbar.html: wsbar
|
||||
$(ASCIIDOC) -a toc -n $<
|
||||
all:
|
||||
$(MAKE) -C .. docs
|
||||
|
||||
clean:
|
||||
rm -f ${ASCIIDOC_TARGETS}
|
||||
$(MAKE) -C .. clean-docs
|
||||
|
||||
.PHONY: all clean
|
||||
|
|
|
@ -647,7 +647,7 @@ endif::doctype-manpage[]
|
|||
</div>
|
||||
{disable-javascript%<div id="footnotes"><hr /></div>}
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
DISTCLEAN_TARGETS += clean-docs
|
||||
|
||||
# To pass additional parameters for asciidoc
|
||||
ASCIIDOC = asciidoc
|
||||
I3POD2HTML = ./docs/i3-pod2html
|
||||
|
||||
ASCIIDOC_NOTOC_TARGETS = \
|
||||
docs/debugging.html \
|
||||
docs/debugging-release-version.html
|
||||
|
||||
ASCIIDOC_TOC_TARGETS = \
|
||||
docs/hacking-howto.html \
|
||||
docs/userguide.html \
|
||||
docs/ipc.html \
|
||||
docs/multi-monitor.html \
|
||||
docs/wsbar.html \
|
||||
docs/testsuite.html \
|
||||
docs/i3bar-protocol.html
|
||||
|
||||
ASCIIDOC_TARGETS = \
|
||||
$(ASCIIDOC_TOC_TARGETS) \
|
||||
$(ASCIIDOC_NOTOC_TARGETS)
|
||||
|
||||
ASCIIDOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -n $(ASCIIDOC_FLAGS) -o $@ $<
|
||||
ASCIIDOC_TOC_CALL = $(V_ASCIIDOC)$(ASCIIDOC) -a toc -n $(ASCIIDOC_FLAGS) -o $@ $<
|
||||
|
||||
POD2HTML_TARGETS = \
|
||||
docs/lib-i3test.html \
|
||||
docs/lib-i3test-test.html
|
||||
|
||||
docs/lib-i3test.html: testcases/lib/i3test.pm
|
||||
$(V_POD2HTML)$(I3POD2HTML) $< $@
|
||||
|
||||
docs/lib-i3test-test.html: testcases/lib/i3test/Test.pm
|
||||
$(V_POD2HTML)$(I3POD2HTML) $< $@
|
||||
|
||||
docs: $(ASCIIDOC_TARGETS) $(POD2HTML_TARGETS)
|
||||
|
||||
$(ASCIIDOC_TOC_TARGETS): docs/%.html: docs/%
|
||||
$(ASCIIDOC_TOC_CALL)
|
||||
|
||||
$(ASCIIDOC_NOTOC_TARGETS): docs/%.html: docs/%
|
||||
$(ASCIIDOC_CALL)
|
||||
|
||||
clean-docs:
|
||||
rm -f $(ASCIIDOC_TARGETS) $(POD2HTML_TARGETS)
|
|
@ -77,7 +77,7 @@ workspace, the split container we are talking about is the workspace.
|
|||
|
||||
To get an impression of how different layouts are represented, just play around
|
||||
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.
|
||||
|
||||
== Files
|
||||
|
@ -141,7 +141,7 @@ src/load_layout.c::
|
|||
Contains code for loading layouts from JSON files.
|
||||
|
||||
src/log.c::
|
||||
Handles the setting of loglevels, contains the logging functions.
|
||||
Contains the logging functions.
|
||||
|
||||
src/main.c::
|
||||
Initializes the window manager.
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env perl
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Pod::Simple::HTML;
|
||||
use v5.10;
|
||||
|
||||
$Pod::Simple::HTML::Tagmap{'Verbatim'} = '<pre><tt>';
|
||||
$Pod::Simple::HTML::Tagmap{'VerbatimFormatted'} = '<pre><tt>';
|
||||
$Pod::Simple::HTML::Tagmap{'/Verbatim'} = '</tt></pre>';
|
||||
$Pod::Simple::HTML::Tagmap{'/VerbatimFormatted'} = '</tt></pre>';
|
||||
|
||||
if (@ARGV < 2) {
|
||||
say STDERR "Syntax: i3-pod2html <pod-input-path> <html-output-path>";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
open(my $in, '<', $ARGV[0]) or die "Couldn’t open $ARGV[0] for reading: $!\n";
|
||||
open(my $out, '>', $ARGV[1]) or die "Couldn’t open $ARGV[1] for writing: $!\n";
|
||||
|
||||
my $parser = Pod::Simple::HTML->new();
|
||||
|
||||
$parser->index(1);
|
||||
$parser->html_header_before_title(
|
||||
<<'EOF'
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="Pod::Simple::HTML">
|
||||
<meta name="description" content="i3 Perl documentation (testsuite)">
|
||||
<link rel="stylesheet" href="http://i3wm.org/css/style.css" type="text/css" />
|
||||
<style type="text/css">
|
||||
.pod pre {
|
||||
background: #333;
|
||||
border: 1px solid #555;
|
||||
border-left: 5px solid #555;
|
||||
padding: 0.5em;
|
||||
padding-left: 0;
|
||||
padding-right: 0.5em;
|
||||
white-space: pre;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pod ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
.pod li {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
tt {
|
||||
font-family: 'Droid Sans Mono', sans-serif;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.pod h1 a, .pod h2 a, .pod h3 a, .pod h4 a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<title>
|
||||
EOF
|
||||
);
|
||||
$parser->html_header_after_title(
|
||||
<<'EOF'
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
<a href="/"><h1 id="title">i3 - improved tiling WM</h1></a>
|
||||
<ul id="nav">
|
||||
<li><a style="border-bottom: 2px solid #fff" href="/docs">Docs</a></li>
|
||||
<li><a href="/screenshots">Screens</a></li>
|
||||
<li><a href="http://faq.i3wm.org/">FAQ</a></li>
|
||||
<li><a href="/contact">Contact</a></li>
|
||||
<li><a href="http://bugs.i3wm.org/">Bugs</a></li>
|
||||
</ul>
|
||||
<br style="clear: both">
|
||||
<div id="content" class="pod">
|
||||
<h1>i3 Perl documentation (testsuite)</h1>
|
||||
|
||||
EOF
|
||||
);
|
||||
|
||||
$parser->output_fh($out);
|
||||
$parser->parse_file($in);
|
|
@ -1,7 +1,7 @@
|
|||
i3bar input protocol
|
||||
====================
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
February 2012
|
||||
August 2012
|
||||
|
||||
This document explains the protocol in which i3bar expects its input. It
|
||||
provides support for colors, urgency, shortening and easy manipulation.
|
||||
|
@ -49,6 +49,9 @@ consists of a single JSON hash:
|
|||
{ "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
|
||||
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
|
||||
|
@ -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
|
||||
\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
|
||||
|
||||
full_text::
|
||||
|
|
54
docs/ipc
54
docs/ipc
|
@ -1,7 +1,7 @@
|
|||
IPC interface (interprocess communication)
|
||||
==========================================
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
February 2012
|
||||
August 2012
|
||||
|
||||
This document describes how to interface with i3 from a separate process. This
|
||||
is useful for example to remote-control i3 (to write test cases for example) or
|
||||
|
@ -70,6 +70,9 @@ GET_BAR_CONFIG (6)::
|
|||
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.
|
||||
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:
|
||||
--------------------------------------------------
|
||||
|
@ -125,6 +128,8 @@ MARKS (5)::
|
|||
Reply to the GET_MARKS message.
|
||||
BAR_CONFIG (6)::
|
||||
Reply to the GET_BAR_CONFIG message.
|
||||
VERSION (7)::
|
||||
Reply to the GET_VERSION message.
|
||||
|
||||
=== COMMAND reply
|
||||
|
||||
|
@ -206,7 +211,7 @@ default) or whether a JSON parse error occurred.
|
|||
{ "success": true }
|
||||
-------------------
|
||||
|
||||
=== GET_OUTPUTS reply
|
||||
=== OUTPUTS reply
|
||||
|
||||
The reply consists of a serialized list of outputs. Each output has the
|
||||
following properties:
|
||||
|
@ -270,12 +275,15 @@ border (string)::
|
|||
Can be either "normal", "none" or "1pixel", dependending on the
|
||||
container’s border style.
|
||||
layout (string)::
|
||||
Can be either "default", "stacked", "tabbed", "dockarea" or "output".
|
||||
Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or
|
||||
"output".
|
||||
Other values might be possible in the future, should we add new
|
||||
layouts.
|
||||
orientation (string)::
|
||||
Can be either "none" (for non-split containers), "horizontal" or
|
||||
"vertical".
|
||||
THIS FIELD IS OBSOLETE. It is still present, but your code should not
|
||||
use it. Instead, rely on the layout field.
|
||||
percent (float)::
|
||||
The percentage which this container takes in its parent. A value of
|
||||
+null+ means that the percent property does not make sense for this
|
||||
|
@ -296,6 +304,11 @@ window_rect (map)::
|
|||
geometry (map)::
|
||||
The original geometry the window specified when i3 mapped it. Used when
|
||||
switching a window to floating mode, for example.
|
||||
window (integer)::
|
||||
The X11 window ID of the *actual client window* inside this container.
|
||||
This field is set to null for split containers or otherwise empty
|
||||
containers. This ID corresponds to what xwininfo(1) and other
|
||||
X11-related tools display (usually in hex).
|
||||
urgent (bool)::
|
||||
Whether this container (window or workspace) has the urgency hint set.
|
||||
focused (bool)::
|
||||
|
@ -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]]
|
||||
|
@ -621,7 +663,7 @@ C::
|
|||
Ruby::
|
||||
http://github.com/badboy/i3-ipc
|
||||
Perl::
|
||||
http://search.cpan.org/search?query=AnyEvent::I3
|
||||
https://metacpan.org/module/AnyEvent::I3
|
||||
Python::
|
||||
https://github.com/whitelynx/i3ipc
|
||||
https://github.com/ziberna/i3-py (includes higher-level features)
|
||||
* https://github.com/whitelynx/i3ipc
|
||||
* https://github.com/ziberna/i3-py (includes higher-level features)
|
||||
|
|
|
@ -42,76 +42,99 @@
|
|||
</p>
|
||||
</header>
|
||||
|
||||
|
||||
<section>
|
||||
<h2>Basics</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
|
||||
<td>open new terminal
|
||||
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>j</kbd>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>j</kbd>
|
||||
<td>focus left
|
||||
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>k</kbd>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>k</kbd>
|
||||
<td>focus down
|
||||
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>l</kbd>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>l</kbd>
|
||||
<td>focus up
|
||||
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>;</kbd>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>;</kbd>
|
||||
<td>focus right
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
|
||||
<td>toggle focus mode
|
||||
</table>
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<h2>Changing the container layout</h2>
|
||||
<h2>Moving windows</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>e</kbd>
|
||||
<td>default
|
||||
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>j</kbd>
|
||||
<td>move window left
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>s</kbd>
|
||||
<td>stacking
|
||||
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>k</kbd>
|
||||
<td>move window down
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>w</kbd>
|
||||
<td>tabbed
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>l</kbd>
|
||||
<td>move window up
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>;</kbd>
|
||||
<td>move window right
|
||||
</table>
|
||||
</section>
|
||||
|
||||
</div><div>
|
||||
|
||||
<section>
|
||||
<h2>Fullscreen mode</h2>
|
||||
<h2>Modifying windows</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>f</kbd>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>f</kbd>
|
||||
<td>toggle fullscreen
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>v</kbd>
|
||||
<td>split a window vertically
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>h</kbd>
|
||||
<td>split a window horizontally
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>r</kbd>
|
||||
<td>resize mode
|
||||
</table>
|
||||
<p class="ref">Look at the “Resizing containers / windows” section of the user guide.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>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>
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<h2>Opening other applications</h2>
|
||||
<h2>Floating</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd>d</kbd>
|
||||
<td>open application (with dmenu)
|
||||
</table>
|
||||
|
||||
|
||||
<section>
|
||||
<h2>Closing windows</h2>
|
||||
<table>
|
||||
<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>+ <kbd>q</kbd>
|
||||
<td>kill a window
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
|
||||
<td>drag floating
|
||||
</table>
|
||||
</section>
|
||||
|
||||
|
@ -120,17 +143,10 @@
|
|||
<h2>Using workspaces</h2>
|
||||
<table>
|
||||
<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
|
||||
</table>
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<h2>Moving windows to workspaces</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>1</kbd>–<kbd>9</kbd>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>0</kbd>-<kbd>9</kbd>
|
||||
<td>move a window to another workspace
|
||||
</table>
|
||||
</section>
|
||||
|
@ -138,37 +154,33 @@
|
|||
</div><div>
|
||||
|
||||
<section>
|
||||
<h2>Resizing</h2>
|
||||
<p class="ref">Look at “Resizing containers / windows” section of the user guide.</p>
|
||||
<h2>Opening applications / Closing windows</h2>
|
||||
<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>
|
||||
<h2>Restart / Exit</h2>
|
||||
<table>
|
||||
<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
|
||||
|
||||
<tr>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" />+<kbd></kbd> + <kbd>e</kbd>
|
||||
|
||||
</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>
|
||||
<td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>e</kbd>
|
||||
<td>exit i3
|
||||
</section>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- footer -->
|
||||
<p id="copyright">
|
||||
|
@ -176,7 +188,7 @@
|
|||
<br />
|
||||
All rights reserved
|
||||
<br />
|
||||
Designed by Zeus Panchenko
|
||||
Designed by Zeus Panchenko, updated by Moritz Bandemer
|
||||
</p>
|
||||
<p id="licence">
|
||||
Permission is granted to copy, distribute and/or modify this document provided
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
i3 testsuite
|
||||
============
|
||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
||||
September 2011
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
September 2012
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
that, but it will also be useful for every future change.
|
||||
|
||||
== Relevant documentation
|
||||
|
||||
Apart from this document, you should also have a look at:
|
||||
|
||||
1. The "Modern Perl" book, which can be found at
|
||||
http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
|
||||
2. The latest Perl documentation of the "i3test" (general testcase setup) and
|
||||
"i3test::Test" (additional test instructions) modules:
|
||||
http://build.i3wm.org/docs/lib-i3test.html respectively
|
||||
http://build.i3wm.org/docs/lib-i3test-test.html
|
||||
3. The latest documentation on i3’s IPC interface:
|
||||
http://build.i3wm.org/docs/ipc.html
|
||||
|
||||
== Implementation
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
|
200
docs/userguide
200
docs/userguide
|
@ -1,11 +1,11 @@
|
|||
i3 User’s Guide
|
||||
===============
|
||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
||||
April 2012
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
August 2012
|
||||
|
||||
This document contains all the information you need to configure and use the i3
|
||||
window manager. If it does not, please contact us on IRC (preferred) or post your
|
||||
question(s) on the mailing list.
|
||||
window manager. If it does not, please check http://faq.i3wm.org/ first, then
|
||||
contact us on IRC (preferred) or post your question(s) on the mailing list.
|
||||
|
||||
== Default keybindings
|
||||
|
||||
|
@ -61,16 +61,18 @@ windows.
|
|||
|
||||
TODO: picture of the tree
|
||||
|
||||
To split a window vertically, press +mod+v+. To split it horizontally, press
|
||||
+mod+h+.
|
||||
To split a window vertically, press +mod+v+ before you create the new window.
|
||||
To split it horizontally, press +mod+h+.
|
||||
|
||||
=== Changing the container layout
|
||||
|
||||
A split container can have one of the following layouts:
|
||||
|
||||
default::
|
||||
splith/splitv::
|
||||
Windows are sized so that every window gets an equal amount of space in the
|
||||
container.
|
||||
container. splith distributes the windows horizontally (windows are right next
|
||||
to each other), splitv distributes them vertically (windows are on top of each
|
||||
other).
|
||||
stacking::
|
||||
Only the focused window in the container is displayed. You get a list of
|
||||
windows at the top of the container.
|
||||
|
@ -78,8 +80,8 @@ tabbed::
|
|||
The same principle as +stacking+, but the list of windows at the top is only
|
||||
a single line which is vertically split.
|
||||
|
||||
To switch modes, press +mod+e+ for default, +mod+s+ for stacking and
|
||||
+mod+w+ for tabbed.
|
||||
To switch modes, press +mod+e+ for splith/splitv (it toggles), +mod+s+ for
|
||||
stacking and +mod+w+ for tabbed.
|
||||
|
||||
image:modes.png[Container modes]
|
||||
|
||||
|
@ -196,20 +198,21 @@ image::tree-shot4.png["shot4",title="Two terminals on standard workspace"]
|
|||
|
||||
It is only natural to use so-called +Split Containers+ in order to build a
|
||||
layout when using a tree as data structure. In i3, every +Container+ has an
|
||||
orientation (horizontal, vertical or unspecified). So, in our example with the
|
||||
workspace, the default orientation of the workspace +Container+ is horizontal
|
||||
(most monitors are widescreen nowadays). If you change the orientation to
|
||||
vertical (+mod+v+ in the default config) and *then* open two terminals, i3 will
|
||||
configure your windows like this:
|
||||
orientation (horizontal, vertical or unspecified) and the orientation depends
|
||||
on the layout the container is in (vertical for splitv and stacking, horizontal
|
||||
for splith and tabbed). So, in our example with the workspace, the default
|
||||
layout of the workspace +Container+ is splith (most monitors are widescreen
|
||||
nowadays). If you change the layout to splitv (+mod+l+ in the default config)
|
||||
and *then* open two terminals, i3 will configure your windows like this:
|
||||
|
||||
image::tree-shot2.png["shot2",title="Vertical Workspace Orientation"]
|
||||
|
||||
An interesting new feature of the tree branch is the ability to split anything:
|
||||
Let’s assume you have two terminals on a workspace (with horizontal
|
||||
orientation), focus is on the right terminal. Now you want to open another
|
||||
terminal window below the current one. If you would just open a new terminal
|
||||
window, it would show up to the right due to the horizontal workspace
|
||||
orientation. Instead, press +mod+v+ to create a +Vertical Split Container+ (to
|
||||
An interesting new feature of i3 since version 4 is the ability to split anything:
|
||||
Let’s assume you have two terminals on a workspace (with splith layout, that is
|
||||
horizontal orientation), focus is on the right terminal. Now you want to open
|
||||
another terminal window below the current one. If you would just open a new
|
||||
terminal window, it would show up to the right due to the splith layout.
|
||||
Instead, press +mod+v+ to split the container with the splitv layout (to
|
||||
open a +Horizontal Split Container+, use +mod+h+). Now you can open a new
|
||||
terminal and it will open below the current one:
|
||||
|
||||
|
@ -291,11 +294,21 @@ a # and can only be used at the beginning of a line:
|
|||
# This is a comment
|
||||
-------------------
|
||||
|
||||
[[fonts]]
|
||||
|
||||
=== Fonts
|
||||
|
||||
i3 uses X core fonts (not Xft) for rendering window titles. You can use
|
||||
+xfontsel(1)+ to generate such a font description. To see special characters
|
||||
(Unicode), you need to use a font which supports the ISO-10646 encoding.
|
||||
i3 has support for both X core fonts and FreeType fonts (through Pango) to
|
||||
render window titles.
|
||||
|
||||
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
|
||||
and fall back to a working font.
|
||||
|
@ -303,11 +316,13 @@ and fall back to a working font.
|
|||
*Syntax*:
|
||||
------------------------------
|
||||
font <X core font description>
|
||||
font xft:<a FreeType font description>
|
||||
------------------------------
|
||||
|
||||
*Examples*:
|
||||
--------------------------------------------------------------
|
||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||
font xft:DejaVu Sans Mono 10
|
||||
--------------------------------------------------------------
|
||||
|
||||
[[keybindings]]
|
||||
|
@ -333,10 +348,15 @@ your bindings in the same physical location on the keyboard, use keycodes.
|
|||
If you don’t switch layouts, and want a clean and simple config file, use
|
||||
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*:
|
||||
----------------------------------
|
||||
bindsym [Modifiers+]keysym command
|
||||
bindcode [Modifiers+]keycode command
|
||||
bindsym [--release] [Modifiers+]keysym command
|
||||
bindcode [--release] [Modifiers+]keycode command
|
||||
----------------------------------
|
||||
|
||||
*Examples*:
|
||||
|
@ -348,7 +368,13 @@ bindsym mod+f fullscreen
|
|||
bindsym mod+Shift+r restart
|
||||
|
||||
# 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:
|
||||
|
@ -465,6 +491,22 @@ new_window <normal|1pixel|none>
|
|||
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)
|
||||
|
||||
With the +for_window+ command, you can let i3 execute any command when it
|
||||
|
@ -573,6 +615,22 @@ logfile first (see http://i3wm.org/docs/debugging.html). It includes more
|
|||
details about the matching process and the window’s actual class, instance and
|
||||
title when starting up.
|
||||
|
||||
Note that if you want to start an application just once on a specific
|
||||
workspace, but you don’t want to assign all instances of it permanently, you
|
||||
can make use of i3’s startup-notification support (see <<exec>>) in your config
|
||||
file in the following way:
|
||||
|
||||
*Start iceweasel on workspace 3 (once)*:
|
||||
-------------------------------------------------------------------------------
|
||||
# Start iceweasel on workspace 3, then switch back to workspace 1
|
||||
# (Being a command-line utility, i3-msg does not support startup notifications,
|
||||
# hence the exec --no-startup-id.)
|
||||
# (Starting iceweasel with i3’s exec command is important in order to make i3
|
||||
# create a startup notification context, without which the iceweasel window(s)
|
||||
# cannot be matched onto the workspace on which the command was started.)
|
||||
exec --no-startup-id i3-msg 'workspace 3; exec iceweasel; workspace 1'
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
=== Automatically starting applications on i3 startup
|
||||
|
||||
By using the +exec+ keyword outside a keybinding, you can configure
|
||||
|
@ -1010,8 +1068,7 @@ xrandr --output <output> --primary
|
|||
|
||||
=== Font
|
||||
|
||||
Specifies the font (again, X core font, not Xft, just like in i3) to be used in
|
||||
the bar.
|
||||
Specifies the font to be used in the bar. See <<fonts>>.
|
||||
|
||||
*Syntax*:
|
||||
---------------------
|
||||
|
@ -1022,6 +1079,7 @@ font <font>
|
|||
--------------------------------------------------------------
|
||||
bar {
|
||||
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
|
||||
|
||||
The split command makes the current window a split container. Split containers
|
||||
can contain multiple windows. Every split container has an orientation, it is
|
||||
either split horizontally (a new window gets placed to the right of the current
|
||||
one) or vertically (a new window gets placed below the current one).
|
||||
can contain multiple windows. Depending on the layout of the split container,
|
||||
new windows get placed to the right of the current one (splith) or new windows
|
||||
get placed below the current one (splitv).
|
||||
|
||||
If you apply this command to a split container with the same orientation,
|
||||
nothing will happen. If you use a different orientation, the split container’s
|
||||
orientation will be changed (if it does not have more than one window).
|
||||
orientation will be changed (if it does not have more than one window). Use
|
||||
+layout toggle split+ to change the layout of any split container from splitv
|
||||
to splith or vice-versa.
|
||||
|
||||
*Syntax*:
|
||||
---------------------------
|
||||
|
@ -1211,19 +1271,32 @@ bindsym mod+h split horizontal
|
|||
|
||||
=== Manipulating layout
|
||||
|
||||
Use +layout default+, +layout stacking+ or +layout tabbed+ to change the
|
||||
current container layout to default, stacking or tabbed layout, respectively.
|
||||
Use +layout toggle split+, +layout stacking+ or +layout tabbed+ to change the
|
||||
current container layout to splith/splitv, stacking or tabbed layout,
|
||||
respectively.
|
||||
|
||||
To make the current window (!) fullscreen, use +fullscreen+, to make
|
||||
it floating (or tiling again) use +floating enable+ respectively +floating disable+
|
||||
(or +floating toggle+):
|
||||
|
||||
*Syntax*:
|
||||
--------------
|
||||
layout <tabbed|stacking>
|
||||
layout toggle [split|all]
|
||||
--------------
|
||||
|
||||
*Examples*:
|
||||
--------------
|
||||
bindsym mod+s layout stacking
|
||||
bindsym mod+l layout default
|
||||
bindsym mod+l layout toggle split
|
||||
bindsym mod+w layout tabbed
|
||||
|
||||
# Toggle between stacking/tabbed/split:
|
||||
bindsym mod+x layout toggle
|
||||
|
||||
# Toggle between stacking/tabbed/splith/splitv:
|
||||
bindsym mod+x layout toggle all
|
||||
|
||||
# Toggle fullscreen
|
||||
bindsym mod+f fullscreen
|
||||
|
||||
|
@ -1313,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
|
||||
combination. To restrict those to the current output, use +workspace
|
||||
next_on_output+ and +workspace prev_on_output+. Similarly, you can use +move
|
||||
container to workspace next+ and +move container to workspace prev+ to move a
|
||||
container to the next/previous workspace.
|
||||
container to workspace next+, +move container to workspace prev+ to move a
|
||||
container to the next/previous workspace and +move container to workspace current+
|
||||
(the last one makes sense only when used with criteria).
|
||||
|
||||
See <<move_to_outputs>> for how to move a container/workspace to a different
|
||||
RandR output.
|
||||
|
||||
[[back_and_forth]]
|
||||
To switch back to the previously focused workspace, use +workspace
|
||||
back_and_forth+.
|
||||
|
||||
To move a container to another xrandr output such as +LVDS1+ or +VGA1+, you can
|
||||
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
|
||||
output name to move to the next output in the specified direction.
|
||||
*Syntax*:
|
||||
-----------------------------------
|
||||
workspace <next|prev|next_on_output|prev_on_output>
|
||||
workspace back_and_forth
|
||||
workspace <name>
|
||||
workspace number <number>
|
||||
|
||||
To move a whole workspace to another xrandr output such as +LVDS1+ or +VGA1+,
|
||||
you can use the +move workspace to output+ command followed by the name of the
|
||||
target output. You may also use +left+, +right+, +up+, +down+ instead of the
|
||||
xrandr output name to move to the next output in the specified direction.
|
||||
move [window|container] [to] workspace <name>
|
||||
move [window|container] [to] workspace number <number>
|
||||
move [window|container] [to] workspace <prev|next|current>
|
||||
-----------------------------------
|
||||
|
||||
*Examples*:
|
||||
-------------------------
|
||||
|
@ -1345,6 +1424,9 @@ bindsym mod+b workspace back_and_forth
|
|||
|
||||
# move the whole workspace to the next output
|
||||
bindsym mod+x move workspace to output right
|
||||
|
||||
# move firefox to current workspace
|
||||
bindsym mod+F1 [class="Firefox"] move workspace current
|
||||
-------------------------
|
||||
|
||||
==== Named workspaces
|
||||
|
@ -1376,7 +1458,7 @@ to switch to the workspace which begins with number 1, regardless of which name
|
|||
it has. This is useful in case you are changing the workspace’s name
|
||||
dynamically.
|
||||
|
||||
=== Renaming workspaces
|
||||
==== Renaming workspaces
|
||||
|
||||
You can rename workspaces. This might be useful to start with the default
|
||||
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"'
|
||||
------------------------------------------------
|
||||
|
||||
=== 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]]
|
||||
|
||||
=== Resizing containers/windows
|
||||
|
|
|
@ -152,7 +152,7 @@ for my $state (@keys) {
|
|||
$cmd =~ s/[^(]+\(//;
|
||||
$cmd =~ s/\)$//;
|
||||
$cmd = ", $cmd" if length($cmd) > 0;
|
||||
say $callfh qq| printf("$fmt\\n"$cmd);|;
|
||||
say $callfh qq| fprintf(stderr, "$fmt\\n"$cmd);|;
|
||||
say $callfh '#endif';
|
||||
say $callfh " state = $next_state;";
|
||||
say $callfh " break;";
|
||||
|
|
|
@ -1,47 +1,10 @@
|
|||
# Default value so one can compile i3-input standalone
|
||||
TOPDIR=..
|
||||
all:
|
||||
$(MAKE) -C .. i3-config-wizard/i3-config-wizard
|
||||
|
||||
include $(TOPDIR)/common.mk
|
||||
|
||||
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||
AUTOGENERATED:=cfgparse.tab.c cfgparse.yy.c
|
||||
FILES:=$(patsubst %.c,%.o,$(filter-out $(AUTOGENERATED),$(wildcard *.c)))
|
||||
HEADERS:=$(wildcard *.h)
|
||||
|
||||
CPPFLAGS += -I$(TOPDIR)/include
|
||||
|
||||
# Depend on the specific file (.c for each .o) and on all headers
|
||||
%.o: %.c ${HEADERS}
|
||||
echo "[i3-config-wizard] CC $<"
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
all: i3-config-wizard
|
||||
|
||||
i3-config-wizard: $(TOPDIR)/libi3/libi3.a cfgparse.y.o cfgparse.yy.o ${FILES}
|
||||
echo "[i3-config-wizard] LINK i3-config-wizard"
|
||||
$(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
|
||||
|
||||
$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c
|
||||
$(MAKE) -C $(TOPDIR)/libi3
|
||||
|
||||
cfgparse.yy.o: cfgparse.l cfgparse.y.o ${HEADERS}
|
||||
echo "[i3-config-wizard] LEX $<"
|
||||
$(FLEX) -i -o$(@:.o=.c) $<
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(@:.o=.c)
|
||||
|
||||
cfgparse.y.o: cfgparse.y ${HEADERS}
|
||||
echo "[i3-config-wizard] YACC $<"
|
||||
$(BISON) --debug --verbose -b $(basename $< .y) -d $<
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $(<:.y=.tab.c)
|
||||
|
||||
|
||||
install: all
|
||||
echo "[i3-config-wizard] INSTALL"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-config-wizard $(DESTDIR)$(PREFIX)/bin/
|
||||
install:
|
||||
$(MAKE) -C .. install-i3-config-wizard
|
||||
|
||||
clean:
|
||||
rm -f *.o cfgparse.tab.{c,h} cfgparse.output cfgparse.yy.c
|
||||
$(MAKE) -C .. clean-i3-config-wizard
|
||||
|
||||
distclean: clean
|
||||
rm -f i3-config-wizard
|
||||
.PHONY: all install clean
|
||||
|
|
|
@ -39,6 +39,8 @@ extern FILE *yyin;
|
|||
YY_BUFFER_STATE yy_scan_string(const char *);
|
||||
|
||||
static struct context *context;
|
||||
static xcb_connection_t *conn;
|
||||
static xcb_key_symbols_t *keysyms;
|
||||
|
||||
/* We don’t need yydebug for now, as we got decent error messages using
|
||||
* yyerror(). Should you ever want to extend the parser, it might be handy
|
||||
|
@ -67,6 +69,13 @@ int yywrap() {
|
|||
char *rewrite_binding(const char *bindingline) {
|
||||
char *result = NULL;
|
||||
|
||||
conn = xcb_connect(NULL, NULL);
|
||||
if (conn == NULL || xcb_connection_has_error(conn)) {
|
||||
fprintf(stderr, "Cannot open display\n");
|
||||
exit(1);
|
||||
}
|
||||
keysyms = xcb_key_symbols_alloc(conn);
|
||||
|
||||
context = calloc(sizeof(struct context), 1);
|
||||
|
||||
yy_scan_string(bindingline);
|
||||
|
@ -81,6 +90,8 @@ char *rewrite_binding(const char *bindingline) {
|
|||
if (context->line_copy)
|
||||
free(context->line_copy);
|
||||
free(context);
|
||||
xcb_key_symbols_free(keysyms);
|
||||
xcb_disconnect(conn);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -101,6 +112,28 @@ static char *modifier_to_string(int modifiers) {
|
|||
else return strdup("");
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if sym is bound to any key except for 'except_keycode' on the
|
||||
* first four layers (normal, shift, mode_switch, mode_switch + shift).
|
||||
*
|
||||
*/
|
||||
static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) {
|
||||
xcb_keycode_t i,
|
||||
min_keycode = xcb_get_setup(conn)->min_keycode,
|
||||
max_keycode = xcb_get_setup(conn)->max_keycode;
|
||||
|
||||
for (i = min_keycode; i && i <= max_keycode; i++) {
|
||||
if (i == except_keycode)
|
||||
continue;
|
||||
for (int level = 0; level < 4; level++) {
|
||||
if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym)
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%error-verbose
|
||||
|
@ -139,11 +172,22 @@ bindcode:
|
|||
* different key than the upper-case one (unlikely for letters, but
|
||||
* more likely for special characters). */
|
||||
level = 1;
|
||||
|
||||
/* Try to use the keysym on the first level (lower-case). In case
|
||||
* this doesn’t make it ambiguous (think of a keyboard layout
|
||||
* having '1' on two different keys, but '!' only on keycode 10),
|
||||
* we’ll stick with the keysym of the first level.
|
||||
*
|
||||
* This reduces a lot of confusion for users who switch keyboard
|
||||
* layouts from qwerty to qwertz or other slight variations of
|
||||
* qwerty (yes, that happens quite often). */
|
||||
KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, 0);
|
||||
if (!keysym_used_on_other_key(sym, $4))
|
||||
level = 0;
|
||||
}
|
||||
KeySym sym = XkbKeycodeToKeysym(dpy, $4, 0, level);
|
||||
char *str = XKeysymToString(sym);
|
||||
char *modifiers = modifier_to_string($<number>3);
|
||||
// TODO: modifier to string
|
||||
sasprintf(&(context->result), "bindsym %s%s %s\n", modifiers, str, $<string>6);
|
||||
free(modifiers);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
ALL_TARGETS += i3-config-wizard/i3-config-wizard
|
||||
INSTALL_TARGETS += install-i3-config-wizard
|
||||
CLEAN_TARGETS += clean-i3-config-wizard
|
||||
|
||||
i3_config_wizard_SOURCES_GENERATED = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c
|
||||
i3_config_wizard_SOURCES := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c))
|
||||
i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h)
|
||||
i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS)
|
||||
i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS)
|
||||
|
||||
i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_config_wizard_SOURCES:.c=.o)
|
||||
|
||||
|
||||
i3-config-wizard/%.o: i3-config-wizard/%.c $(i3_config_wizard_HEADERS)
|
||||
echo "[i3-config-wizard] CC $<"
|
||||
$(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
i3-config-wizard/cfgparse.yy.c: i3-config-wizard/cfgparse.l i3-config-wizard/cfgparse.tab.o $(i3_config_wizard_HEADERS)
|
||||
echo "[i3-config-wizard] LEX $<"
|
||||
$(FLEX) -i -o $@ $<
|
||||
|
||||
i3-config-wizard/cfgparse.tab.c: i3-config-wizard/cfgparse.y $(i3_config_wizard_HEADERS)
|
||||
echo "[i3-config-wizard] YACC $<"
|
||||
$(BISON) --debug --verbose -b $(basename $< .y) -d $<
|
||||
|
||||
i3-config-wizard/i3-config-wizard: libi3.a $(i3_config_wizard_OBJECTS)
|
||||
echo "[i3-config-wizard] Link i3-config-wizard"
|
||||
$(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_config_wizard_LIBS)
|
||||
|
||||
install-i3-config-wizard: i3-config-wizard/i3-config-wizard
|
||||
echo "[i3-config-wizard] Install"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-config-wizard/i3-config-wizard $(DESTDIR)$(PREFIX)/bin/
|
||||
|
||||
clean-i3-config-wizard:
|
||||
echo "[i3-config-wizard] Clean"
|
||||
rm -f $(i3_config_wizard_OBJECTS) $(i3_config_wizard_SOURCES_GENERATED) i3-config-wizard/i3-config-wizard i3-config-wizard/cfgparse.{output,dot,tab.h,y.o}
|
|
@ -69,6 +69,7 @@ enum { MOD_Mod1, MOD_Mod4 } modifier = MOD_Mod4;
|
|||
static char *config_path;
|
||||
static uint32_t xcb_numlock_mask;
|
||||
xcb_connection_t *conn;
|
||||
xcb_screen_t *root_screen;
|
||||
static xcb_get_modifier_mapping_reply_t *modmap_reply;
|
||||
static i3Font font;
|
||||
static i3Font bold_font;
|
||||
|
@ -83,6 +84,26 @@ Display *dpy;
|
|||
char *rewrite_binding(const char *bindingline);
|
||||
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.
|
||||
* 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);
|
||||
|
||||
#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)
|
||||
|
||||
if (current_step == STEP_WELCOME) {
|
||||
|
@ -456,7 +477,7 @@ int main(int argc, char *argv[]) {
|
|||
#include "atoms.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;
|
||||
|
||||
if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL)))
|
||||
|
|
|
@ -1,32 +1,10 @@
|
|||
# Default value so one can compile i3-dump-log standalone
|
||||
TOPDIR=..
|
||||
all:
|
||||
$(MAKE) -C .. i3-dump-log/i3-dump-log
|
||||
|
||||
include $(TOPDIR)/common.mk
|
||||
|
||||
CFLAGS += -I$(TOPDIR)/include
|
||||
|
||||
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||
FILES=$(patsubst %.c,%.o,$(wildcard *.c))
|
||||
HEADERS=$(wildcard *.h)
|
||||
|
||||
# Depend on the specific file (.c for each .o) and on all headers
|
||||
%.o: %.c ${HEADERS}
|
||||
echo "[i3-dump-log] CC $<"
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
all: i3-dump-log
|
||||
|
||||
i3-dump-log: ${FILES}
|
||||
echo "[i3-dump-log] LINK i3-dump-log"
|
||||
$(CC) $(LDFLAGS) -o i3-dump-log ${FILES} $(LIBS)
|
||||
|
||||
install: all
|
||||
echo "[i3-dump-log] INSTALL"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-dump-log $(DESTDIR)$(PREFIX)/bin/
|
||||
install:
|
||||
$(MAKE) -C .. install-i3-dump-log
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
$(MAKE) -C .. clean-i3-dump-log
|
||||
|
||||
distclean: clean
|
||||
rm -f i3-dump-log
|
||||
.PHONY: all install clean
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
ALL_TARGETS += i3-dump-log/i3-dump-log
|
||||
INSTALL_TARGETS += install-i3-dump-log
|
||||
CLEAN_TARGETS += clean-i3-dump-log
|
||||
|
||||
i3_dump_log_SOURCES := $(wildcard i3-dump-log/*.c)
|
||||
i3_dump_log_HEADERS := $(wildcard i3-dump-log/*.h)
|
||||
i3_dump_log_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS)
|
||||
i3_dump_log_LIBS = $(XCB_LIBS)
|
||||
|
||||
i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o)
|
||||
|
||||
|
||||
i3-dump-log/%.o: i3-dump-log/%.c $(i3_dump_log_HEADERS)
|
||||
echo "[i3-dump-log] CC $<"
|
||||
$(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
i3-dump-log/i3-dump-log: libi3.a $(i3_dump_log_OBJECTS)
|
||||
echo "[i3-dump-log] Link i3-dump-log"
|
||||
$(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_dump_log_LIBS)
|
||||
|
||||
install-i3-dump-log: i3-dump-log/i3-dump-log
|
||||
echo "[i3-dump-log] Install"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-dump-log/i3-dump-log $(DESTDIR)$(PREFIX)/bin/
|
||||
|
||||
clean-i3-dump-log:
|
||||
echo "[i3-dump-log] Clean"
|
||||
rm -f $(i3_dump_log_OBJECTS) i3-dump-log/i3-dump-log
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
|
@ -28,18 +28,47 @@
|
|||
#include "shmlog.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 o, option_index = 0;
|
||||
bool verbose = false;
|
||||
bool verbose = false,
|
||||
follow = false;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"verbose", no_argument, 0, 'V'},
|
||||
{"follow", no_argument, 0, 'f'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{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) {
|
||||
if (o == 'v') {
|
||||
|
@ -47,9 +76,11 @@ int main(int argc, char *argv[]) {
|
|||
return 0;
|
||||
} else if (o == 'V') {
|
||||
verbose = true;
|
||||
} else if (o == 'f') {
|
||||
follow = true;
|
||||
} else if (o == 'h') {
|
||||
printf("i3-dump-log " I3_VERSION "\n");
|
||||
printf("i3-dump-log [-s <socket>]\n");
|
||||
printf("i3-dump-log [-f] [-s <socket>]\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -90,45 +121,61 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
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)
|
||||
err(EXIT_FAILURE, "Could not shm_open SHM segment for the i3 log (%s)", shmname);
|
||||
|
||||
if (fstat(logbuffer_shm, &statbuf) != 0)
|
||||
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)
|
||||
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)
|
||||
printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n",
|
||||
header->offset_next_write, header->offset_last_wrap, header->size, shmname);
|
||||
int chars;
|
||||
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++;
|
||||
walk = logbuffer + header->offset_next_write;
|
||||
|
||||
/* Print the oldest log lines. We use printf("%s") to stop on \0. */
|
||||
while (walk < (logbuffer + header->offset_last_wrap)) {
|
||||
chars = printf("%s", walk);
|
||||
/* Shortcut: If there are two consecutive \0 bytes, this part of the
|
||||
* buffer was never touched. To not call printf() for every byte of the
|
||||
* buffer, we directly exit the loop. */
|
||||
if (*walk == '\0' && *(walk+1) == '\0')
|
||||
break;
|
||||
walk += (chars > 0 ? chars : 1);
|
||||
/* We first need to print old content in case there was at least one
|
||||
* wrapping already. */
|
||||
|
||||
if (*walk != '\0') {
|
||||
/* In case there was a write to the buffer already, skip the first
|
||||
* old line, it very likely is mangled. Not a problem, though, the log
|
||||
* is chatty enough to have plenty lines left. */
|
||||
while (*walk != '\n')
|
||||
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 */
|
||||
walk = logbuffer + sizeof(i3_shmlog_header);
|
||||
while (walk < (logbuffer + header->offset_next_write)) {
|
||||
chars = printf("%s", walk);
|
||||
walk += (chars > 0 ? chars : 1);
|
||||
print_till_end();
|
||||
|
||||
if (follow) {
|
||||
/* Since pthread_cond_wait() expects a mutex, we need to provide one.
|
||||
* To not lock i3 (that’s bad, mhkay?) we just define one outside of
|
||||
* the shared memory. */
|
||||
pthread_mutex_t dummy_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_lock(&dummy_mutex);
|
||||
while (1) {
|
||||
pthread_cond_wait(&(header->condvar), &dummy_mutex);
|
||||
/* If this was not a spurious wakeup, print the new lines. */
|
||||
if (header->offset_next_write != offset_next_write) {
|
||||
offset_next_write = header->offset_next_write;
|
||||
print_till_end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,35 +1,10 @@
|
|||
# Default value so one can compile i3-input standalone
|
||||
TOPDIR=..
|
||||
all:
|
||||
$(MAKE) -C .. i3-input/i3-input
|
||||
|
||||
include $(TOPDIR)/common.mk
|
||||
|
||||
CPPFLAGS += -I$(TOPDIR)/include
|
||||
|
||||
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||
FILES=$(patsubst %.c,%.o,$(wildcard *.c))
|
||||
HEADERS=$(wildcard *.h)
|
||||
|
||||
# Depend on the specific file (.c for each .o) and on all headers
|
||||
%.o: %.c ${HEADERS}
|
||||
echo "[i3-input] CC $<"
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
all: i3-input
|
||||
|
||||
i3-input: $(TOPDIR)/libi3/libi3.a ${FILES}
|
||||
echo "[i3-input] LINK i3-input"
|
||||
$(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
|
||||
|
||||
$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c
|
||||
$(MAKE) -C $(TOPDIR)/libi3
|
||||
|
||||
install: all
|
||||
echo "[i3-input] INSTALL"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-input $(DESTDIR)$(PREFIX)/bin/
|
||||
install:
|
||||
$(MAKE) -C .. install-i3-input
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
$(MAKE) -C .. clean-i3-input
|
||||
|
||||
distclean: clean
|
||||
rm -f i3-input
|
||||
.PHONY: all install clean
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
ALL_TARGETS += i3-input/i3-input
|
||||
INSTALL_TARGETS += install-i3-input
|
||||
CLEAN_TARGETS += clean-i3-input
|
||||
|
||||
i3_input_SOURCES := $(wildcard i3-input/*.c)
|
||||
i3_input_HEADERS := $(wildcard i3-input/*.h)
|
||||
i3_input_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS)
|
||||
i3_input_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS)
|
||||
|
||||
i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o)
|
||||
|
||||
|
||||
i3-input/%.o: i3-input/%.c $(i3_input_HEADERS)
|
||||
echo "[i3-input] CC $<"
|
||||
$(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_input_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
i3-input/i3-input: libi3.a $(i3_input_OBJECTS)
|
||||
echo "[i3-input] Link i3-input"
|
||||
$(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_input_LIBS)
|
||||
|
||||
install-i3-input: i3-input/i3-input
|
||||
echo "[i3-input] Install"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-input/i3-input $(DESTDIR)$(PREFIX)/bin/
|
||||
|
||||
clean-i3-input:
|
||||
echo "[i3-input] Clean"
|
||||
rm -f $(i3_input_OBJECTS) i3-input/i3-input
|
|
@ -45,15 +45,36 @@ static bool modeswitch_active = false;
|
|||
static xcb_window_t win;
|
||||
static xcb_pixmap_t pixmap;
|
||||
static xcb_gcontext_t pixmap_gc;
|
||||
static char *glyphs_ucs[512];
|
||||
static xcb_char2b_t glyphs_ucs[512];
|
||||
static char *glyphs_utf8[512];
|
||||
static int input_position;
|
||||
static i3Font font;
|
||||
static char *prompt;
|
||||
static size_t prompt_len;
|
||||
static i3String *prompt;
|
||||
static int prompt_offset = 0;
|
||||
static int limit;
|
||||
xcb_window_t root;
|
||||
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
|
||||
|
@ -96,25 +117,21 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
|
|||
/* restore font color */
|
||||
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
|
||||
|
||||
/* draw the text */
|
||||
uint8_t *con = concat_strings(glyphs_ucs, input_position);
|
||||
char *full_text = (char*)con;
|
||||
/* draw the prompt … */
|
||||
if (prompt != NULL) {
|
||||
full_text = malloc((prompt_len + input_position) * 2 + 1);
|
||||
if (full_text == NULL)
|
||||
err(EXIT_FAILURE, "malloc() failed\n");
|
||||
memcpy(full_text, prompt, prompt_len * 2);
|
||||
memcpy(full_text + (prompt_len * 2), con, input_position * 2);
|
||||
draw_text(prompt, pixmap, pixmap_gc, 4, 4, 492);
|
||||
}
|
||||
/* … and the text */
|
||||
if (input_position > 0)
|
||||
{
|
||||
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 */
|
||||
xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8);
|
||||
xcb_flush(conn);
|
||||
free(con);
|
||||
if (prompt != NULL)
|
||||
free(full_text);
|
||||
|
||||
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) {
|
||||
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);
|
||||
if (sym == XK_Mode_switch) {
|
||||
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;
|
||||
|
||||
input_position--;
|
||||
free(glyphs_ucs[input_position]);
|
||||
free(glyphs_utf8[input_position]);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* store the UCS into a string */
|
||||
uint8_t inp[3] = {(ucs & 0xFF00) >> 8, (ucs & 0xFF), 0};
|
||||
xcb_char2b_t inp;
|
||||
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 */
|
||||
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);
|
||||
|
||||
glyphs_ucs[input_position] = malloc(3 * sizeof(uint8_t));
|
||||
if (glyphs_ucs[input_position] == NULL)
|
||||
err(EXIT_FAILURE, "malloc() failed\n");
|
||||
memcpy(glyphs_ucs[input_position], inp, 3);
|
||||
glyphs_ucs[input_position] = inp;
|
||||
glyphs_utf8[input_position] = out;
|
||||
input_position++;
|
||||
|
||||
|
@ -318,8 +322,8 @@ int main(int argc, char *argv[]) {
|
|||
limit = atoi(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
FREE(prompt);
|
||||
prompt = strdup(optarg);
|
||||
i3string_free(prompt);
|
||||
prompt = i3string_from_utf8(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
FREE(pattern);
|
||||
|
@ -349,15 +353,12 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
sockfd = ipc_connect(socket_path);
|
||||
|
||||
if (prompt != NULL)
|
||||
prompt = (char*)convert_utf8_to_ucs2(prompt, &prompt_len);
|
||||
|
||||
int screens;
|
||||
conn = xcb_connect(NULL, &screens);
|
||||
if (!conn || xcb_connection_has_error(conn))
|
||||
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;
|
||||
|
||||
symbols = xcb_key_symbols_alloc(conn);
|
||||
|
@ -365,6 +366,9 @@ int main(int argc, char *argv[]) {
|
|||
font = load_font(pattern, true);
|
||||
set_font(&font);
|
||||
|
||||
if (prompt != NULL)
|
||||
prompt_offset = predict_text_width(prompt);
|
||||
|
||||
/* Open an input window */
|
||||
win = xcb_generate_id(conn);
|
||||
xcb_create_window(
|
||||
|
|
|
@ -202,7 +202,7 @@ sub convert_command {
|
|||
# simple replacements
|
||||
my @replace = (
|
||||
qr/^s/ => 'layout stacking',
|
||||
qr/^d/ => 'layout default',
|
||||
qr/^d/ => 'layout toggle split',
|
||||
qr/^T/ => 'layout tabbed',
|
||||
qr/^f($|[^go])/ => 'fullscreen',
|
||||
qr/^fg/ => 'fullscreen global',
|
||||
|
|
|
@ -1,32 +1,10 @@
|
|||
# Default value so one can compile i3-msg standalone
|
||||
TOPDIR=..
|
||||
all:
|
||||
$(MAKE) -C .. i3-msg/i3-msg
|
||||
|
||||
include $(TOPDIR)/common.mk
|
||||
|
||||
CFLAGS += -I$(TOPDIR)/include
|
||||
|
||||
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||
FILES=$(patsubst %.c,%.o,$(wildcard *.c))
|
||||
HEADERS=$(wildcard *.h)
|
||||
|
||||
# Depend on the specific file (.c for each .o) and on all headers
|
||||
%.o: %.c ${HEADERS}
|
||||
echo "[i3-msg] CC $<"
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
all: i3-msg
|
||||
|
||||
i3-msg: ${FILES}
|
||||
echo "[i3-msg] LINK i3-msg"
|
||||
$(CC) $(LDFLAGS) -o i3-msg ${FILES} $(LIBS)
|
||||
|
||||
install: all
|
||||
echo "[i3-msg] INSTALL"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-msg $(DESTDIR)$(PREFIX)/bin/
|
||||
install:
|
||||
$(MAKE) -C .. install-i3-msg
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
$(MAKE) -C .. clean-i3-msg
|
||||
|
||||
distclean: clean
|
||||
rm -f i3-msg
|
||||
.PHONY: all install clean
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
ALL_TARGETS += i3-msg/i3-msg
|
||||
INSTALL_TARGETS += install-i3-msg
|
||||
CLEAN_TARGETS += clean-i3-msg
|
||||
|
||||
i3_msg_SOURCES := $(wildcard i3-msg/*.c)
|
||||
i3_msg_HEADERS := $(wildcard i3-msg/*.h)
|
||||
i3_msg_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS)
|
||||
i3_msg_LIBS = $(XCB_LIBS)
|
||||
|
||||
i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o)
|
||||
|
||||
|
||||
i3-msg/%.o: i3-msg/%.c $(i3_msg_HEADERS)
|
||||
echo "[i3-msg] CC $<"
|
||||
$(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_msg_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
i3-msg/i3-msg: libi3.a $(i3_msg_OBJECTS)
|
||||
echo "[i3-msg] Link i3-msg"
|
||||
$(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_msg_LIBS)
|
||||
|
||||
install-i3-msg: i3-msg/i3-msg
|
||||
echo "[i3-msg] Install"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-msg/i3-msg $(DESTDIR)$(PREFIX)/bin/
|
||||
|
||||
clean-i3-msg:
|
||||
echo "[i3-msg] Clean"
|
||||
rm -f $(i3_msg_OBJECTS) i3-msg/i3-msg
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
* IPC via UNIX domain sockets.
|
||||
|
@ -73,9 +73,11 @@ int main(int argc, char *argv[]) {
|
|||
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
|
||||
else if (strcasecmp(optarg, "get_bar_config") == 0)
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
} else if (o == 'q') {
|
||||
|
|
|
@ -1,35 +1,10 @@
|
|||
# Default value so one can compile i3-nagbar standalone
|
||||
TOPDIR=..
|
||||
all:
|
||||
$(MAKE) -C .. i3-nagbar/i3-nagbar
|
||||
|
||||
include $(TOPDIR)/common.mk
|
||||
|
||||
CPPFLAGS += -I$(TOPDIR)/include
|
||||
|
||||
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||
FILES=$(patsubst %.c,%.o,$(wildcard *.c))
|
||||
HEADERS=$(wildcard *.h)
|
||||
|
||||
# Depend on the specific file (.c for each .o) and on all headers
|
||||
%.o: %.c ${HEADERS}
|
||||
echo "[i3-nagbar] CC $<"
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
all: i3-nagbar
|
||||
|
||||
i3-nagbar: $(TOPDIR)/libi3/libi3.a ${FILES}
|
||||
echo "[i3-nagbar] LINK i3-nagbar"
|
||||
$(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
|
||||
|
||||
$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c
|
||||
$(MAKE) -C $(TOPDIR)/libi3
|
||||
|
||||
install: all
|
||||
echo "[i3-nagbar] INSTALL"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-nagbar $(DESTDIR)$(PREFIX)/bin/
|
||||
install:
|
||||
$(MAKE) -C .. install-i3-nagbar
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
$(MAKE) -C .. clean-i3-nagbar
|
||||
|
||||
distclean: clean
|
||||
rm -f i3-nagbar
|
||||
.PHONY: all install clean
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
ALL_TARGETS += i3-nagbar/i3-nagbar
|
||||
INSTALL_TARGETS += install-i3-nagbar
|
||||
CLEAN_TARGETS += clean-i3-nagbar
|
||||
|
||||
i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c)
|
||||
i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h)
|
||||
i3_nagbar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS)
|
||||
i3_nagbar_LIBS = $(XCB_LIBS) $(PANGO_LIBS)
|
||||
|
||||
i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o)
|
||||
|
||||
|
||||
i3-nagbar/%.o: i3-nagbar/%.c $(i3_nagbar_HEADERS)
|
||||
echo "[i3-nagbar] CC $<"
|
||||
$(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
i3-nagbar/i3-nagbar: libi3.a $(i3_nagbar_OBJECTS)
|
||||
echo "[i3-nagbar] Link i3-nagbar"
|
||||
$(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3_nagbar_LIBS)
|
||||
|
||||
install-i3-nagbar: i3-nagbar/i3-nagbar
|
||||
echo "[i3-nagbar] Install"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3-nagbar/i3-nagbar $(DESTDIR)$(PREFIX)/bin/
|
||||
|
||||
clean-i3-nagbar:
|
||||
echo "[i3-nagbar] Clean"
|
||||
rm -f $(i3_nagbar_OBJECTS) i3-nagbar/i3-nagbar
|
|
@ -30,7 +30,7 @@
|
|||
#include "i3-nagbar.h"
|
||||
|
||||
typedef struct {
|
||||
char *label;
|
||||
i3String *label;
|
||||
char *action;
|
||||
int16_t x;
|
||||
uint16_t width;
|
||||
|
@ -41,7 +41,7 @@ static xcb_pixmap_t pixmap;
|
|||
static xcb_gcontext_t pixmap_gc;
|
||||
static xcb_rectangle_t rect = { 0, 0, 600, 20 };
|
||||
static i3Font font;
|
||||
static char *prompt;
|
||||
static i3String *prompt;
|
||||
static button_t *buttons;
|
||||
static int buttoncnt;
|
||||
|
||||
|
@ -54,6 +54,27 @@ static uint32_t color_text; /* color of the text */
|
|||
|
||||
xcb_window_t root;
|
||||
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
|
||||
|
@ -132,7 +153,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
|||
|
||||
/* restore font color */
|
||||
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);
|
||||
|
||||
/* render close button */
|
||||
|
@ -159,7 +180,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
|||
|
||||
values[0] = 1;
|
||||
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);
|
||||
y -= w;
|
||||
|
||||
|
@ -190,7 +211,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
|||
values[0] = color_text;
|
||||
values[1] = 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;
|
||||
|
@ -216,7 +237,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
|
|||
}
|
||||
|
||||
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;
|
||||
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";
|
||||
|
||||
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) {
|
||||
switch (o) {
|
||||
|
@ -241,11 +262,11 @@ int main(int argc, char *argv[]) {
|
|||
return 0;
|
||||
case 'f':
|
||||
FREE(pattern);
|
||||
pattern = strdup(optarg);
|
||||
pattern = sstrdup(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
FREE(prompt);
|
||||
prompt = strdup(optarg);
|
||||
i3string_free(prompt);
|
||||
prompt = i3string_from_utf8(optarg);
|
||||
break;
|
||||
case 't':
|
||||
bar_type = (strcasecmp(optarg, "warning") == 0 ? TYPE_WARNING : TYPE_ERROR);
|
||||
|
@ -256,10 +277,10 @@ int main(int argc, char *argv[]) {
|
|||
return 0;
|
||||
case 'b':
|
||||
buttons = realloc(buttons, sizeof(button_t) * (buttoncnt + 1));
|
||||
buttons[buttoncnt].label = optarg;
|
||||
buttons[buttoncnt].label = i3string_from_utf8(optarg);
|
||||
buttons[buttoncnt].action = argv[optind];
|
||||
printf("button with label *%s* and action *%s*\n",
|
||||
buttons[buttoncnt].label,
|
||||
i3string_as_utf8(buttons[buttoncnt].label),
|
||||
buttons[buttoncnt].action);
|
||||
buttoncnt++;
|
||||
printf("now %d buttons\n", buttoncnt);
|
||||
|
@ -280,7 +301,7 @@ int main(int argc, char *argv[]) {
|
|||
#include "atoms.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;
|
||||
|
||||
if (bar_type == TYPE_ERROR) {
|
||||
|
@ -432,5 +453,7 @@ int main(int argc, char *argv[]) {
|
|||
free(event);
|
||||
}
|
||||
|
||||
FREE(pattern);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# Distributions/packagers should enhance this script with a
|
||||
# distribution-specific mechanism to find the preferred terminal emulator. On
|
||||
# Debian, there is the x-terminal-emulator symlink for example.
|
||||
for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm; do
|
||||
for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
|
||||
if which $terminal > /dev/null 2>&1; then
|
||||
exec $terminal "$@"
|
||||
fi
|
||||
|
|
14
i3.config
14
i3.config
|
@ -9,8 +9,14 @@
|
|||
# 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
|
||||
# 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
|
||||
floating_modifier Mod1
|
||||
|
@ -57,10 +63,10 @@ bindsym Mod1+v split v
|
|||
# enter fullscreen mode for the focused container
|
||||
bindsym Mod1+f fullscreen
|
||||
|
||||
# change container layout (stacked, tabbed, default)
|
||||
# change container layout (stacked, tabbed, toggle split)
|
||||
bindsym Mod1+s layout stacking
|
||||
bindsym Mod1+w layout tabbed
|
||||
bindsym Mod1+e layout default
|
||||
bindsym Mod1+e layout toggle split
|
||||
|
||||
# toggle tiling / floating
|
||||
bindsym Mod1+Shift+space floating toggle
|
||||
|
@ -103,7 +109,7 @@ bindsym Mod1+Shift+c reload
|
|||
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
||||
bindsym Mod1+Shift+r restart
|
||||
# exit i3 (logs you out of your X session)
|
||||
bindsym Mod1+Shift+e exit
|
||||
bindsym Mod1+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'"
|
||||
|
||||
# resize window (you can also use the mouse for that)
|
||||
mode "resize" {
|
||||
|
|
|
@ -10,8 +10,14 @@
|
|||
|
||||
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
|
||||
# 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
|
||||
floating_modifier $mod
|
||||
|
@ -58,10 +64,10 @@ bindcode $mod+55 split v
|
|||
# enter fullscreen mode for the focused container
|
||||
bindcode $mod+41 fullscreen
|
||||
|
||||
# change container layout (stacked, tabbed, default)
|
||||
# change container layout (stacked, tabbed, toggle split)
|
||||
bindcode $mod+39 layout stacking
|
||||
bindcode $mod+25 layout tabbed
|
||||
bindcode $mod+26 layout default
|
||||
bindcode $mod+26 layout toggle split
|
||||
|
||||
# toggle tiling / floating
|
||||
bindcode $mod+Shift+65 floating toggle
|
||||
|
@ -104,7 +110,7 @@ bindcode $mod+Shift+54 reload
|
|||
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
||||
bindcode $mod+Shift+27 restart
|
||||
# exit i3 (logs you out of your X session)
|
||||
bindcode $mod+Shift+26 exit
|
||||
bindcode $mod+Shift+26 exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'"
|
||||
|
||||
# resize window (you can also use the mouse for that)
|
||||
mode "resize" {
|
||||
|
|
|
@ -1,42 +1,10 @@
|
|||
TOPDIR=..
|
||||
all:
|
||||
$(MAKE) -C .. i3bar/i3bar
|
||||
|
||||
include $(TOPDIR)/common.mk
|
||||
|
||||
FILES:=$(wildcard src/*.c)
|
||||
FILES:=$(FILES:.c=.o)
|
||||
HEADERS:=$(wildcard include/*.h)
|
||||
|
||||
CPPFLAGS += -I$(TOPDIR)/include
|
||||
|
||||
all: i3bar doc
|
||||
|
||||
i3bar: $(TOPDIR)/libi3/libi3.a ${FILES}
|
||||
echo "[i3bar] LINK"
|
||||
$(CC) $(LDFLAGS) -o $@ $(filter-out libi3/libi3.a,$^) $(LIBS)
|
||||
|
||||
$(TOPDIR)/libi3/%.a: $(TOPDIR)/libi3/*.c
|
||||
$(MAKE) -C $(TOPDIR)/libi3
|
||||
|
||||
doc:
|
||||
echo ""
|
||||
echo "[i3bar] SUBDIR doc"
|
||||
$(MAKE) -C doc
|
||||
|
||||
src/%.o: src/%.c ${HEADERS}
|
||||
echo "[i3bar] CC $<"
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
install: all
|
||||
echo "[i3bar] INSTALL"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3bar $(DESTDIR)$(PREFIX)/bin
|
||||
install:
|
||||
$(MAKE) -C .. install-i3bar
|
||||
|
||||
clean:
|
||||
rm -f src/*.o
|
||||
$(MAKE) -C doc clean
|
||||
$(MAKE) -C .. clean-i3bar
|
||||
|
||||
distclean: clean
|
||||
rm -f i3bar
|
||||
$(MAKE) -C doc distclean
|
||||
|
||||
.PHONY: install clean distclean doc
|
||||
.PHONY: all install clean
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
all: i3bar.1
|
||||
|
||||
i3bar.1: i3bar.man
|
||||
echo "A2X i3bar"
|
||||
a2x --no-xmllint -f manpage i3bar.man
|
||||
clean:
|
||||
rm -f i3bar.xml i3bar.1 i3bar.html
|
||||
|
||||
distclean: clean
|
|
@ -0,0 +1,28 @@
|
|||
ALL_TARGETS += i3bar/i3bar
|
||||
INSTALL_TARGETS += install-i3bar
|
||||
CLEAN_TARGETS += clean-i3bar
|
||||
|
||||
i3bar_SOURCES := $(wildcard i3bar/src/*.c)
|
||||
i3bar_HEADERS := $(wildcard i3bar/include/*.h)
|
||||
i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
|
||||
i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS)
|
||||
|
||||
i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)
|
||||
|
||||
|
||||
i3bar/src/%.o: i3bar/src/%.c $(i3bar_HEADERS)
|
||||
echo "[i3bar] CC $<"
|
||||
$(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3bar_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -Ii3bar/include -c -o $@ $<
|
||||
|
||||
i3bar/i3bar: libi3.a $(i3bar_OBJECTS)
|
||||
echo "[i3bar] Link i3bar"
|
||||
$(CC) $(I3_LDFLAGS) $(LDFLAGS) -o $@ $(filter-out libi3.a,$^) $(LIBS) $(i3bar_LIBS)
|
||||
|
||||
install-i3bar: i3bar/i3bar
|
||||
echo "[i3bar] Install"
|
||||
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) -m 0755 i3bar/i3bar $(DESTDIR)$(PREFIX)/bin/
|
||||
|
||||
clean-i3bar:
|
||||
echo "[i3bar] Clean"
|
||||
rm -f $(i3bar_OBJECTS) i3bar/i3bar
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -24,25 +24,25 @@ void start_child(char *command);
|
|||
* 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
|
||||
* free()s the stdin- and sigchild-watchers
|
||||
*
|
||||
*/
|
||||
void kill_child();
|
||||
void kill_child(void);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*
|
||||
*/
|
||||
void cont_child();
|
||||
void cont_child(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <stdbool.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xproto.h>
|
||||
#include "libi3.h"
|
||||
#include "queue.h"
|
||||
|
||||
typedef struct rect_t rect;
|
||||
|
@ -29,15 +30,10 @@ struct rect_t {
|
|||
/* This data structure represents one JSON dictionary, multiple of these make
|
||||
* up one status line. */
|
||||
struct status_block {
|
||||
char *full_text;
|
||||
i3String *full_text;
|
||||
|
||||
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
|
||||
* only temporarily used in refresh_statusline(). */
|
||||
uint32_t width;
|
||||
|
@ -56,5 +52,6 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head;
|
|||
#include "xcb.h"
|
||||
#include "config.h"
|
||||
#include "libi3.h"
|
||||
#include "determine_json_version.h"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* determine_json_version.c: Determines the JSON protocol version based on the
|
||||
* first line of input from a child program.
|
||||
*
|
||||
*/
|
||||
#ifndef DETERMINE_JSON_VERSION_H_
|
||||
#define DETERMINE_JSON_VERSION_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Determines the JSON i3bar protocol version from the given buffer. In case
|
||||
* the buffer does not contain valid JSON, or no version field is found, this
|
||||
* function returns -1. The amount of bytes consumed by parsing the header is
|
||||
* returned in *consumed (if non-NULL).
|
||||
*
|
||||
* The return type is an int32_t to avoid machines with different sizes of
|
||||
* 'int' to allow different values here. It’s highly unlikely we ever exceed
|
||||
* even an int8_t, but still…
|
||||
*
|
||||
*/
|
||||
int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed);
|
||||
|
||||
#endif
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -23,7 +23,7 @@ int init_connection(const char *socket_path);
|
|||
* Destroy the connection to i3.
|
||||
*
|
||||
*/
|
||||
void destroy_connection();
|
||||
void destroy_connection(void);
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
void subscribe_events();
|
||||
void subscribe_events(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ void parse_outputs_json(char* json);
|
|||
* Initiate the output-list
|
||||
*
|
||||
*/
|
||||
void init_outputs();
|
||||
void init_outputs(void);
|
||||
|
||||
/*
|
||||
* Returns the output with the given name
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
#include "queue.h"
|
||||
|
||||
/* Get the maximum/minimum of x and y */
|
||||
#undef MAX
|
||||
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
||||
#undef MIN
|
||||
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
/* Securely free p */
|
||||
|
@ -51,6 +53,11 @@
|
|||
} \
|
||||
} 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 { \
|
||||
fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -28,13 +28,11 @@ void parse_workspaces_json(char *json);
|
|||
* free() all workspace data-structures
|
||||
*
|
||||
*/
|
||||
void free_workspaces();
|
||||
void free_workspaces(void);
|
||||
|
||||
struct i3_ws {
|
||||
int num; /* The internal number of the ws */
|
||||
char *name; /* The name (in utf8) 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 */
|
||||
i3String *name; /* The name of the ws */
|
||||
int name_width; /* The rendered width of the name */
|
||||
bool visible; /* If the ws is currently visible on an output */
|
||||
bool focused; /* If the ws is currently focused */
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -69,13 +69,13 @@ void init_colors(const struct xcb_color_strings_t *colors);
|
|||
* 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
|
||||
*
|
||||
*/
|
||||
void get_atoms();
|
||||
void get_atoms(void);
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
void realloc_sl_buffer();
|
||||
void realloc_sl_buffer(void);
|
||||
|
||||
/*
|
||||
* Reconfigure all bars and create new for newly activated outputs
|
||||
*
|
||||
*/
|
||||
void reconfig_windows();
|
||||
void reconfig_windows(void);
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
void redraw_bars();
|
||||
void redraw_bars(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -56,7 +56,7 @@ char *statusline_buffer = NULL;
|
|||
* Stop and free() the stdin- and sigchild-watchers
|
||||
*
|
||||
*/
|
||||
void cleanup() {
|
||||
void cleanup(void) {
|
||||
if (stdin_io != NULL) {
|
||||
ev_io_stop(main_loop, stdin_io);
|
||||
FREE(stdin_io);
|
||||
|
@ -80,7 +80,7 @@ static int stdin_start_array(void *context) {
|
|||
struct status_block *first;
|
||||
while (!TAILQ_EMPTY(&statusline_head)) {
|
||||
first = TAILQ_FIRST(&statusline_head);
|
||||
FREE(first->full_text);
|
||||
I3STRING_FREE(first->full_text);
|
||||
FREE(first->color);
|
||||
TAILQ_REMOVE(&statusline_head, first, blocks);
|
||||
free(first);
|
||||
|
@ -116,7 +116,7 @@ static int stdin_string(void *context, const unsigned char *val, unsigned int le
|
|||
#endif
|
||||
parser_ctx *ctx = context;
|
||||
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) {
|
||||
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),
|
||||
* i3bar doesn’t crash and the user gets an annoying message. */
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ static int stdin_end_array(void *context) {
|
|||
DLOG("dumping statusline:\n");
|
||||
struct status_block *current;
|
||||
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("end of dump\n");
|
||||
|
@ -192,19 +192,18 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
|||
if (first_line) {
|
||||
DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer);
|
||||
/* Detect whether this is JSON or plain text. */
|
||||
plaintext = (strncasecmp((char*)buffer, "{\"version\":", strlen("{\"version\":")) != 0);
|
||||
unsigned int consumed = 0;
|
||||
/* At the moment, we don’t care for the version. This might change
|
||||
* in the future, but for now, we just discard it. */
|
||||
plaintext = (determine_json_version(buffer, buffer_len, &consumed) == -1);
|
||||
if (plaintext) {
|
||||
/* In case of plaintext, we just add a single block and change its
|
||||
* full_text pointer later. */
|
||||
struct status_block *new_block = scalloc(sizeof(struct status_block));
|
||||
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
||||
} else {
|
||||
/* At the moment, we don’t care for the version. This might change
|
||||
* in the future, but for now, we just discard it. */
|
||||
while (*json_input != '\n' && *json_input != '\0') {
|
||||
json_input++;
|
||||
rec--;
|
||||
}
|
||||
json_input += consumed;
|
||||
rec -= consumed;
|
||||
}
|
||||
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",
|
||||
status, rec, json_input);
|
||||
}
|
||||
free(buffer);
|
||||
} else {
|
||||
struct status_block *first = TAILQ_FIRST(&statusline_head);
|
||||
/* 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
|
||||
* time. */
|
||||
if (buffer[rec-1] == '\n' || buffer[rec-1] == '\r')
|
||||
buffer[rec-1] = '\0';
|
||||
else buffer[rec] = '\0';
|
||||
first->full_text = (char*)buffer;
|
||||
first->full_text = i3string_from_utf8((const char *)buffer);
|
||||
}
|
||||
free(buffer);
|
||||
draw_bars();
|
||||
}
|
||||
|
||||
|
@ -328,7 +327,7 @@ void start_child(char *command) {
|
|||
* 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) {
|
||||
kill(child_pid, SIGCONT);
|
||||
kill(child_pid, SIGTERM);
|
||||
|
@ -340,7 +339,7 @@ void kill_child_at_exit() {
|
|||
* free()s the stdin- and sigchild-watchers
|
||||
*
|
||||
*/
|
||||
void kill_child() {
|
||||
void kill_child(void) {
|
||||
if (child_pid != 0) {
|
||||
kill(child_pid, SIGCONT);
|
||||
kill(child_pid, SIGTERM);
|
||||
|
@ -355,7 +354,7 @@ void kill_child() {
|
|||
* Sends a SIGSTOP to the child-process (if existent)
|
||||
*
|
||||
*/
|
||||
void stop_child() {
|
||||
void stop_child(void) {
|
||||
if (child_pid != 0) {
|
||||
kill(child_pid, SIGSTOP);
|
||||
}
|
||||
|
@ -365,7 +364,7 @@ void stop_child() {
|
|||
* Sends a SIGCONT to the child-process (if existent)
|
||||
*
|
||||
*/
|
||||
void cont_child() {
|
||||
void cont_child(void) {
|
||||
if (child_pid != 0) {
|
||||
kill(child_pid, SIGCONT);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3bar - an xcb-based status- and ws-bar for i3
|
||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||
*
|
||||
* determine_json_version.c: Determines the JSON protocol version based on the
|
||||
* first line of input from a child program.
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
#include <ev.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <yajl/yajl_common.h>
|
||||
#include <yajl/yajl_parse.h>
|
||||
#include <yajl/yajl_version.h>
|
||||
|
||||
static bool version_key;
|
||||
static int32_t version_number;
|
||||
|
||||
#if YAJL_MAJOR >= 2
|
||||
static int version_integer(void *ctx, long long val) {
|
||||
#else
|
||||
static int version_integer(void *ctx, long val) {
|
||||
#endif
|
||||
if (version_key)
|
||||
version_number = (uint32_t)val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if YAJL_MAJOR >= 2
|
||||
static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
|
||||
#else
|
||||
static int version_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
|
||||
#endif
|
||||
version_key = (stringlen == strlen("version") &&
|
||||
strncmp((const char*)stringval, "version", strlen("version")) == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static yajl_callbacks version_callbacks = {
|
||||
NULL, /* null */
|
||||
NULL, /* boolean */
|
||||
&version_integer,
|
||||
NULL, /* double */
|
||||
NULL, /* number */
|
||||
NULL, /* string */
|
||||
NULL, /* start_map */
|
||||
&version_map_key,
|
||||
NULL, /* end_map */
|
||||
NULL, /* start_array */
|
||||
NULL /* end_array */
|
||||
};
|
||||
|
||||
/*
|
||||
* Determines the JSON i3bar protocol version from the given buffer. In case
|
||||
* the buffer does not contain valid JSON, or no version field is found, this
|
||||
* function returns -1. The amount of bytes consumed by parsing the header is
|
||||
* returned in *consumed (if non-NULL).
|
||||
*
|
||||
* The return type is an int32_t to avoid machines with different sizes of
|
||||
* 'int' to allow different values here. It’s highly unlikely we ever exceed
|
||||
* even an int8_t, but still…
|
||||
*
|
||||
*/
|
||||
int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed) {
|
||||
#if YAJL_MAJOR >= 2
|
||||
yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL);
|
||||
/* Allow trailing garbage. yajl 1 always behaves that way anyways, but for
|
||||
* yajl 2, we need to be explicit. */
|
||||
yajl_config(handle, yajl_allow_trailing_garbage, 1);
|
||||
#else
|
||||
yajl_parser_config parse_conf = { 0, 0 };
|
||||
|
||||
yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, NULL);
|
||||
#endif
|
||||
|
||||
version_key = false;
|
||||
version_number = -1;
|
||||
|
||||
yajl_status state = yajl_parse(handle, buffer, length);
|
||||
if (state != yajl_status_ok) {
|
||||
version_number = -1;
|
||||
if (consumed != NULL)
|
||||
*consumed = 0;
|
||||
} else {
|
||||
if (consumed != NULL)
|
||||
*consumed = yajl_get_bytes_consumed(handle);
|
||||
}
|
||||
|
||||
yajl_free(handle);
|
||||
|
||||
return version_number;
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -286,7 +286,7 @@ int init_connection(const char *socket_path) {
|
|||
/*
|
||||
* Destroy the connection to i3.
|
||||
*/
|
||||
void destroy_connection() {
|
||||
void destroy_connection(void) {
|
||||
close(i3_connection->fd);
|
||||
ev_io_stop(main_loop, i3_connection);
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ void destroy_connection() {
|
|||
* Subscribe to all the i3-events, we need
|
||||
*
|
||||
*/
|
||||
void subscribe_events() {
|
||||
void subscribe_events(void) {
|
||||
if (config.disable_ws) {
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\" ]");
|
||||
} else {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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>
|
||||
|
@ -17,6 +17,26 @@
|
|||
|
||||
#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 ~
|
||||
*
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -266,7 +266,7 @@ yajl_callbacks outputs_callbacks = {
|
|||
* Initiate the output-list
|
||||
*
|
||||
*/
|
||||
void init_outputs() {
|
||||
void init_outputs(void) {
|
||||
outputs = smalloc(sizeof(struct outputs_head));
|
||||
SLIST_INIT(outputs);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -114,23 +114,16 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, unsigne
|
|||
|
||||
if (!strcmp(params->cur_key, "name")) {
|
||||
/* Save the name */
|
||||
params->workspaces_walk->name = smalloc(sizeof(const unsigned char) * (len + 1));
|
||||
strncpy(params->workspaces_walk->name, (const char*) val, len);
|
||||
params->workspaces_walk->name[len] = '\0';
|
||||
params->workspaces_walk->name = i3string_from_utf8_with_length((const char *)val, len);
|
||||
|
||||
/* Convert the name to ucs2, save its length in glyphs and calculate 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;
|
||||
/* Save its rendered width */
|
||||
params->workspaces_walk->name_width =
|
||||
predict_text_width((char *)params->workspaces_walk->ucs2_name,
|
||||
params->workspaces_walk->name_glyphs, true);
|
||||
predict_text_width(params->workspaces_walk->name);
|
||||
|
||||
DLOG("Got Workspace %s, name_width: %d, glyphs: %d\n",
|
||||
params->workspaces_walk->name,
|
||||
DLOG("Got Workspace %s, name_width: %d, glyphs: %zu\n",
|
||||
i3string_as_utf8(params->workspaces_walk->name),
|
||||
params->workspaces_walk->name_width,
|
||||
params->workspaces_walk->name_glyphs);
|
||||
i3string_get_num_glyphs(params->workspaces_walk->name));
|
||||
FREE(params->cur_key);
|
||||
|
||||
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.
|
||||
*
|
||||
*/
|
||||
void free_workspaces() {
|
||||
void free_workspaces(void) {
|
||||
i3_output *outputs_walk;
|
||||
if (outputs == NULL) {
|
||||
return;
|
||||
|
@ -279,8 +272,7 @@ void free_workspaces() {
|
|||
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
|
||||
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
||||
FREE(ws_walk->name);
|
||||
FREE(ws_walk->ucs2_name);
|
||||
I3STRING_FREE(ws_walk->name);
|
||||
}
|
||||
FREE_TAILQ(outputs_walk->workspaces, i3_ws);
|
||||
}
|
||||
|
|
106
i3bar/src/xcb.c
106
i3bar/src/xcb.c
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
*
|
||||
|
@ -47,7 +47,7 @@ xcb_atom_t atoms[NUM_ATOMS];
|
|||
/* Variables, that are the same for all functions at all times */
|
||||
xcb_connection_t *xcb_connection;
|
||||
int screen;
|
||||
xcb_screen_t *xcb_screen;
|
||||
xcb_screen_t *root_screen;
|
||||
xcb_window_t xcb_root;
|
||||
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
void refresh_statusline() {
|
||||
void refresh_statusline(void) {
|
||||
struct status_block *block;
|
||||
|
||||
uint32_t old_statusline_width = statusline_width;
|
||||
statusline_width = 0;
|
||||
|
||||
/* Convert all blocks from UTF-8 to UCS-2 and predict the text width (in
|
||||
* pixels). */
|
||||
/* Predict the text width of all blocks (in pixels). */
|
||||
TAILQ_FOREACH(block, &statusline_head, blocks) {
|
||||
if (strlen(block->full_text) == 0)
|
||||
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||
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((char*)block->ucs2_full_text, block->glyph_count_full_text, true);
|
||||
block->width = predict_text_width(block->full_text);
|
||||
/* If this is not the last block, add some pixels for a separator. */
|
||||
if (TAILQ_NEXT(block, blocks) != NULL)
|
||||
block->width += 9;
|
||||
|
@ -130,24 +128,23 @@ void refresh_statusline() {
|
|||
|
||||
/* 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 */
|
||||
if (statusline_width > xcb_screen->width_in_pixels &&
|
||||
if (statusline_width > root_screen->width_in_pixels &&
|
||||
statusline_width > old_statusline_width)
|
||||
realloc_sl_buffer();
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Draw the text of each block. */
|
||||
uint32_t x = 0;
|
||||
TAILQ_FOREACH(block, &statusline_head, blocks) {
|
||||
if (strlen(block->full_text) == 0)
|
||||
if (i3string_get_num_bytes(block->full_text) == 0)
|
||||
continue;
|
||||
|
||||
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
|
||||
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg);
|
||||
draw_text((char*)block->ucs2_full_text, block->glyph_count_full_text,
|
||||
true, statusline_pm, statusline_ctx, x, 0, block->width);
|
||||
draw_text(block->full_text, statusline_pm, statusline_ctx, x, 0, block->width);
|
||||
x += block->width;
|
||||
|
||||
if (TAILQ_NEXT(block, blocks) != NULL) {
|
||||
|
@ -157,8 +154,6 @@ void refresh_statusline() {
|
|||
statusline_ctx, 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)
|
||||
*
|
||||
*/
|
||||
void hide_bars() {
|
||||
void hide_bars(void) {
|
||||
if (!config.hide_on_modifier) {
|
||||
return;
|
||||
}
|
||||
|
@ -185,7 +180,7 @@ void hide_bars() {
|
|||
* Unhides all bars (maps them)
|
||||
*
|
||||
*/
|
||||
void unhide_bars() {
|
||||
void unhide_bars(void) {
|
||||
if (!config.hide_on_modifier) {
|
||||
return;
|
||||
}
|
||||
|
@ -326,7 +321,8 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
* buffer, then we copy character by character. */
|
||||
int num_quotes = 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 == '"')
|
||||
num_quotes++;
|
||||
/* While we’re looping through the name anyway, we can save one
|
||||
|
@ -341,11 +337,11 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
for (inpos = 0, outpos = strlen("workspace \"");
|
||||
inpos < namelen;
|
||||
inpos++, outpos++) {
|
||||
if (cur_ws->name[inpos] == '"') {
|
||||
if (utf8_name[inpos] == '"') {
|
||||
buffer[outpos] = '\\';
|
||||
outpos++;
|
||||
}
|
||||
buffer[outpos] = cur_ws->name[inpos];
|
||||
buffer[outpos] = utf8_name[inpos];
|
||||
}
|
||||
buffer[outpos] = '"';
|
||||
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.
|
||||
*
|
||||
*/
|
||||
static void configure_trayclients() {
|
||||
static void configure_trayclients(void) {
|
||||
trayclient *trayclient;
|
||||
i3_output *output;
|
||||
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);
|
||||
#include "xcb_atoms.def"
|
||||
|
||||
xcb_screen = xcb_aux_get_screen(xcb_connection, screen);
|
||||
xcb_root = xcb_screen->root;
|
||||
root_screen = xcb_aux_get_screen(xcb_connection, screen);
|
||||
xcb_root = root_screen->root;
|
||||
|
||||
/* 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 */
|
||||
|
@ -852,11 +848,11 @@ char *init_xcb_early() {
|
|||
|
||||
statusline_pm = xcb_generate_id(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,
|
||||
xcb_root,
|
||||
xcb_screen->width_in_pixels,
|
||||
xcb_screen->height_in_pixels);
|
||||
root_screen->width_in_pixels,
|
||||
root_screen->height_in_pixels);
|
||||
|
||||
|
||||
/* 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.
|
||||
*
|
||||
*/
|
||||
void init_tray() {
|
||||
void init_tray(void) {
|
||||
DLOG("Initializing system tray functionality\n");
|
||||
/* request the tray manager atom for the X11 display we are running on */
|
||||
char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
|
||||
|
@ -984,14 +980,14 @@ void init_tray() {
|
|||
uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT;
|
||||
uint32_t selval[] = { 1 };
|
||||
xcb_create_window(xcb_connection,
|
||||
xcb_screen->root_depth,
|
||||
root_screen->root_depth,
|
||||
selwin,
|
||||
xcb_root,
|
||||
-1, -1,
|
||||
1, 1,
|
||||
1,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
xcb_screen->root_visual,
|
||||
root_screen->root_visual,
|
||||
selmask,
|
||||
selval);
|
||||
|
||||
|
@ -1059,7 +1055,7 @@ void init_tray() {
|
|||
* Called once, before the program terminates.
|
||||
*
|
||||
*/
|
||||
void clean_xcb() {
|
||||
void clean_xcb(void) {
|
||||
i3_output *o_walk;
|
||||
free_workspaces();
|
||||
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
|
||||
*
|
||||
*/
|
||||
void get_atoms() {
|
||||
void get_atoms(void) {
|
||||
xcb_intern_atom_reply_t *reply;
|
||||
#define ATOM_DO(name) reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \
|
||||
if (reply == NULL) { \
|
||||
|
@ -1149,17 +1145,17 @@ void destroy_window(i3_output *output) {
|
|||
* Reallocate the statusline-buffer
|
||||
*
|
||||
*/
|
||||
void realloc_sl_buffer() {
|
||||
DLOG("Re-allocating statusline-buffer, statusline_width = %d, xcb_screen->width_in_pixels = %d\n",
|
||||
statusline_width, xcb_screen->width_in_pixels);
|
||||
void realloc_sl_buffer(void) {
|
||||
DLOG("Re-allocating statusline-buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n",
|
||||
statusline_width, root_screen->width_in_pixels);
|
||||
xcb_free_pixmap(xcb_connection, statusline_pm);
|
||||
statusline_pm = xcb_generate_id(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,
|
||||
xcb_root,
|
||||
MAX(xcb_screen->width_in_pixels, statusline_width),
|
||||
xcb_screen->height_in_pixels);
|
||||
MAX(root_screen->width_in_pixels, statusline_width),
|
||||
root_screen->height_in_pixels);
|
||||
|
||||
uint32_t mask = XCB_GC_FOREGROUND;
|
||||
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
|
||||
*
|
||||
*/
|
||||
void reconfig_windows() {
|
||||
void reconfig_windows(void) {
|
||||
uint32_t mask;
|
||||
uint32_t values[5];
|
||||
static bool tray_configured = false;
|
||||
|
@ -1229,20 +1225,20 @@ void reconfig_windows() {
|
|||
values[2] |= XCB_EVENT_MASK_BUTTON_PRESS;
|
||||
}
|
||||
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
|
||||
xcb_screen->root_depth,
|
||||
root_screen->root_depth,
|
||||
walk->bar,
|
||||
xcb_root,
|
||||
walk->rect.x, walk->rect.y + walk->rect.h - font.height - 6,
|
||||
walk->rect.w, font.height + 6,
|
||||
1,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
xcb_screen->root_visual,
|
||||
root_screen->root_visual,
|
||||
mask,
|
||||
values);
|
||||
|
||||
/* The double-buffer we use to render stuff off-screen */
|
||||
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
||||
xcb_screen->root_depth,
|
||||
root_screen->root_depth,
|
||||
walk->buffer,
|
||||
walk->bar,
|
||||
walk->rect.w,
|
||||
|
@ -1382,7 +1378,7 @@ void reconfig_windows() {
|
|||
|
||||
DLOG("Recreating buffer for output %s", walk->name);
|
||||
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
|
||||
xcb_screen->root_depth,
|
||||
root_screen->root_depth,
|
||||
walk->buffer,
|
||||
walk->bar,
|
||||
walk->rect.w,
|
||||
|
@ -1402,7 +1398,7 @@ void reconfig_windows() {
|
|||
* Render the bars, with buttons and statusline
|
||||
*
|
||||
*/
|
||||
void draw_bars() {
|
||||
void draw_bars(void) {
|
||||
DLOG("Drawing Bars...\n");
|
||||
int i = 0;
|
||||
|
||||
|
@ -1464,8 +1460,11 @@ void draw_bars() {
|
|||
}
|
||||
|
||||
i3_ws *ws_walk;
|
||||
static char *last_urgent_ws = NULL;
|
||||
bool has_urgent = false, walks_away = true;
|
||||
|
||||
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
||||
DLOG("Drawing Button for WS %s at x = %d, len = %d\n", ws_walk->name, i, ws_walk->name_width);
|
||||
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 bg_color = colors.inactive_ws_bg;
|
||||
uint32_t border_color = colors.inactive_ws_border;
|
||||
|
@ -1478,13 +1477,20 @@ void draw_bars() {
|
|||
fg_color = colors.focus_ws_fg;
|
||||
bg_color = colors.focus_ws_bg;
|
||||
border_color = colors.focus_ws_border;
|
||||
if (last_urgent_ws && strcmp(i3string_as_utf8(ws_walk->name), last_urgent_ws) == 0)
|
||||
walks_away = false;
|
||||
}
|
||||
}
|
||||
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;
|
||||
bg_color = colors.urgent_ws_bg;
|
||||
border_color = colors.urgent_ws_border;
|
||||
has_urgent = true;
|
||||
if (!ws_walk->focused) {
|
||||
FREE(last_urgent_ws);
|
||||
last_urgent_ws = sstrdup(i3string_as_utf8(ws_walk->name));
|
||||
}
|
||||
/* The urgent-hint should get noticed, so we unhide the bars shortly */
|
||||
unhide_bars();
|
||||
}
|
||||
|
@ -1512,11 +1518,15 @@ void draw_bars() {
|
|||
1,
|
||||
&rect);
|
||||
set_font_colors(outputs_walk->bargc, fg_color, bg_color);
|
||||
draw_text((char*)ws_walk->ucs2_name, ws_walk->name_glyphs, true,
|
||||
outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width);
|
||||
draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width);
|
||||
i += 10 + ws_walk->name_width + 1;
|
||||
}
|
||||
|
||||
if (!has_urgent && !mod_pressed && walks_away) {
|
||||
FREE(last_urgent_ws);
|
||||
hide_bars();
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
|
||||
|
@ -1527,7 +1537,7 @@ void draw_bars() {
|
|||
* Redraw the bars, i.e. simply copy the buffer to the barwindow
|
||||
*
|
||||
*/
|
||||
void redraw_bars() {
|
||||
void redraw_bars(void) {
|
||||
i3_output *outputs_walk;
|
||||
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||
if (!outputs_walk->active) {
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "i3.h"
|
||||
#include "x.h"
|
||||
#include "click.h"
|
||||
#include "key_press.h"
|
||||
#include "floating.h"
|
||||
#include "config.h"
|
||||
#include "handlers.h"
|
||||
|
@ -79,5 +80,6 @@
|
|||
#include "commands.h"
|
||||
#include "commands_parser.h"
|
||||
#include "fake_outputs.h"
|
||||
#include "display_version.h"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,3 +27,4 @@ xmacro(I3_SOCKET_PATH)
|
|||
xmacro(I3_CONFIG_PATH)
|
||||
xmacro(I3_SYNC)
|
||||
xmacro(I3_SHMLOG_PATH)
|
||||
xmacro(I3_PID)
|
||||
|
|
|
@ -200,11 +200,17 @@ void cmd_fullscreen(I3_CMD, char *fullscreen_mode);
|
|||
void cmd_move_direction(I3_CMD, char *direction, char *move_px);
|
||||
|
||||
/**
|
||||
* Implementation of 'layout default|stacked|stacking|tabbed'.
|
||||
* Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
|
||||
*
|
||||
*/
|
||||
void cmd_layout(I3_CMD, char *layout_str);
|
||||
|
||||
/**
|
||||
* Implementation of 'layout toggle [all|split]'.
|
||||
*
|
||||
*/
|
||||
void cmd_layout_toggle(I3_CMD, char *toggle_mode);
|
||||
|
||||
/**
|
||||
* Implementaiton of 'exit'.
|
||||
*
|
||||
|
|
|
@ -221,6 +221,12 @@ Con *con_descend_direction(Con *con, direction_t direction);
|
|||
*/
|
||||
Rect con_border_style_rect(Con *con);
|
||||
|
||||
/**
|
||||
* Returns adjacent borders of the window. We need this if hide_edge_borders is
|
||||
* enabled.
|
||||
*/
|
||||
adjacent_t con_adjacent_borders(Con *con);
|
||||
|
||||
/**
|
||||
* Use this function to get a container’s border style. This is important
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* This function toggles the layout of a given container. toggle_mode can be
|
||||
* either 'default' (toggle only between stacked/tabbed/last_split_layout),
|
||||
* 'split' (toggle only between splitv/splith) or 'all' (toggle between all
|
||||
* layouts).
|
||||
*
|
||||
*/
|
||||
void con_toggle_layout(Con *con, const char *toggle_mode);
|
||||
|
||||
/**
|
||||
* Determines the minimum size of the given con by looking at its children (for
|
||||
* split/stacked/tabbed cons). Will be called when resizing floating cons
|
||||
|
@ -255,4 +270,27 @@ void con_set_layout(Con *con, int layout);
|
|||
*/
|
||||
Rect con_minimum_size(Con *con);
|
||||
|
||||
/**
|
||||
* Returns true if changing the focus to con would be allowed considering
|
||||
* the fullscreen focus constraints. Specifically, if a fullscreen container or
|
||||
* any of its descendants is focused, this function returns true if and only if
|
||||
* focusing con would mean that focus would still be visible on screen, i.e.,
|
||||
* the newly focused container would not be obscured by a fullscreen container.
|
||||
*
|
||||
* In the simplest case, if a fullscreen container or any of its descendants is
|
||||
* fullscreen, this functions returns true if con is the fullscreen container
|
||||
* itself or any of its descendants, as this means focus wouldn't escape the
|
||||
* boundaries of the fullscreen container.
|
||||
*
|
||||
* In case the fullscreen container is of type CF_OUTPUT, this function returns
|
||||
* true if con is on a different workspace, as focus wouldn't be obscured by
|
||||
* the fullscreen container that is constrained to a different workspace.
|
||||
*
|
||||
* Note that this same logic can be applied to moving containers. If a
|
||||
* container can be focused under the fullscreen focus constraints, it can also
|
||||
* become a parent or sibling to the currently focused container.
|
||||
*
|
||||
*/
|
||||
bool con_fullscreen_permits_focusing(Con *con);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -108,6 +108,12 @@ struct Config {
|
|||
* It is not planned to add any different focus models. */
|
||||
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.
|
||||
* 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
|
||||
|
@ -306,7 +312,7 @@ void switch_mode(const char *new_mode);
|
|||
* 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.
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <pcre.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "libi3.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 */
|
||||
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 {
|
||||
BIND_NONE = 0,
|
||||
BIND_SHIFT = XCB_MOD_MASK_SHIFT, /* (1 << 0) */
|
||||
|
@ -160,6 +168,9 @@ struct Startup_Sequence {
|
|||
char *workspace;
|
||||
/** libstartup-notification context for this launch */
|
||||
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;
|
||||
};
|
||||
|
@ -189,6 +200,20 @@ struct regex {
|
|||
*
|
||||
*/
|
||||
struct Binding {
|
||||
/** If true, the binding should be executed upon a KeyRelease event, not a
|
||||
* KeyPress (the default). */
|
||||
enum {
|
||||
/* This binding will only be executed upon KeyPress events */
|
||||
B_UPON_KEYPRESS = 0,
|
||||
/* This binding will be executed either upon a KeyRelease event, or… */
|
||||
B_UPON_KEYRELEASE = 1,
|
||||
/* …upon a KeyRelease event, even if the modifiers don’t match. This
|
||||
* state is triggered from get_binding() when the corresponding
|
||||
* KeyPress (!) happens, so that users can release the modifier keys
|
||||
* before releasing the actual key. */
|
||||
B_UPON_KEYRELEASE_IGNORE_MODS = 2,
|
||||
} release;
|
||||
|
||||
/** Symbol the user specified in configfile, if any. This needs to be
|
||||
* stored with the binding to be able to re-convert it into a keycode
|
||||
* if the keyboard mapping changes (using Xmodmap for example) */
|
||||
|
@ -280,9 +305,8 @@ struct Window {
|
|||
char *class_class;
|
||||
char *class_instance;
|
||||
|
||||
/** The name of the window as it will be passed to X11 (in UCS2 if the
|
||||
* application supports _NET_WM_NAME, in COMPOUND_TEXT otherwise). */
|
||||
char *name_x;
|
||||
/** The name of the window. */
|
||||
i3String *name;
|
||||
|
||||
/** The WM_WINDOW_ROLE of this window (for example, the pidgin buddy window
|
||||
* 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 */
|
||||
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 */
|
||||
bool uses_net_wm_name;
|
||||
|
||||
|
@ -423,6 +440,8 @@ struct Assignment {
|
|||
*/
|
||||
struct Con {
|
||||
bool mapped;
|
||||
/** whether this is a split container or not */
|
||||
bool split;
|
||||
enum {
|
||||
CT_ROOT = 0,
|
||||
CT_OUTPUT = 1,
|
||||
|
@ -431,7 +450,6 @@ struct Con {
|
|||
CT_WORKSPACE = 4,
|
||||
CT_DOCKAREA = 5
|
||||
} type;
|
||||
orientation_t orientation;
|
||||
struct Con *parent;
|
||||
|
||||
struct Rect rect;
|
||||
|
@ -496,7 +514,29 @@ struct Con {
|
|||
TAILQ_HEAD(swallow_head, Match) swallow_head;
|
||||
|
||||
enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode;
|
||||
enum { L_DEFAULT = 0, L_STACKED = 1, L_TABBED = 2, L_DOCKAREA = 3, L_OUTPUT = 4 } layout;
|
||||
/* 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;
|
||||
/** 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
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||
*
|
||||
* display_version.c: displays the running i3 version, runs as part of
|
||||
* i3 --moreversion.
|
||||
*/
|
||||
#ifndef _DISPLAY_VERSION_H
|
||||
#define _DISPLAY_VERSION_H
|
||||
|
||||
/**
|
||||
* Connects to i3 to find out the currently running version. Useful since it
|
||||
* might be different from the version compiled into this binary (maybe the
|
||||
* user didn’t correctly install i3 or forgot te restart it).
|
||||
*
|
||||
* The output looks like this:
|
||||
* Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804)
|
||||
*
|
||||
* The i3 binary you just called: /home/michael/i3/i3
|
||||
* The i3 binary you are running: /home/michael/i3/i3
|
||||
*
|
||||
*/
|
||||
void display_running_version(void);
|
||||
|
||||
#endif
|
|
@ -2,7 +2,7 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* 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
|
||||
* for the IPC interface to i3 (see docs/ipc for more information).
|
||||
|
@ -40,6 +40,9 @@
|
|||
/** Request the configuration for a specific 'bar' */
|
||||
#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
|
||||
*
|
||||
|
@ -66,6 +69,9 @@
|
|||
/** Bar config reply type */
|
||||
#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.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||
*
|
||||
* key_press.c: key press handler
|
||||
*
|
||||
*/
|
||||
#ifndef _KEY_PRESS_H
|
||||
#define _KEY_PRESS_H
|
||||
|
||||
/**
|
||||
* There was a key press. We compare this key code with our bindings table and pass
|
||||
* the bound action to parse_command().
|
||||
*
|
||||
*/
|
||||
void handle_key_press(xcb_key_press_event_t *event);
|
||||
|
||||
/**
|
||||
* Kills the commanderror i3-nagbar process, if any.
|
||||
*
|
||||
* Called when reloading/restarting, since the user probably fixed his wrong
|
||||
* keybindings.
|
||||
*
|
||||
* If wait_for_it is set (restarting), this function will waitpid(), otherwise,
|
||||
* ev is assumed to handle it (reloading).
|
||||
*
|
||||
*/
|
||||
void kill_commanderror_nagbar(bool wait_for_it);
|
||||
|
||||
#endif
|
140
include/libi3.h
140
include/libi3.h
|
@ -18,6 +18,16 @@
|
|||
#include <xcb/xproto.h>
|
||||
#include <xcb/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;
|
||||
|
||||
/**
|
||||
|
@ -27,23 +37,44 @@ typedef struct Font i3Font;
|
|||
*
|
||||
*/
|
||||
struct Font {
|
||||
/** 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;
|
||||
/** The type of font */
|
||||
enum {
|
||||
FONT_TYPE_NONE = 0,
|
||||
FONT_TYPE_XCB,
|
||||
FONT_TYPE_PANGO
|
||||
} type;
|
||||
|
||||
/** The height of the font, built from font_ascent + font_descent */
|
||||
int height;
|
||||
|
||||
union {
|
||||
struct {
|
||||
/** The xcb-id for the font */
|
||||
xcb_font_t id;
|
||||
|
||||
/** Font information gathered from the server */
|
||||
xcb_query_font_reply_t *info;
|
||||
|
||||
/** Font table for this font (may be NULL) */
|
||||
xcb_charinfo_t *table;
|
||||
} xcb;
|
||||
|
||||
#if PANGO_SUPPORT
|
||||
/** The pango font description */
|
||||
PangoFontDescription *pango_desc;
|
||||
#endif
|
||||
} specific;
|
||||
};
|
||||
|
||||
/* Since this file also gets included by utilities which don’t use the i3 log
|
||||
* 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)
|
||||
#define ELOG(fmt, ...) fprintf(stderr, "ERROR: " fmt, ##__VA_ARGS__)
|
||||
void errorlog(char *fmt, ...);
|
||||
#define ELOG(fmt, ...) errorlog("[libi3] ERROR: " fmt, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -91,6 +122,71 @@ char *sstrdup(const char *str);
|
|||
*/
|
||||
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
|
||||
* 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
|
||||
* 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).
|
||||
* Text must be specified as an i3String.
|
||||
*
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Predict the text width in pixels for the given text. Text can be specified
|
||||
* as UCS-2 or UTF-8.
|
||||
* ASCII version of draw_text to print static strings.
|
||||
*
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* i3 - an improved dynamic tiling window manager
|
||||
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
||||
*
|
||||
* log.c: Setting of loglevels, logging functions.
|
||||
* log.c: Logging functions.
|
||||
*
|
||||
*/
|
||||
#ifndef _LOG_H
|
||||
|
@ -13,13 +13,20 @@
|
|||
#include <stdarg.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
|
||||
is, delete the preceding comma */
|
||||
#define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__)
|
||||
#define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__)
|
||||
#define DLOG(fmt, ...) debuglog(LOGLEVEL, "%s:%s:%d - " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
extern char *loglevels[];
|
||||
extern char *errorfilename;
|
||||
extern char *shmlogname;
|
||||
extern int shmlog_size;
|
||||
|
@ -32,10 +39,10 @@ extern int shmlog_size;
|
|||
void init_logging(void);
|
||||
|
||||
/**
|
||||
* Enables the given loglevel.
|
||||
* Set debug logging.
|
||||
*
|
||||
*/
|
||||
void add_loglevel(const char *level);
|
||||
void set_debug_logging(const bool _debug_logging);
|
||||
|
||||
/**
|
||||
* Set verbosity of i3. If verbose is set to true, informative messages will
|
||||
|
@ -47,29 +54,32 @@ void set_verbosity(bool _verbose);
|
|||
|
||||
/**
|
||||
* Logs the given message to stdout while prefixing the current time to it,
|
||||
* but only if the corresponding debug loglevel was activated.
|
||||
* but only if debug logging was activated.
|
||||
*
|
||||
*/
|
||||
void debuglog(uint64_t lev, char *fmt, ...);
|
||||
void debuglog(char *fmt, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* 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.
|
||||
* This is to be called by LOG() which includes filename/linenumber
|
||||
*
|
||||
* Deletes the unused log files. Useful if i3 exits immediately, eg.
|
||||
* 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
|
||||
|
|
|
@ -31,7 +31,7 @@ void regex_free(struct regex *regex);
|
|||
/**
|
||||
* Checks if the given regular expression matches the given input and returns
|
||||
* true if it does. In either case, it logs the outcome using LOG(), so it will
|
||||
* be visible without any debug loglevel.
|
||||
* be visible without debug logging.
|
||||
*
|
||||
*/
|
||||
bool regex_matches(struct regex *regex, const char *input);
|
||||
|
|
|
@ -30,4 +30,14 @@ void scratchpad_move(Con *con);
|
|||
*/
|
||||
void scratchpad_show(Con *con);
|
||||
|
||||
/**
|
||||
* When starting i3 initially (and after each change to the connected outputs),
|
||||
* this function fixes the resolution of the __i3 pseudo-output. When that
|
||||
* resolution is not set to a function which shares a common divisor with every
|
||||
* active output’s resolution, floating point calculation errors will lead to
|
||||
* the scratchpad window moving when shown repeatedly.
|
||||
*
|
||||
*/
|
||||
void scratchpad_fix_resolution(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,11 +12,33 @@
|
|||
#define _I3_SHMLOG_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 {
|
||||
/* Byte offset where the next line will be written to. */
|
||||
uint32_t offset_next_write;
|
||||
|
||||
/* Byte offset where the last wrap occured. */
|
||||
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;
|
||||
|
||||
/* wrap counter. We need it to reliably signal to clients that we just
|
||||
* wrapped (clients cannot use offset_last_wrap because that might
|
||||
* coincidentally be exactly the same as previously). Overflows can happen
|
||||
* and don’t matter — clients use an equality check (==). */
|
||||
uint32_t wrap_count;
|
||||
|
||||
/* pthread condvar which will be broadcasted whenever there is a new
|
||||
* message in the log. i3-dump-log uses this to implement -f (follow, like
|
||||
* tail -f) in an efficient way. */
|
||||
pthread_cond_t condvar;
|
||||
} i3_shmlog_header;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -39,16 +39,16 @@ Con *tree_open_con(Con *con, i3Window *window);
|
|||
void tree_split(Con *con, orientation_t orientation);
|
||||
|
||||
/**
|
||||
* Moves focus one level up.
|
||||
* Moves focus one level up. Returns true if focus changed.
|
||||
*
|
||||
*/
|
||||
void level_up(void);
|
||||
bool level_up(void);
|
||||
|
||||
/**
|
||||
* Moves focus one level down.
|
||||
* Moves focus one level down. Returns true if focus changed.
|
||||
*
|
||||
*/
|
||||
void level_down(void);
|
||||
bool level_down(void);
|
||||
|
||||
/**
|
||||
* Renders the tree, that is rendering all outputs using render_con() and
|
||||
|
|
|
@ -1,26 +1,7 @@
|
|||
# Default value so one can compile i3-msg standalone
|
||||
TOPDIR=..
|
||||
|
||||
include $(TOPDIR)/common.mk
|
||||
|
||||
CFLAGS += -I$(TOPDIR)/include
|
||||
|
||||
# Depend on the object files of all source-files in src/*.c and on all header files
|
||||
FILES=$(patsubst %.c,%.o,$(wildcard *.c))
|
||||
HEADERS=$(wildcard *.h)
|
||||
|
||||
# Depend on the specific file (.c for each .o) and on all headers
|
||||
%.o: %.c ${HEADERS}
|
||||
echo "[libi3] CC $<"
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
all: libi3.a
|
||||
|
||||
libi3.a: ${FILES}
|
||||
echo "[libi3] AR libi3.a"
|
||||
ar rcs libi3.a ${FILES}
|
||||
all:
|
||||
$(MAKE) -C .. libi3.a
|
||||
|
||||
clean:
|
||||
rm -f *.o libi3.a
|
||||
$(MAKE) -C .. clean-libi3
|
||||
|
||||
distclean: clean
|
||||
.PHONY: all clean
|
||||
|
|
374
libi3/font.c
374
libi3/font.c
|
@ -12,11 +12,125 @@
|
|||
#include <stdbool.h>
|
||||
#include <err.h>
|
||||
|
||||
#if PANGO_SUPPORT
|
||||
#include <cairo/cairo-xcb.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#endif
|
||||
|
||||
#include "libi3.h"
|
||||
|
||||
extern xcb_connection_t *conn;
|
||||
extern xcb_screen_t *root_screen;
|
||||
|
||||
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,
|
||||
* 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 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 */
|
||||
font.id = xcb_generate_id(conn);
|
||||
xcb_void_cookie_t font_cookie = xcb_open_font_checked(conn, font.id,
|
||||
font.specific.xcb.id = xcb_generate_id(conn);
|
||||
xcb_void_cookie_t font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id,
|
||||
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. */
|
||||
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",
|
||||
pattern, error->error_code);
|
||||
pattern = "fixed";
|
||||
font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern);
|
||||
info_cookie = xcb_query_font(conn, font.id);
|
||||
font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id,
|
||||
strlen(pattern), pattern);
|
||||
info_cookie = xcb_query_font(conn, font.specific.xcb.id);
|
||||
|
||||
/* Check if we managed to open 'fixed' */
|
||||
error = xcb_request_check(conn, font_cookie);
|
||||
|
@ -50,8 +175,9 @@ i3Font load_font(const char *pattern, const bool fallback) {
|
|||
if (error != NULL) {
|
||||
ELOG("Could not open fallback font 'fixed', trying with '-misc-*'.\n");
|
||||
pattern = "-misc-*";
|
||||
font_cookie = xcb_open_font_checked(conn, font.id, strlen(pattern), pattern);
|
||||
info_cookie = xcb_query_font(conn, font.id);
|
||||
font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id,
|
||||
strlen(pattern), pattern);
|
||||
info_cookie = xcb_query_font(conn, font.specific.xcb.id);
|
||||
|
||||
if ((error = xcb_request_check(conn, font_cookie)) != NULL)
|
||||
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 */
|
||||
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);
|
||||
|
||||
/* Get the font table, if possible */
|
||||
if (xcb_query_font_char_infos_length(font.info) == 0)
|
||||
font.table = NULL;
|
||||
if (xcb_query_font_char_infos_length(font.specific.xcb.info) == 0)
|
||||
font.specific.xcb.table = NULL;
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -88,10 +218,27 @@ void set_font(i3Font *font) {
|
|||
*
|
||||
*/
|
||||
void free_font(void) {
|
||||
/* Close the font and free the info */
|
||||
xcb_close_font(conn, savedFont->id);
|
||||
if (savedFont->info)
|
||||
free(savedFont->info);
|
||||
switch (savedFont->type) {
|
||||
case FONT_TYPE_NONE:
|
||||
/* Nothing to do */
|
||||
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) {
|
||||
assert(savedFont != NULL);
|
||||
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
|
||||
uint32_t values[] = { foreground, background, savedFont->id };
|
||||
xcb_change_gc(conn, gc, mask, values);
|
||||
|
||||
switch (savedFont->type) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 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,
|
||||
static int predict_text_width_xcb(const xcb_char2b_t *text, size_t text_len);
|
||||
|
||||
static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawable_t drawable,
|
||||
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 */
|
||||
int pos_y = y + savedFont->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));
|
||||
int pos_y = y + savedFont->specific.xcb.info->font_ascent;
|
||||
|
||||
/* The X11 protocol limits text drawing to 255 chars, so we may need
|
||||
* multiple calls */
|
||||
int pos_x = x;
|
||||
int offset = 0;
|
||||
for (;;) {
|
||||
/* Calculate the size of this chunk */
|
||||
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 */
|
||||
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 */
|
||||
offset += chunk_size;
|
||||
|
@ -154,15 +300,83 @@ void draw_text(char *text, size_t text_len, bool is_ucs2, xcb_drawable_t drawabl
|
|||
break;
|
||||
|
||||
/* Advance pos_x based on the predicted text width */
|
||||
pos_x += predict_text_width((char*)chunk, chunk_size, true);
|
||||
x += predict_text_width_xcb(chunk, chunk_size);
|
||||
}
|
||||
|
||||
/* If we had to convert, free the converted string */
|
||||
if (!is_ucs2)
|
||||
free(input);
|
||||
}
|
||||
|
||||
static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) {
|
||||
/*
|
||||
* Draws text onto the specified X drawable (normally a pixmap) at the
|
||||
* specified coordinates (from the top left corner of the leftmost, uppermost
|
||||
* glyph) and using the provided gc.
|
||||
*
|
||||
* Text must be specified as an i3String.
|
||||
*
|
||||
*/
|
||||
void draw_text(i3String *text, xcb_drawable_t drawable,
|
||||
xcb_gcontext_t gc, int x, int y, int max_width) {
|
||||
assert(savedFont != NULL);
|
||||
|
||||
switch (savedFont->type) {
|
||||
case FONT_TYPE_NONE:
|
||||
/* Nothing to do */
|
||||
return;
|
||||
case FONT_TYPE_XCB:
|
||||
draw_text_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text),
|
||||
drawable, gc, x, y, max_width);
|
||||
break;
|
||||
#if PANGO_SUPPORT
|
||||
case FONT_TYPE_PANGO:
|
||||
/* Render the text using Pango */
|
||||
draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
|
||||
drawable, x, y, max_width);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ASCII version of draw_text to print static strings.
|
||||
*
|
||||
*/
|
||||
void draw_text_ascii(const char *text, xcb_drawable_t drawable,
|
||||
xcb_gcontext_t gc, int x, int y, int max_width) {
|
||||
assert(savedFont != NULL);
|
||||
|
||||
switch (savedFont->type) {
|
||||
case FONT_TYPE_NONE:
|
||||
/* Nothing to do */
|
||||
return;
|
||||
case FONT_TYPE_XCB:
|
||||
{
|
||||
size_t text_len = strlen(text);
|
||||
if (text_len > 255) {
|
||||
/* The text is too long to draw it directly to X */
|
||||
i3String *str = i3string_from_utf8(text);
|
||||
draw_text(str, drawable, gc, x, y, max_width);
|
||||
i3string_free(str);
|
||||
} else {
|
||||
/* X11 coordinates for fonts start at the baseline */
|
||||
int pos_y = y + savedFont->specific.xcb.info->font_ascent;
|
||||
|
||||
xcb_image_text_8(conn, text_len, drawable, gc, x, pos_y, text);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if PANGO_SUPPORT
|
||||
case FONT_TYPE_PANGO:
|
||||
/* Render the text using Pango */
|
||||
draw_text_pango(text, strlen(text),
|
||||
drawable, x, y, max_width);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int xcb_query_text_width(const xcb_char2b_t *text, size_t text_len) {
|
||||
/* Make the user know we’re using the slow path, but only once. */
|
||||
static bool first_invocation = true;
|
||||
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 */
|
||||
xcb_generic_error_t *error;
|
||||
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,
|
||||
cookie, &error);
|
||||
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. */
|
||||
fprintf(stderr, "Could not get text extents (X error code %d)\n",
|
||||
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;
|
||||
|
@ -189,27 +403,18 @@ static int xcb_query_text_width(xcb_char2b_t *text, size_t text_len) {
|
|||
return width;
|
||||
}
|
||||
|
||||
/*
|
||||
* Predict the text width in pixels for the given text. Text can be specified
|
||||
* as UCS-2 or UTF-8.
|
||||
*
|
||||
*/
|
||||
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);
|
||||
static int predict_text_width_xcb(const xcb_char2b_t *input, size_t text_len) {
|
||||
if (text_len == 0)
|
||||
return 0;
|
||||
|
||||
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 */
|
||||
width = xcb_query_text_width(input, text_len);
|
||||
} else {
|
||||
/* Save some pointers for convenience */
|
||||
xcb_query_font_reply_t *font_info = savedFont->info;
|
||||
xcb_charinfo_t *font_table = savedFont->table;
|
||||
xcb_query_font_reply_t *font_info = savedFont->specific.xcb.info;
|
||||
xcb_charinfo_t *font_table = savedFont->specific.xcb.table;
|
||||
|
||||
/* Calculate the width using the font table */
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Predict the text width in pixels for the given text. Text must be
|
||||
* specified as an i3String.
|
||||
*
|
||||
*/
|
||||
int predict_text_width(i3String *text) {
|
||||
assert(savedFont != NULL);
|
||||
|
||||
switch (savedFont->type) {
|
||||
case FONT_TYPE_NONE:
|
||||
/* Nothing to do */
|
||||
return 0;
|
||||
case FONT_TYPE_XCB:
|
||||
return predict_text_width_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text));
|
||||
#if PANGO_SUPPORT
|
||||
case FONT_TYPE_PANGO:
|
||||
/* Calculate extents using Pango */
|
||||
return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text));
|
||||
#endif
|
||||
default:
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
||||
*
|
||||
*/
|
||||
#include "libi3.h"
|
||||
|
||||
/*
|
||||
* Returns the visual type associated with the given screen.
|
||||
*
|
||||
*/
|
||||
xcb_visualtype_t *get_visualtype(xcb_screen_t *screen) {
|
||||
xcb_depth_iterator_t depth_iter;
|
||||
for (depth_iter = xcb_screen_allowed_depths_iterator(screen);
|
||||
depth_iter.rem;
|
||||
xcb_depth_next(&depth_iter)) {
|
||||
xcb_visualtype_iterator_t visual_iter;
|
||||
for (visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
|
||||
visual_iter.rem;
|
||||
xcb_visualtype_next(&visual_iter)) {
|
||||
if (screen->root_visual == visual_iter.data->visual_id)
|
||||
return visual_iter.data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
CLEAN_TARGETS += clean-libi3
|
||||
|
||||
libi3_SOURCES := $(wildcard libi3/*.c)
|
||||
libi3_HEADERS := $(wildcard libi3/*.h)
|
||||
libi3_CFLAGS = $(PANGO_CFLAGS)
|
||||
libi3_LIBS =
|
||||
|
||||
libi3_OBJECTS := $(libi3_SOURCES:.c=.o)
|
||||
|
||||
|
||||
libi3/%.o: libi3/%.c $(libi3_HEADERS)
|
||||
echo "[libi3] CC $<"
|
||||
$(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(libi3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
libi3.a: $(libi3_OBJECTS)
|
||||
echo "[libi3] AR libi3.a"
|
||||
ar rcs $@ $^ $(libi3_LIBS)
|
||||
|
||||
clean-libi3:
|
||||
echo "[libi3] Clean"
|
||||
rm -f $(libi3_OBJECTS) libi3/libi3.a
|
|
@ -28,7 +28,7 @@ char *root_atom_contents(const char *atomname) {
|
|||
xcb_intern_atom_cookie_t atom_cookie;
|
||||
xcb_intern_atom_reply_t *atom_reply;
|
||||
int screen;
|
||||
char *socket_path;
|
||||
char *content;
|
||||
|
||||
if ((conn = xcb_connect(NULL, &screen)) == NULL ||
|
||||
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);
|
||||
if (prop_reply == NULL || xcb_get_property_value_length(prop_reply) == 0)
|
||||
return NULL;
|
||||
if (asprintf(&socket_path, "%.*s", xcb_get_property_value_length(prop_reply),
|
||||
(char*)xcb_get_property_value(prop_reply)) == -1)
|
||||
return NULL;
|
||||
if (prop_reply->type == XCB_ATOM_CARDINAL) {
|
||||
/* We treat a CARDINAL as a >= 32-bit unsigned int. The only CARDINAL
|
||||
* 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);
|
||||
return socket_path;
|
||||
return content;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
||||
*
|
||||
* string.c: Define an i3String type to automagically handle UTF-8/UCS-2
|
||||
* conversions. Some font backends need UCS-2 (X core fonts),
|
||||
* others want UTF-8 (Pango).
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libi3.h"
|
||||
|
||||
struct _i3String {
|
||||
char *utf8;
|
||||
xcb_char2b_t *ucs2;
|
||||
size_t num_glyphs;
|
||||
size_t num_bytes;
|
||||
};
|
||||
|
||||
/*
|
||||
* Build an i3String from an UTF-8 encoded string.
|
||||
* Returns the newly-allocated i3String.
|
||||
*
|
||||
*/
|
||||
i3String *i3string_from_utf8(const char *from_utf8) {
|
||||
i3String *str = scalloc(sizeof(i3String));
|
||||
|
||||
/* Get the text */
|
||||
str->utf8 = sstrdup(from_utf8);
|
||||
|
||||
/* Compute and store the length */
|
||||
str->num_bytes = strlen(str->utf8);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an i3String from an UTF-8 encoded string with fixed length.
|
||||
* To be used when no proper NUL-terminaison is available.
|
||||
* Returns the newly-allocated i3String.
|
||||
*
|
||||
*/
|
||||
i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes) {
|
||||
i3String *str = scalloc(sizeof(i3String));
|
||||
|
||||
/* Copy the actual text to our i3String */
|
||||
str->utf8 = scalloc(sizeof(char) * (num_bytes + 1));
|
||||
strncpy(str->utf8, from_utf8, num_bytes);
|
||||
str->utf8[num_bytes] = '\0';
|
||||
|
||||
/* Store the length */
|
||||
str->num_bytes = num_bytes;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an i3String from an UCS-2 encoded string.
|
||||
* Returns the newly-allocated i3String.
|
||||
*
|
||||
*/
|
||||
i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs) {
|
||||
i3String *str = scalloc(sizeof(i3String));
|
||||
|
||||
/* Copy the actual text to our i3String */
|
||||
size_t num_bytes = num_glyphs * sizeof(xcb_char2b_t);
|
||||
str->ucs2 = scalloc(num_bytes);
|
||||
memcpy(str->ucs2, from_ucs2, num_bytes);
|
||||
|
||||
/* Store the length */
|
||||
str->num_glyphs = num_glyphs;
|
||||
|
||||
str->utf8 = NULL;
|
||||
str->num_bytes = 0;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free an i3String.
|
||||
*
|
||||
*/
|
||||
void i3string_free(i3String *str) {
|
||||
if (str == NULL)
|
||||
return;
|
||||
free(str->utf8);
|
||||
free(str->ucs2);
|
||||
free(str);
|
||||
}
|
||||
|
||||
static void i3string_ensure_utf8(i3String *str) {
|
||||
if (str->utf8 != NULL)
|
||||
return;
|
||||
if ((str->utf8 = convert_ucs2_to_utf8(str->ucs2, str->num_glyphs)) != NULL)
|
||||
str->num_bytes = strlen(str->utf8);
|
||||
}
|
||||
|
||||
static void i3string_ensure_ucs2(i3String *str) {
|
||||
if (str->ucs2 != NULL)
|
||||
return;
|
||||
str->ucs2 = convert_utf8_to_ucs2(str->utf8, &str->num_glyphs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the UTF-8 encoded version of the i3String.
|
||||
*
|
||||
*/
|
||||
const char *i3string_as_utf8(i3String *str) {
|
||||
i3string_ensure_utf8(str);
|
||||
return str->utf8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the UCS-2 encoded version of the i3String.
|
||||
*
|
||||
*/
|
||||
const xcb_char2b_t *i3string_as_ucs2(i3String *str) {
|
||||
i3string_ensure_ucs2(str);
|
||||
return str->ucs2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of bytes (UTF-8 encoded) in an i3String.
|
||||
*
|
||||
*/
|
||||
size_t i3string_get_num_bytes(i3String *str) {
|
||||
i3string_ensure_utf8(str);
|
||||
return str->num_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of glyphs in an i3String.
|
||||
*
|
||||
*/
|
||||
size_t i3string_get_num_glyphs(i3String *str) {
|
||||
i3string_ensure_ucs2(str);
|
||||
return str->num_glyphs;
|
||||
}
|
16
man/Makefile
16
man/Makefile
|
@ -1,15 +1,7 @@
|
|||
A2M:=a2x -f manpage --asciidoc-opts="-f asciidoc.conf"
|
||||
|
||||
all: i3.1 i3-msg.1 i3-input.1 i3-nagbar.1 i3-config-wizard.1 i3-migrate-config-to-v4.1 i3-sensible-editor.1 i3-sensible-pager.1 i3-sensible-terminal.1 i3-dump-log.1
|
||||
|
||||
%.1: %.man asciidoc.conf
|
||||
${A2M} $<
|
||||
all:
|
||||
$(MAKE) -C .. mans
|
||||
|
||||
clean:
|
||||
for file in $$(echo i3 i3-msg i3-input i3-nagbar i3-wsbar i3-config-wizard i3-migrate-config-to-v4 i3-sensible-editor i3-sensible-pager i3-sensible-terminal i3-dump-log); \
|
||||
do \
|
||||
rm -f $${file}.1 $${file}.html $${file}.xml; \
|
||||
done
|
||||
$(MAKE) -C .. clean-mans
|
||||
|
||||
distclean: clean
|
||||
rm -f *.1
|
||||
.PHONY: all clean
|
||||
|
|
|
@ -7,7 +7,7 @@ template::[header-declarations]
|
|||
<refentrytitle>{mantitle}</refentrytitle>
|
||||
<manvolnum>{manvolnum}</manvolnum>
|
||||
<refmiscinfo class="source">i3</refmiscinfo>
|
||||
<refmiscinfo class="version">4.1.2</refmiscinfo>
|
||||
<refmiscinfo class="version">4.3</refmiscinfo>
|
||||
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
||||
</refmeta>
|
||||
<refnamediv>
|
||||
|
|
|
@ -14,7 +14,7 @@ i3-dump-log [-s <socketpath>]
|
|||
== DESCRIPTION
|
||||
|
||||
Debug versions of i3 automatically use 1% of your RAM (but 25 MiB max) to store
|
||||
full debug loglevel log output. This is extremely helpful for bugreports and
|
||||
full debug log output. This is extremely helpful for bugreports and
|
||||
figuring out what is going on, without permanently logging to a file.
|
||||
|
||||
With i3-dump-log, you can dump the SHM log to stdout.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
i3-msg(1)
|
||||
=========
|
||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
||||
v4.2, January 2012
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
v4.2, August 2012
|
||||
|
||||
== NAME
|
||||
|
||||
|
@ -38,6 +38,9 @@ get_bar_config::
|
|||
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.
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
i3-sensible-terminal(1)
|
||||
=======================
|
||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
||||
v4.1, November 2011
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
v4.2, August 2012
|
||||
|
||||
== NAME
|
||||
|
||||
|
@ -30,6 +30,7 @@ It tries to start one of the following (in that order):
|
|||
* xterm
|
||||
* gnome-terminal
|
||||
* roxterm
|
||||
* xfce4-terminal
|
||||
|
||||
Please don’t complain about the order: If the user has any preference, he will
|
||||
have $TERMINAL set or modified his i3 configuration file.
|
||||
|
|
55
man/i3.man
55
man/i3.man
|
@ -1,7 +1,7 @@
|
|||
i3(1)
|
||||
=====
|
||||
Michael Stapelberg <michael+i3@stapelberg.de>
|
||||
v4.0, July 2011
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
v4.3, September 2012
|
||||
|
||||
== NAME
|
||||
|
||||
|
@ -9,7 +9,7 @@ i3 - an improved dynamic, tiling window manager
|
|||
|
||||
== SYNOPSIS
|
||||
|
||||
i3 [-a] [-c configfile] [-C] [-d <loglevel>] [-v] [-V]
|
||||
i3 [-a] [-c configfile] [-C] [-d all] [-v] [-V]
|
||||
|
||||
== OPTIONS
|
||||
|
||||
|
@ -22,8 +22,9 @@ Specifies an alternate configuration file path.
|
|||
-C::
|
||||
Check the configuration file for validity and exit.
|
||||
|
||||
-d::
|
||||
Specifies the debug loglevel. To see the most output, use -d all.
|
||||
-d all::
|
||||
Enables debug logging.
|
||||
The 'all' parameter is present for historical reasons.
|
||||
|
||||
-v::
|
||||
Display version number (and date of the last commit).
|
||||
|
@ -31,6 +32,18 @@ Display version number (and date of the last commit).
|
|||
-V::
|
||||
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
|
||||
|
||||
=== 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
|
||||
|
||||
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
|
||||
i3, like so:
|
||||
before version 302.17, you need to use the +--force-xinerama+ flag (in your
|
||||
~/.xsession) when starting i3, like so:
|
||||
|
||||
----------------------------------------------
|
||||
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:
|
||||
|
||||
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/;::
|
||||
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.
|
||||
|
@ -261,19 +280,15 @@ xset -b
|
|||
# Enable zapping (C-A-<Bksp> kills X)
|
||||
setxkbmap -option terminate:ctrl_alt_bksp
|
||||
|
||||
# Enforce correct locales from the beginning
|
||||
unset LC_COLLATE
|
||||
export LC_CTYPE=de_DE.UTF-8
|
||||
export LC_TIME=de_DE.UTF-8
|
||||
export LC_NUMERIC=de_DE.UTF-8
|
||||
export LC_MONETARY=de_DE.UTF-8
|
||||
# Enforce correct locales from the beginning:
|
||||
# LC_ALL is unset since it overwrites everything
|
||||
# LANG=de_DE.UTF-8 is used, except for:
|
||||
# LC_MESSAGES=C never translates program output
|
||||
# LC_TIME=en_DK leads to yyyy-mm-dd hh:mm date/time output
|
||||
unset LC_ALL
|
||||
export LANG=de_DE.UTF-8
|
||||
export LC_MESSAGES=C
|
||||
export LC_PAPER=de_DE.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
|
||||
export LC_TIME=en_DK.UTF-8
|
||||
|
||||
# Use XToolkit in java applications
|
||||
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/
|
||||
|
||||
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)
|
||||
|
||||
== AUTHOR
|
||||
|
|
|
@ -39,9 +39,8 @@ Display a short help-message and exit
|
|||
workspace switching buttons and a statusline generated by i3status(1) or
|
||||
similar. It is automatically invoked (and configured through) i3.
|
||||
|
||||
i3bar does not support any color or other markups, so stdin should be plain
|
||||
utf8, one line at a time. If you use *i3status*(1), you therefore should
|
||||
specify 'output_format = none' in the general section of its config file.
|
||||
i3bar supports colors via a JSON protocol starting from v4.2, see
|
||||
http://i3wm.org/docs/i3bar-protocol.html
|
||||
|
||||
== ENVIRONMENT
|
||||
|
||||
|
@ -59,7 +58,7 @@ Instead, see the i3 documentation, especially the User’s Guide.
|
|||
|
||||
== 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.
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
DISTCLEAN_TARGETS += clean-mans
|
||||
|
||||
A2X = a2x
|
||||
|
||||
A2X_MAN_CALL = $(V_A2X)$(A2X) -f manpage --asciidoc-opts="-f man/asciidoc.conf" $(A2X_FLAGS) $<
|
||||
|
||||
MANS_1 = \
|
||||
man/i3.1 \
|
||||
man/i3bar.1 \
|
||||
man/i3-msg.1 \
|
||||
man/i3-input.1 \
|
||||
man/i3-nagbar.1 \
|
||||
man/i3-config-wizard.1 \
|
||||
man/i3-migrate-config-to-v4.1 \
|
||||
man/i3-sensible-editor.1 \
|
||||
man/i3-sensible-pager.1 \
|
||||
man/i3-sensible-terminal.1 \
|
||||
man/i3-dump-log.1
|
||||
|
||||
MANS = \
|
||||
$(MANS_1)
|
||||
|
||||
mans: $(MANS)
|
||||
|
||||
$(MANS_1): %.1: %.man man/asciidoc.conf
|
||||
$(A2X_MAN_CALL)
|
||||
|
||||
clean-mans:
|
||||
for file in $(notdir $(MANS)); \
|
||||
do \
|
||||
rm -f man/$${file} man/$${file%.*}.html man/$${file%.*}.xml; \
|
||||
done
|
|
@ -66,10 +66,20 @@ state BORDER:
|
|||
border_style = 'normal', 'none', '1pixel', 'toggle'
|
||||
-> call cmd_border($border_style)
|
||||
|
||||
# layout default|stacked|stacking|tabbed
|
||||
# layout default|stacked|stacking|tabbed|splitv|splith
|
||||
# layout toggle [split|all]
|
||||
state LAYOUT:
|
||||
layout_mode = 'default', 'stacked', 'stacking', 'tabbed'
|
||||
layout_mode = 'default', 'stacked', 'stacking', 'tabbed', 'splitv', 'splith'
|
||||
-> call cmd_layout($layout_mode)
|
||||
'toggle'
|
||||
-> LAYOUT_TOGGLE
|
||||
|
||||
# layout toggle [split|all]
|
||||
state LAYOUT_TOGGLE:
|
||||
end
|
||||
-> call cmd_layout_toggle($toggle_mode)
|
||||
toggle_mode = 'split', 'all'
|
||||
-> call cmd_layout_toggle($toggle_mode)
|
||||
|
||||
# append_layout <path>
|
||||
state APPEND_LAYOUT:
|
||||
|
@ -190,7 +200,7 @@ state RENAME_WORKSPACE_TO:
|
|||
-> call cmd_rename_workspace($old_name, $new_name)
|
||||
|
||||
# move <direction> [<pixels> [px]]
|
||||
# move [window|container] [to] workspace <str>
|
||||
# move [window|container] [to] workspace [<str>|next|prev|current]
|
||||
# move [window|container] [to] output <str>
|
||||
# move [window|container] [to] scratchpad
|
||||
# move workspace to [output] <str>
|
||||
|
@ -231,7 +241,7 @@ state MOVE_DIRECTION_PX:
|
|||
state MOVE_WORKSPACE:
|
||||
'to'
|
||||
-> MOVE_WORKSPACE_TO_OUTPUT
|
||||
workspace = 'next', 'prev', 'next_on_output', 'prev_on_output'
|
||||
workspace = 'next', 'prev', 'next_on_output', 'prev_on_output', 'current'
|
||||
-> call cmd_move_con_to_workspace($workspace)
|
||||
'number'
|
||||
-> MOVE_WORKSPACE_NUMBER
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
all:
|
||||
$(MAKE) -C .. i3
|
||||
|
||||
install:
|
||||
$(MAKE) -C .. install-i3
|
||||
|
||||
clean:
|
||||
$(MAKE) -C .. clean-i3
|
||||
|
||||
.PHONY: all install clean
|
|
@ -1,3 +1,5 @@
|
|||
#undef I3__FILE__
|
||||
#define I3__FILE__ "assignments.c"
|
||||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
|
|
|
@ -66,6 +66,7 @@ EOL (\r?\n)
|
|||
%x BAR_COLOR
|
||||
|
||||
%x EXEC
|
||||
%x OPTRELEASE
|
||||
|
||||
%%
|
||||
|
||||
|
@ -159,7 +160,7 @@ EOL (\r?\n)
|
|||
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; }
|
||||
<COLOR_COND>#[0-9a-fA-F]+ { yy_pop_state(); yylval.string = sstrdup(yytext); return HEXCOLOR; }
|
||||
<COLOR_COND>{EOL} {
|
||||
|
@ -172,12 +173,14 @@ EOL (\r?\n)
|
|||
<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>. { 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; }
|
||||
bar { yy_push_state(BAR); return TOK_BAR; }
|
||||
mode { return TOKMODE; }
|
||||
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; }
|
||||
bindsym { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; }
|
||||
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); yy_push_state(OPTRELEASE); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; }
|
||||
floating_maximum_size { return TOKFLOATING_MAXIMUM_SIZE; }
|
||||
floating_minimum_size { return TOKFLOATING_MINIMUM_SIZE; }
|
||||
floating_modifier { return TOKFLOATING_MODIFIER; }
|
||||
|
@ -200,6 +203,8 @@ new_float { return TOKNEWFLOAT; }
|
|||
normal { return TOK_NORMAL; }
|
||||
none { return TOK_NONE; }
|
||||
1pixel { return TOK_1PIXEL; }
|
||||
hide_edge_borders { return TOK_HIDE_EDGE_BORDERS; }
|
||||
both { return TOK_BOTH; }
|
||||
focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; }
|
||||
force_focus_wrapping { return TOK_FORCE_FOCUS_WRAPPING; }
|
||||
force_xinerama { return TOK_FORCE_XINERAMA; }
|
||||
|
|
105
src/cfgparse.y
105
src/cfgparse.y
|
@ -3,6 +3,8 @@
|
|||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
*/
|
||||
#undef I3__FILE__
|
||||
#define I3__FILE__ "cfgparse.y"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.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 the i3Font. */
|
||||
static char *font_pattern;
|
||||
/* The path to the temporary script files used by i3-nagbar. We need to keep
|
||||
* them around to delete the files in the i3-nagbar SIGCHLD handler. */
|
||||
static char *edit_script_path, *pager_script_path;
|
||||
|
||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
extern int yylex(struct context *context);
|
||||
|
@ -233,6 +238,12 @@ static char *migrate_config(char *input, off_t size) {
|
|||
*/
|
||||
static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
|
||||
ev_child_stop(EV_A_ watcher);
|
||||
|
||||
if (unlink(edit_script_path) != 0)
|
||||
warn("Could not delete temporary i3-nagbar script %s", edit_script_path);
|
||||
if (unlink(pager_script_path) != 0)
|
||||
warn("Could not delete temporary i3-nagbar script %s", pager_script_path);
|
||||
|
||||
if (!WIFEXITED(watcher->rstatus)) {
|
||||
fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
|
||||
return;
|
||||
|
@ -264,6 +275,23 @@ static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Writes the given command as a shell script to path.
|
||||
* Returns true unless something went wrong.
|
||||
*
|
||||
*/
|
||||
static bool write_nagbar_script(const char *path, const char *command) {
|
||||
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
|
||||
if (fd == -1) {
|
||||
warn("Could not create temporary script to store the nagbar command");
|
||||
return false;
|
||||
}
|
||||
write(fd, "#!/bin/sh\n", strlen("#!/bin/sh\n"));
|
||||
write(fd, command, strlen(command));
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Starts an i3-nagbar process which alerts the user that his configuration
|
||||
* file contains one or more errors. Also offers two buttons: One to launch an
|
||||
|
@ -276,6 +304,18 @@ static void start_configerror_nagbar(const char *config_path) {
|
|||
return;
|
||||
|
||||
fprintf(stderr, "Starting i3-nagbar due to configuration errors\n");
|
||||
|
||||
/* We need to create a custom script containing our actual command
|
||||
* since not every terminal emulator which is contained in
|
||||
* i3-sensible-terminal supports -e with multiple arguments (and not
|
||||
* all of them support -e with one quoted argument either).
|
||||
*
|
||||
* NB: The paths need to be unique, that is, don’t assume users close
|
||||
* their nagbars at any point in time (and they still need to work).
|
||||
* */
|
||||
edit_script_path = get_process_filename("nagbar-cfgerror-edit");
|
||||
pager_script_path = get_process_filename("nagbar-cfgerror-pager");
|
||||
|
||||
configerror_pid = fork();
|
||||
if (configerror_pid == -1) {
|
||||
warn("Could not fork()");
|
||||
|
@ -284,10 +324,17 @@ static void start_configerror_nagbar(const char *config_path) {
|
|||
|
||||
/* child */
|
||||
if (configerror_pid == 0) {
|
||||
char *edit_command, *pager_command;
|
||||
sasprintf(&edit_command, "i3-sensible-editor \"%s\" && i3-msg reload\n", config_path);
|
||||
sasprintf(&pager_command, "i3-sensible-pager \"%s\"\n", errorfilename);
|
||||
if (!write_nagbar_script(edit_script_path, edit_command) ||
|
||||
!write_nagbar_script(pager_script_path, pager_command))
|
||||
return;
|
||||
|
||||
char *editaction,
|
||||
*pageraction;
|
||||
sasprintf(&editaction, "i3-sensible-terminal -e sh -c \"i3-sensible-editor \\\"%s\\\" && i3-msg reload\"", config_path);
|
||||
sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename);
|
||||
sasprintf(&editaction, "i3-sensible-terminal -e \"%s\"", edit_script_path);
|
||||
sasprintf(&pageraction, "i3-sensible-terminal -e \"%s\"", pager_script_path);
|
||||
char *argv[] = {
|
||||
NULL, /* will be replaced by the executable path */
|
||||
"-t",
|
||||
|
@ -384,8 +431,10 @@ static void check_for_duplicate_bindings(struct context *context) {
|
|||
/* Check if the keycodes or modifiers are different. If so, they
|
||||
* can't be duplicate */
|
||||
if (bind->keycode != current->keycode ||
|
||||
bind->mods != current->mods)
|
||||
bind->mods != current->mods ||
|
||||
bind->release != current->release)
|
||||
continue;
|
||||
|
||||
context->has_errors = true;
|
||||
if (current->keycode != 0) {
|
||||
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_NONE "none"
|
||||
%token TOK_1PIXEL "1pixel"
|
||||
%token TOK_HIDE_EDGE_BORDERS "hide_edge_borders"
|
||||
%token TOK_BOTH "both"
|
||||
%token TOKFOCUSFOLLOWSMOUSE "focus_follows_mouse"
|
||||
%token TOK_FORCE_FOCUS_WRAPPING "force_focus_wrapping"
|
||||
%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_URGENT_WORKSPACE "urgent_workspace"
|
||||
%token TOK_NO_STARTUP_ID "--no-startup-id"
|
||||
%token TOK_RELEASE "--release"
|
||||
|
||||
%token TOK_MARK "mark"
|
||||
%token TOK_CLASS "class"
|
||||
|
@ -754,6 +806,8 @@ void parse_file(const char *f) {
|
|||
%type <number> layout_mode
|
||||
%type <number> border_style
|
||||
%type <number> new_window
|
||||
%type <number> hide_edge_borders
|
||||
%type <number> edge_hiding_mode
|
||||
%type <number> new_float
|
||||
%type <number> colorpixel
|
||||
%type <number> bool
|
||||
|
@ -762,6 +816,7 @@ void parse_file(const char *f) {
|
|||
%type <number> bar_mode_mode
|
||||
%type <number> bar_modifier_modifier
|
||||
%type <number> optional_no_startup_id
|
||||
%type <number> optional_release
|
||||
%type <string> command
|
||||
%type <string> word_or_number
|
||||
%type <string> qstring_or_number
|
||||
|
@ -788,6 +843,7 @@ line:
|
|||
| workspace_layout
|
||||
| new_window
|
||||
| new_float
|
||||
| hide_edge_borders
|
||||
| focus_follows_mouse
|
||||
| force_focus_wrapping
|
||||
| force_xinerama
|
||||
|
@ -829,33 +885,40 @@ binding:
|
|||
;
|
||||
|
||||
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));
|
||||
|
||||
new->keycode = $2;
|
||||
new->mods = $1;
|
||||
new->command = $3;
|
||||
new->release = $1;
|
||||
new->keycode = $3;
|
||||
new->mods = $2;
|
||||
new->command = $4;
|
||||
|
||||
$$ = new;
|
||||
}
|
||||
;
|
||||
|
||||
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));
|
||||
|
||||
new->symbol = $2;
|
||||
new->mods = $1;
|
||||
new->command = $3;
|
||||
new->release = $1;
|
||||
new->symbol = $3;
|
||||
new->mods = $2;
|
||||
new->command = $4;
|
||||
|
||||
$$ = new;
|
||||
}
|
||||
;
|
||||
|
||||
optional_release:
|
||||
/* empty */ { $$ = B_UPON_KEYPRESS; }
|
||||
| TOK_RELEASE { $$ = B_UPON_KEYRELEASE; }
|
||||
;
|
||||
|
||||
for_window:
|
||||
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:
|
||||
TOKFOCUSFOLLOWSMOUSE bool
|
||||
{
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#undef I3__FILE__
|
||||
#define I3__FILE__ "click.c"
|
||||
/*
|
||||
* 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;
|
||||
while (resize_con->type != CT_WORKSPACE &&
|
||||
resize_con->type != CT_FLOATING_CON &&
|
||||
resize_con->parent->orientation != orientation)
|
||||
con_orientation(resize_con->parent) != orientation)
|
||||
resize_con = resize_con->parent;
|
||||
|
||||
DLOG("resize_con = %p\n", resize_con);
|
||||
if (resize_con->type != CT_WORKSPACE &&
|
||||
resize_con->type != CT_FLOATING_CON &&
|
||||
resize_con->parent->orientation == orientation) {
|
||||
con_orientation(resize_con->parent) == orientation) {
|
||||
first = resize_con;
|
||||
second = (way == 'n') ? TAILQ_NEXT(first, nodes) : TAILQ_PREV(first, nodes_head, nodes);
|
||||
if (second == TAILQ_END(&(first->nodes_head))) {
|
||||
|
@ -145,7 +147,7 @@ static bool tiling_resize(Con *con, xcb_button_press_event_t *event, const click
|
|||
|
||||
if ((check_con->layout == L_STACKED ||
|
||||
check_con->layout == L_TABBED ||
|
||||
check_con->orientation == HORIZ) &&
|
||||
con_orientation(check_con) == HORIZ) &&
|
||||
con_num_children(check_con) > 1) {
|
||||
DLOG("Not handling this resize, this container has > 1 child.\n");
|
||||
return false;
|
||||
|
|
229
src/commands.c
229
src/commands.c
|
@ -1,3 +1,5 @@
|
|||
#undef I3__FILE__
|
||||
#define I3__FILE__ "commands.c"
|
||||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
|
@ -23,7 +25,7 @@
|
|||
} while (0)
|
||||
|
||||
/** When the command did not include match criteria (!), we use the currently
|
||||
* focused command. Do not confuse this case with a command which included
|
||||
* focused container. Do not confuse this case with a command which included
|
||||
* criteria but which did not match any windows. This macro has to be called in
|
||||
* every command.
|
||||
*/
|
||||
|
@ -351,7 +353,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
|
|||
|
||||
/*
|
||||
* Implementation of 'move [window|container] [to] workspace
|
||||
* next|prev|next_on_output|prev_on_output'.
|
||||
* next|prev|next_on_output|prev_on_output|current'.
|
||||
*
|
||||
*/
|
||||
void cmd_move_con_to_workspace(I3_CMD, char *which) {
|
||||
|
@ -359,6 +361,15 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
|
|||
|
||||
DLOG("which=%s\n", which);
|
||||
|
||||
/* We have nothing to move:
|
||||
* when criteria was specified but didn't match any window or
|
||||
* when criteria wasn't specified and we don't have any window focused. */
|
||||
if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
|
||||
(match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
/* get the workspace */
|
||||
|
@ -371,6 +382,8 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
|
|||
ws = workspace_next_on_output();
|
||||
else if (strcmp(which, "prev_on_output") == 0)
|
||||
ws = workspace_prev_on_output();
|
||||
else if (strcmp(which, "current") == 0)
|
||||
ws = con_get_workspace(focused);
|
||||
else {
|
||||
ELOG("BUG: called with which=%s\n", which);
|
||||
ysuccess(false);
|
||||
|
@ -400,9 +413,17 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
|
|||
|
||||
owindow *current;
|
||||
|
||||
/* Error out early to not create a non-existing workspace (in
|
||||
* workspace_get()) if we are not actually able to move anything. */
|
||||
/* 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)) {
|
||||
ELOG("No windows match your criteria, cannot move.\n");
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
|
||||
ELOG("No window to move, you have focused a workspace.\n");
|
||||
ysuccess(false);
|
||||
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) {
|
||||
owindow *current;
|
||||
|
||||
/* Error out early to not create a non-existing workspace (in
|
||||
* workspace_get()) if we are not actually able to move anything. */
|
||||
if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
|
||||
/* We have nothing to move:
|
||||
* when criteria was specified but didn't match any window or
|
||||
* when criteria wasn't specified and we don't have any window focused. */
|
||||
if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
|
||||
(match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("should move window to workspace with number %d\n", which);
|
||||
LOG("should move window to workspace %s\n", which);
|
||||
/* get the workspace */
|
||||
Con *output, *workspace = NULL;
|
||||
|
||||
|
@ -463,14 +486,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
|||
child->num == parsed_num);
|
||||
|
||||
if (!workspace) {
|
||||
y(map_open);
|
||||
ystr("success");
|
||||
y(bool, false);
|
||||
ystr("error");
|
||||
// TODO: better error message
|
||||
ystr("No such workspace");
|
||||
y(map_close);
|
||||
return;
|
||||
workspace = workspace_get(which, NULL);
|
||||
}
|
||||
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
@ -504,6 +520,8 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
|
|||
LOG("tiling resize\n");
|
||||
/* get the appropriate current container (skip stacked/tabbed cons) */
|
||||
Con *current = focused;
|
||||
Con *other = NULL;
|
||||
double percentage = 0;
|
||||
while (current->parent->layout == L_STACKED ||
|
||||
current->parent->layout == L_TABBED)
|
||||
current = current->parent;
|
||||
|
@ -512,40 +530,50 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
|
|||
orientation_t search_orientation =
|
||||
(strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT);
|
||||
|
||||
while (current->type != CT_WORKSPACE &&
|
||||
current->type != CT_FLOATING_CON &&
|
||||
current->parent->orientation != search_orientation)
|
||||
current = current->parent;
|
||||
do {
|
||||
if (con_orientation(current->parent) != search_orientation) {
|
||||
current = current->parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* get the default percentage */
|
||||
int children = con_num_children(current->parent);
|
||||
Con *other;
|
||||
LOG("ins. %d children\n", children);
|
||||
double percentage = 1.0 / children;
|
||||
LOG("default percentage = %f\n", percentage);
|
||||
/* get the default percentage */
|
||||
int children = con_num_children(current->parent);
|
||||
LOG("ins. %d children\n", children);
|
||||
percentage = 1.0 / children;
|
||||
LOG("default percentage = %f\n", percentage);
|
||||
|
||||
orientation_t orientation = current->parent->orientation;
|
||||
orientation_t orientation = con_orientation(current->parent);
|
||||
|
||||
if ((orientation == HORIZ &&
|
||||
(strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
|
||||
(orientation == VERT &&
|
||||
(strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
|
||||
LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
|
||||
(orientation == HORIZ ? "horizontal" : "vertical"));
|
||||
if ((orientation == HORIZ &&
|
||||
(strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
|
||||
(orientation == VERT &&
|
||||
(strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
|
||||
LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
|
||||
(orientation == HORIZ ? "horizontal" : "vertical"));
|
||||
ysuccess(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) {
|
||||
other = TAILQ_PREV(current, nodes_head, nodes);
|
||||
} else {
|
||||
other = TAILQ_NEXT(current, nodes);
|
||||
}
|
||||
if (other == TAILQ_END(workspaces)) {
|
||||
LOG("No other container in this direction found, trying to look further up in the tree...\n");
|
||||
current = current->parent;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
} while (current->type != CT_WORKSPACE &&
|
||||
current->type != CT_FLOATING_CON);
|
||||
|
||||
if (other == NULL) {
|
||||
LOG("No other container in this direction found, trying to look further up in the tree...\n");
|
||||
ysuccess(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
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("current->percent before = %f\n", current->percent);
|
||||
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 &&
|
||||
current->type != CT_FLOATING_CON &&
|
||||
current->parent->orientation != search_orientation)
|
||||
con_orientation(current->parent) != search_orientation)
|
||||
current = current->parent;
|
||||
|
||||
/* get the default percentage */
|
||||
|
@ -594,7 +622,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
|
|||
double percentage = 1.0 / children;
|
||||
LOG("default percentage = %f\n", percentage);
|
||||
|
||||
orientation_t orientation = current->parent->orientation;
|
||||
orientation_t orientation = con_orientation(current->parent);
|
||||
|
||||
if ((orientation == HORIZ &&
|
||||
strcmp(direction, "height") == 0) ||
|
||||
|
@ -808,17 +836,15 @@ void cmd_workspace_number(I3_CMD, char *which) {
|
|||
child->num == parsed_num);
|
||||
|
||||
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);
|
||||
/* terminate the which string after the endposition of the number */
|
||||
*endptr = '\0';
|
||||
if (maybe_back_and_forth(cmd_output, which))
|
||||
return;
|
||||
workspace_show_by_name(which);
|
||||
cmd_output->needs_tree_render = true;
|
||||
return;
|
||||
}
|
||||
if (maybe_back_and_forth(cmd_output, which))
|
||||
if (maybe_back_and_forth(cmd_output, workspace->name))
|
||||
return;
|
||||
workspace_show(workspace);
|
||||
|
||||
|
@ -1088,9 +1114,17 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
|||
*
|
||||
*/
|
||||
void cmd_split(I3_CMD, char *direction) {
|
||||
owindow *current;
|
||||
/* TODO: use matches */
|
||||
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;
|
||||
// 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) {
|
||||
if (focused &&
|
||||
focused->type != CT_WORKSPACE &&
|
||||
focused->fullscreen_mode != CF_NONE) {
|
||||
LOG("Cannot change focus while in fullscreen mode.\n");
|
||||
ysuccess(false);
|
||||
return;
|
||||
DLOG("level = %s\n", level);
|
||||
bool success = false;
|
||||
|
||||
/* Focusing the parent can only be allowed if the newly
|
||||
* focused container won't escape the fullscreen container. */
|
||||
if (strcmp(level, "parent") == 0) {
|
||||
if (focused && focused->parent) {
|
||||
if (con_fullscreen_permits_focusing(focused->parent))
|
||||
success = level_up();
|
||||
else
|
||||
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)
|
||||
level_up();
|
||||
else level_down();
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
cmd_output->needs_tree_render = success;
|
||||
// XXX: default reply for now, make this a better reply
|
||||
ysuccess(true);
|
||||
ysuccess(success);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1275,13 +1312,9 @@ void cmd_focus(I3_CMD) {
|
|||
if (!ws)
|
||||
continue;
|
||||
|
||||
/* Don't allow the focus switch if the focused and current
|
||||
* containers are in the same workspace. */
|
||||
if (focused &&
|
||||
focused->type != CT_WORKSPACE &&
|
||||
focused->fullscreen_mode != CF_NONE &&
|
||||
con_get_workspace(focused) == ws) {
|
||||
LOG("Cannot change focus while in fullscreen mode (same workspace).\n");
|
||||
/* Check the fullscreen focus constraints. */
|
||||
if (!con_fullscreen_permits_focusing(current->con)) {
|
||||
LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n");
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
@ -1376,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) {
|
||||
if (strcmp(layout_str, "stacking") == 0)
|
||||
layout_str = "stacked";
|
||||
DLOG("changing layout to %s\n", layout_str);
|
||||
owindow *current;
|
||||
int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT :
|
||||
(strcmp(layout_str, "stacked") == 0 ? L_STACKED :
|
||||
L_TABBED));
|
||||
int layout;
|
||||
/* default is a special case which will be handled in con_set_layout(). */
|
||||
if (strcmp(layout_str, "default") == 0)
|
||||
layout = L_DEFAULT;
|
||||
else if (strcmp(layout_str, "stacked") == 0)
|
||||
layout = L_STACKED;
|
||||
else if (strcmp(layout_str, "tabbed") == 0)
|
||||
layout = L_TABBED;
|
||||
else if (strcmp(layout_str, "splitv") == 0)
|
||||
layout = L_SPLITV;
|
||||
else if (strcmp(layout_str, "splith") == 0)
|
||||
layout = L_SPLITH;
|
||||
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 */
|
||||
if (match_is_empty(current_match))
|
||||
con_set_layout(focused->parent, layout);
|
||||
con_set_layout(focused, layout);
|
||||
else {
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||
|
@ -1403,12 +1450,40 @@ void cmd_layout(I3_CMD, char *layout_str) {
|
|||
ysuccess(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of 'layout toggle [all|split]'.
|
||||
*
|
||||
*/
|
||||
void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
|
||||
owindow *current;
|
||||
|
||||
if (toggle_mode == NULL)
|
||||
toggle_mode = "default";
|
||||
|
||||
DLOG("toggling layout (mode = %s)\n", toggle_mode);
|
||||
|
||||
/* check if the match is empty, not if the result is empty */
|
||||
if (match_is_empty(current_match))
|
||||
con_toggle_layout(focused, toggle_mode);
|
||||
else {
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||
con_toggle_layout(current->con, toggle_mode);
|
||||
}
|
||||
}
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
// XXX: default reply for now, make this a better reply
|
||||
ysuccess(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementaiton of 'exit'.
|
||||
*
|
||||
*/
|
||||
void cmd_exit(I3_CMD) {
|
||||
LOG("Exiting due to user command.\n");
|
||||
xcb_disconnect(conn);
|
||||
exit(0);
|
||||
|
||||
/* unreached */
|
||||
|
@ -1421,6 +1496,7 @@ void cmd_exit(I3_CMD) {
|
|||
void cmd_reload(I3_CMD) {
|
||||
LOG("reloading\n");
|
||||
kill_configerror_nagbar(false);
|
||||
kill_commanderror_nagbar(false);
|
||||
load_configuration(conn, NULL, true);
|
||||
x_set_i3_atoms();
|
||||
/* Send an IPC event just in case the ws names have changed */
|
||||
|
@ -1449,6 +1525,7 @@ void cmd_restart(I3_CMD) {
|
|||
void cmd_open(I3_CMD) {
|
||||
LOG("opening new container\n");
|
||||
Con *con = tree_open_con(NULL, NULL);
|
||||
con->layout = L_SPLITH;
|
||||
con_focus(con);
|
||||
|
||||
y(map_open);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#undef I3__FILE__
|
||||
#define I3__FILE__ "commands_parser.c"
|
||||
/*
|
||||
* 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
|
||||
// called functions.
|
||||
static char *get_string(const char *identifier) {
|
||||
DLOG("Getting string %s from stack...\n", identifier);
|
||||
for (int c = 0; c < 10; c++) {
|
||||
if (stack[c].identifier == NULL)
|
||||
break;
|
||||
|
@ -115,7 +116,6 @@ static char *get_string(const char *identifier) {
|
|||
}
|
||||
|
||||
static void clear_stack(void) {
|
||||
DLOG("clearing stack.\n");
|
||||
for (int c = 0; c < 10; c++) {
|
||||
if (stack[c].str != NULL)
|
||||
free(stack[c].str);
|
||||
|
@ -187,8 +187,6 @@ static struct CommandResult command_output;
|
|||
|
||||
static void next_state(const cmdp_token *token) {
|
||||
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.needs_tree_render = false;
|
||||
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) {
|
||||
DLOG("new parser handling: %s\n", input);
|
||||
DLOG("COMMAND: *%s*\n", input);
|
||||
state = INITIAL;
|
||||
|
||||
/* 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++;
|
||||
|
||||
DLOG("remaining input = %s\n", walk);
|
||||
|
||||
cmdp_token_ptr *ptr = &(tokens[state]);
|
||||
token_handled = false;
|
||||
for (c = 0; c < ptr->n; c++) {
|
||||
token = &(ptr->array[c]);
|
||||
DLOG("trying token %d = %s\n", c, token->name);
|
||||
|
||||
/* A literal. */
|
||||
if (token->name[0] == '\'') {
|
||||
DLOG("literal\n");
|
||||
if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
|
||||
DLOG("found literal, moving to next state\n");
|
||||
if (token->identifier != NULL)
|
||||
push_string(token->identifier, sstrdup(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 ||
|
||||
strcmp(token->name, "word") == 0) {
|
||||
DLOG("parsing this as a string\n");
|
||||
const char *beginning = walk;
|
||||
/* Handle quoted strings (or words). */
|
||||
if (*walk == '"') {
|
||||
|
@ -310,7 +301,6 @@ struct CommandResult *parse_command(const char *input) {
|
|||
}
|
||||
if (token->identifier)
|
||||
push_string(token->identifier, str);
|
||||
DLOG("str is \"%s\"\n", str);
|
||||
/* If we are at the end of a quoted string, skip the ending
|
||||
* double quote. */
|
||||
if (*walk == '"')
|
||||
|
@ -322,9 +312,7 @@ struct CommandResult *parse_command(const char *input) {
|
|||
}
|
||||
|
||||
if (strcmp(token->name, "end") == 0) {
|
||||
DLOG("checking for the end token.\n");
|
||||
if (*walk == '\0' || *walk == ',' || *walk == ';') {
|
||||
DLOG("yes, indeed. end\n");
|
||||
next_state(token);
|
||||
token_handled = true;
|
||||
/* 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[len] = '\0';
|
||||
|
||||
printf("%s\n", errormessage);
|
||||
printf("Your command: %s\n", input);
|
||||
printf(" %s\n", position);
|
||||
ELOG("%s\n", errormessage);
|
||||
ELOG("Your command: %s\n", input);
|
||||
ELOG(" %s\n", position);
|
||||
|
||||
/* Format this error message as a JSON reply. */
|
||||
y(map_open);
|
||||
ystr("success");
|
||||
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(errormessage);
|
||||
ystr("input");
|
||||
|
@ -414,7 +407,6 @@ struct CommandResult *parse_command(const char *input) {
|
|||
|
||||
y(array_close);
|
||||
|
||||
DLOG("command_output.needs_tree_render = %d\n", command_output.needs_tree_render);
|
||||
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,
|
||||
* but only if the corresponding debug loglevel was activated.
|
||||
* but only if debug logging was activated.
|
||||
* This is to be called by DLOG() which includes filename/linenumber
|
||||
*
|
||||
*/
|
||||
void debuglog(uint64_t lev, char *fmt, ...) {
|
||||
void debuglog(char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "# ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void errorlog(char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
fprintf(stderr, "# ");
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
|
355
src/con.c
355
src/con.c
|
@ -1,3 +1,5 @@
|
|||
#undef I3__FILE__
|
||||
#define I3__FILE__ "con.c"
|
||||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
|
@ -133,7 +135,7 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) {
|
|||
*/
|
||||
if (con->window != NULL &&
|
||||
parent->type == CT_WORKSPACE &&
|
||||
config.default_layout != L_DEFAULT) {
|
||||
parent->workspace_layout != L_DEFAULT) {
|
||||
DLOG("Parent is a workspace. Applying default layout...\n");
|
||||
Con *target = workspace_attach_to(parent);
|
||||
|
||||
|
@ -217,8 +219,8 @@ bool con_accepts_window(Con *con) {
|
|||
if (con->type == CT_WORKSPACE)
|
||||
return false;
|
||||
|
||||
if (con->orientation != NO_ORIENTATION) {
|
||||
DLOG("container %p does not accepts windows, orientation != NO_ORIENTATION\n", con);
|
||||
if (con->split) {
|
||||
DLOG("container %p does not accept windows, it is a split container.\n", con);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -265,8 +267,11 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation) {
|
|||
while (con_orientation(parent) != orientation) {
|
||||
DLOG("Need to go one level further up\n");
|
||||
parent = parent->parent;
|
||||
/* Abort when we reach a floating con */
|
||||
if (parent && parent->type == CT_FLOATING_CON)
|
||||
/* Abort when we reach a floating con, or an output con */
|
||||
if (parent &&
|
||||
(parent->type == CT_FLOATING_CON ||
|
||||
parent->type == CT_OUTPUT ||
|
||||
(parent->parent && parent->parent->type == CT_OUTPUT)))
|
||||
parent = NULL;
|
||||
if (parent == NULL)
|
||||
break;
|
||||
|
@ -576,11 +581,27 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
|||
return;
|
||||
}
|
||||
|
||||
/* Prevent moving if this would violate the fullscreen focus restrictions. */
|
||||
if (!con_fullscreen_permits_focusing(workspace)) {
|
||||
LOG("Cannot move out of a fullscreen container");
|
||||
return;
|
||||
}
|
||||
|
||||
if (con_is_floating(con)) {
|
||||
DLOG("Using FLOATINGCON instead\n");
|
||||
con = con->parent;
|
||||
}
|
||||
|
||||
Con *source_ws = con_get_workspace(con);
|
||||
if (workspace == source_ws) {
|
||||
DLOG("Not moving, already there\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save the current workspace. So we can call workspace_show() by the end
|
||||
* of this function. */
|
||||
Con *current_ws = con_get_workspace(focused);
|
||||
|
||||
Con *source_output = con_get_output(con),
|
||||
*dest_output = con_get_output(workspace);
|
||||
|
||||
|
@ -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
|
||||
* occur if we move to the same workspace. Also show current workspace
|
||||
* to ensure it is focused. */
|
||||
workspace_show(con_get_workspace(focus_next));
|
||||
con_focus(con_descend_focused(focus_next));
|
||||
workspace_show(current_ws);
|
||||
|
||||
/* Set focus only if con was on current workspace before moving.
|
||||
* Otherwise we would give focus to some window on different workspace. */
|
||||
if (source_ws == current_ws)
|
||||
con_focus(con_descend_focused(focus_next));
|
||||
}
|
||||
|
||||
CALL(parent, on_remove_child);
|
||||
|
@ -679,14 +704,32 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
|||
*
|
||||
*/
|
||||
int con_orientation(Con *con) {
|
||||
/* stacking containers behave like they are in vertical orientation */
|
||||
if (con->layout == L_STACKED)
|
||||
return VERT;
|
||||
switch (con->layout) {
|
||||
case L_SPLITV:
|
||||
/* stacking containers behave like they are in vertical orientation */
|
||||
case L_STACKED:
|
||||
return VERT;
|
||||
|
||||
if (con->layout == L_TABBED)
|
||||
return HORIZ;
|
||||
case L_SPLITH:
|
||||
/* 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) {
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
return (Rect){0, 0, 0, 0};
|
||||
|
@ -910,6 +987,24 @@ Rect con_border_style_rect(Con *con) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns adjacent borders of the window. We need this if hide_edge_borders is
|
||||
* enabled.
|
||||
*/
|
||||
adjacent_t con_adjacent_borders(Con *con) {
|
||||
adjacent_t result = ADJ_NONE;
|
||||
Con *workspace = con_get_workspace(con);
|
||||
if (con->rect.x == workspace->rect.x)
|
||||
result |= ADJ_LEFT_SCREEN_EDGE;
|
||||
if (con->rect.x + con->rect.width == workspace->rect.x + workspace->rect.width)
|
||||
result |= ADJ_RIGHT_SCREEN_EDGE;
|
||||
if (con->rect.y == workspace->rect.y)
|
||||
result |= ADJ_UPPER_SCREEN_EDGE;
|
||||
if (con->rect.y + con->rect.height == workspace->rect.y + workspace->rect.height)
|
||||
result |= ADJ_LOWER_SCREEN_EDGE;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use this function to get a container’s border style. This is important
|
||||
* 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) {
|
||||
DLOG("con_set_layout(%p, %d), con->type = %d\n",
|
||||
con, layout, con->type);
|
||||
|
||||
/* Users can focus workspaces, but not any higher in the hierarchy.
|
||||
* Focus on the workspace is a special case, since in every other case, the
|
||||
* user means "change the layout of the parent split container". */
|
||||
if (con->type != CT_WORKSPACE)
|
||||
con = con->parent;
|
||||
|
||||
/* We fill in last_split_layout when switching to a different layout
|
||||
* since there are many places in the code that don’t use
|
||||
* con_set_layout(). */
|
||||
if (con->layout == L_SPLITH || con->layout == L_SPLITV)
|
||||
con->last_split_layout = con->layout;
|
||||
|
||||
/* When the container type is CT_WORKSPACE, the user wants to change the
|
||||
* whole workspace into stacked/tabbed mode. To do this and still allow
|
||||
* intuitive operations (like level-up and then opening a new window), we
|
||||
* need to create a new split container. */
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
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 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;
|
||||
if (con->type == CT_WORKSPACE &&
|
||||
(layout == L_STACKED || layout == L_TABBED)) {
|
||||
if (con_num_children(con) == 0) {
|
||||
DLOG("Setting workspace_layout to %d\n", layout);
|
||||
con->workspace_layout = layout;
|
||||
} 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;
|
||||
}
|
||||
|
||||
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)
|
||||
* or height (v-split) and use the maximum of the height (h-split) or width
|
||||
* (v-split) as minimum size. */
|
||||
if (con->orientation == HORIZ || con->orientation == VERT) {
|
||||
if (con->split) {
|
||||
uint32_t width = 0, height = 0;
|
||||
Con *child;
|
||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||
Rect min = con_minimum_size(child);
|
||||
if (con->orientation == HORIZ) {
|
||||
if (con->layout == L_SPLITH) {
|
||||
width += min.width;
|
||||
height = max(height, min.height);
|
||||
} else {
|
||||
|
@ -1130,7 +1304,70 @@ Rect con_minimum_size(Con *con) {
|
|||
return (Rect){ 0, 0, width, height };
|
||||
}
|
||||
|
||||
ELOG("Unhandled case, type = %d, layout = %d, orientation = %d\n",
|
||||
con->type, con->layout, con->orientation);
|
||||
ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
|
||||
con->type, con->layout, con->split);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if changing the focus to con would be allowed considering
|
||||
* the fullscreen focus constraints. Specifically, if a fullscreen container or
|
||||
* any of its descendants is focused, this function returns true if and only if
|
||||
* focusing con would mean that focus would still be visible on screen, i.e.,
|
||||
* the newly focused container would not be obscured by a fullscreen container.
|
||||
*
|
||||
* In the simplest case, if a fullscreen container or any of its descendants is
|
||||
* fullscreen, this functions returns true if con is the fullscreen container
|
||||
* itself or any of its descendants, as this means focus wouldn't escape the
|
||||
* boundaries of the fullscreen container.
|
||||
*
|
||||
* In case the fullscreen container is of type CF_OUTPUT, this function returns
|
||||
* true if con is on a different workspace, as focus wouldn't be obscured by
|
||||
* the fullscreen container that is constrained to a different workspace.
|
||||
*
|
||||
* Note that this same logic can be applied to moving containers. If a
|
||||
* container can be focused under the fullscreen focus constraints, it can also
|
||||
* become a parent or sibling to the currently focused container.
|
||||
*
|
||||
*/
|
||||
bool con_fullscreen_permits_focusing(Con *con) {
|
||||
/* No focus, no problem. */
|
||||
if (!focused)
|
||||
return true;
|
||||
|
||||
/* Find the first fullscreen ascendent. */
|
||||
Con *fs = focused;
|
||||
while (fs && fs->fullscreen_mode == CF_NONE)
|
||||
fs = fs->parent;
|
||||
|
||||
/* fs must be non-NULL since the workspace con doesn’t have CF_NONE and
|
||||
* there always has to be a workspace con in the hierarchy. */
|
||||
assert(fs != NULL);
|
||||
/* The most common case is we hit the workspace level. In this
|
||||
* situation, changing focus is also harmless. */
|
||||
assert(fs->fullscreen_mode != CF_NONE);
|
||||
if (fs->type == CT_WORKSPACE)
|
||||
return true;
|
||||
|
||||
/* Allow it if the container itself is the fullscreen container. */
|
||||
if (con == fs)
|
||||
return true;
|
||||
|
||||
/* If fullscreen is per-output, the focus being in a different workspace is
|
||||
* sufficient to guarantee that change won't leave fullscreen in bad shape. */
|
||||
if (fs->fullscreen_mode == CF_OUTPUT &&
|
||||
con_get_workspace(con) != con_get_workspace(fs)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Allow it only if the container to be focused is contained within the
|
||||
* current fullscreen container. */
|
||||
do {
|
||||
if (con->parent == fs)
|
||||
return true;
|
||||
con = con->parent;
|
||||
} while (con);
|
||||
|
||||
/* Focusing con would hide it behind a fullscreen window, disallow it. */
|
||||
return false;
|
||||
}
|
||||
|
|
156
src/config.c
156
src/config.c
|
@ -1,3 +1,5 @@
|
|||
#undef I3__FILE__
|
||||
#define I3__FILE__ "config.c"
|
||||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
|
@ -24,27 +26,27 @@ struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
|
|||
*
|
||||
*/
|
||||
void ungrab_all_keys(xcb_connection_t *conn) {
|
||||
DLOG("Ungrabbing all keys\n");
|
||||
xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
|
||||
DLOG("Ungrabbing all keys\n");
|
||||
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) {
|
||||
DLOG("Grabbing %d\n", keycode);
|
||||
/* Grab the key in all combinations */
|
||||
#define GRAB_KEY(modifier) \
|
||||
do { \
|
||||
xcb_grab_key(conn, 0, root, modifier, keycode, \
|
||||
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \
|
||||
} while (0)
|
||||
int mods = bind->mods;
|
||||
if ((bind->mods & BIND_MODE_SWITCH) != 0) {
|
||||
mods &= ~BIND_MODE_SWITCH;
|
||||
if (mods == 0)
|
||||
mods = XCB_MOD_MASK_ANY;
|
||||
}
|
||||
GRAB_KEY(mods);
|
||||
GRAB_KEY(mods | xcb_numlock_mask);
|
||||
GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
||||
DLOG("Grabbing %d\n", keycode);
|
||||
/* Grab the key in all combinations */
|
||||
#define GRAB_KEY(modifier) \
|
||||
do { \
|
||||
xcb_grab_key(conn, 0, root, modifier, keycode, \
|
||||
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \
|
||||
} while (0)
|
||||
int mods = bind->mods;
|
||||
if ((bind->mods & BIND_MODE_SWITCH) != 0) {
|
||||
mods &= ~BIND_MODE_SWITCH;
|
||||
if (mods == 0)
|
||||
mods = XCB_MOD_MASK_ANY;
|
||||
}
|
||||
GRAB_KEY(mods);
|
||||
GRAB_KEY(mods | xcb_numlock_mask);
|
||||
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.
|
||||
*
|
||||
*/
|
||||
Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode) {
|
||||
Binding *bind;
|
||||
Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode) {
|
||||
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) {
|
||||
/* First compare the modifiers */
|
||||
if (bind->mods != modifiers)
|
||||
continue;
|
||||
|
||||
/* If a symbol was specified by the user, we need to look in
|
||||
* the array of translated keycodes for the event’s keycode */
|
||||
if (bind->symbol != NULL) {
|
||||
if (memmem(bind->translated_to,
|
||||
bind->number_keycodes * sizeof(xcb_keycode_t),
|
||||
&keycode, sizeof(xcb_keycode_t)) != NULL)
|
||||
break;
|
||||
} else {
|
||||
/* This case is easier: The user specified a keycode */
|
||||
if (bind->keycode == keycode)
|
||||
break;
|
||||
}
|
||||
if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
|
||||
bind->release = B_UPON_KEYRELEASE;
|
||||
}
|
||||
|
||||
return (bind == TAILQ_END(bindings) ? NULL : bind);
|
||||
/* Then we transition the KeyRelease bindings into a state where the
|
||||
* modifiers no longer matter for the KeyRelease event so that users
|
||||
* can release the modifier key before releasing the actual key. */
|
||||
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||
if (bind->release == B_UPON_KEYRELEASE && !key_release)
|
||||
bind->release = B_UPON_KEYRELEASE_IGNORE_MODS;
|
||||
}
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||
/* First compare the modifiers (unless this is a
|
||||
* B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
|
||||
* event) */
|
||||
if (bind->mods != modifiers &&
|
||||
(bind->release != B_UPON_KEYRELEASE_IGNORE_MODS ||
|
||||
!key_release))
|
||||
continue;
|
||||
|
||||
/* Check if the binding is for a KeyPress or a KeyRelease event */
|
||||
if ((bind->release == B_UPON_KEYPRESS && key_release) ||
|
||||
(bind->release >= B_UPON_KEYRELEASE && !key_release))
|
||||
continue;
|
||||
|
||||
/* If a symbol was specified by the user, we need to look in
|
||||
* the array of translated keycodes for the event’s keycode */
|
||||
if (bind->symbol != NULL) {
|
||||
if (memmem(bind->translated_to,
|
||||
bind->number_keycodes * sizeof(xcb_keycode_t),
|
||||
&keycode, sizeof(xcb_keycode_t)) != NULL)
|
||||
break;
|
||||
} else {
|
||||
/* This case is easier: The user specified a keycode */
|
||||
if (bind->keycode == keycode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (bind == TAILQ_END(bindings) ? NULL : bind);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -131,22 +159,22 @@ void translate_keysyms(void) {
|
|||
*
|
||||
*/
|
||||
void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
|
||||
Binding *bind;
|
||||
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||
if ((bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
|
||||
(!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0))
|
||||
continue;
|
||||
Binding *bind;
|
||||
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||
if ((bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
|
||||
(!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0))
|
||||
continue;
|
||||
|
||||
/* The easy case: the user specified a keycode directly. */
|
||||
if (bind->keycode > 0) {
|
||||
grab_keycode_for_binding(conn, bind, bind->keycode);
|
||||
continue;
|
||||
}
|
||||
|
||||
xcb_keycode_t *walk = bind->translated_to;
|
||||
for (int i = 0; i < bind->number_keycodes; i++)
|
||||
grab_keycode_for_binding(conn, bind, *walk++);
|
||||
/* The easy case: the user specified a keycode directly. */
|
||||
if (bind->keycode > 0) {
|
||||
grab_keycode_for_binding(conn, bind, bind->keycode);
|
||||
continue;
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
if (strcasecmp(mode->name, new_mode) != 0)
|
||||
continue;
|
||||
SLIST_FOREACH(mode, &modes, modes) {
|
||||
if (strcasecmp(mode->name, new_mode) != 0)
|
||||
continue;
|
||||
|
||||
ungrab_all_keys(conn);
|
||||
bindings = mode->bindings;
|
||||
translate_keysyms();
|
||||
grab_all_keys(conn, false);
|
||||
return;
|
||||
}
|
||||
ungrab_all_keys(conn);
|
||||
bindings = mode->bindings;
|
||||
translate_keysyms();
|
||||
grab_all_keys(conn, false);
|
||||
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);
|
||||
}
|
||||
|
||||
if (config.font.id == 0) {
|
||||
if (config.font.type == FONT_TYPE_NONE) {
|
||||
ELOG("You did not specify required configuration option \"font\"\n");
|
||||
config.font = load_font("fixed", true);
|
||||
set_font(&config.font);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#undef I3__FILE__
|
||||
#define I3__FILE__ "debug.c"
|
||||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
#undef I3__FILE__
|
||||
#define I3__FILE__ "key_press.c"
|
||||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||
*
|
||||
* display_version.c: displays the running i3 version, runs as part of
|
||||
* i3 --moreversion.
|
||||
*
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <fcntl.h>
|
||||
#include "all.h"
|
||||
|
||||
static bool human_readable_key;
|
||||
static char *human_readable_version;
|
||||
|
||||
#if YAJL_MAJOR >= 2
|
||||
static int version_string(void *ctx, const unsigned char *val, size_t len) {
|
||||
#else
|
||||
static int version_string(void *ctx, const unsigned char *val, unsigned int len) {
|
||||
#endif
|
||||
if (human_readable_key)
|
||||
sasprintf(&human_readable_version, "%.*s", (int)len, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if YAJL_MAJOR >= 2
|
||||
static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
|
||||
#else
|
||||
static int version_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
|
||||
#endif
|
||||
human_readable_key = (stringlen == strlen("human_readable") &&
|
||||
strncmp((const char*)stringval, "human_readable", strlen("human_readable")) == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static yajl_callbacks version_callbacks = {
|
||||
NULL, /* null */
|
||||
NULL, /* boolean */
|
||||
NULL, /* integer */
|
||||
NULL, /* double */
|
||||
NULL, /* number */
|
||||
&version_string,
|
||||
NULL, /* start_map */
|
||||
&version_map_key,
|
||||
NULL, /* end_map */
|
||||
NULL, /* start_array */
|
||||
NULL /* end_array */
|
||||
};
|
||||
|
||||
/*
|
||||
* Connects to i3 to find out the currently running version. Useful since it
|
||||
* might be different from the version compiled into this binary (maybe the
|
||||
* user didn’t correctly install i3 or forgot te restart it).
|
||||
*
|
||||
* The output looks like this:
|
||||
* Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804)
|
||||
*
|
||||
* The i3 binary you just called: /home/michael/i3/i3
|
||||
* The i3 binary you are running: /home/michael/i3/i3
|
||||
*
|
||||
*/
|
||||
void display_running_version(void) {
|
||||
char *socket_path = root_atom_contents("I3_SOCKET_PATH");
|
||||
if (socket_path == NULL)
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
char *pid_from_atom = root_atom_contents("I3_PID");
|
||||
if (pid_from_atom == NULL) {
|
||||
/* If I3_PID is not set, the running version is older than 4.2-200. */
|
||||
printf("\nRunning version: < 4.2-200\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* Inform the user of what we are doing. While a single IPC request is
|
||||
* really fast normally, in case i3 hangs, this will not terminate. */
|
||||
printf("(Getting version from running i3, press ctrl-c to abort…)");
|
||||
fflush(stdout);
|
||||
|
||||
/* TODO: refactor this with the code for sending commands */
|
||||
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
if (sockfd == -1)
|
||||
err(EXIT_FAILURE, "Could not create socket");
|
||||
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_LOCAL;
|
||||
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
||||
if (connect(sockfd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0)
|
||||
err(EXIT_FAILURE, "Could not connect to i3");
|
||||
|
||||
if (ipc_send_message(sockfd, 0, I3_IPC_MESSAGE_TYPE_GET_VERSION,
|
||||
(uint8_t*)"") == -1)
|
||||
err(EXIT_FAILURE, "IPC: write()");
|
||||
|
||||
uint32_t reply_length;
|
||||
uint8_t *reply;
|
||||
int ret;
|
||||
if ((ret = ipc_recv_message(sockfd, I3_IPC_MESSAGE_TYPE_GET_VERSION,
|
||||
&reply_length, &reply)) != 0) {
|
||||
if (ret == -1)
|
||||
err(EXIT_FAILURE, "IPC: read()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#if YAJL_MAJOR >= 2
|
||||
yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL);
|
||||
#else
|
||||
yajl_parser_config parse_conf = { 0, 0 };
|
||||
|
||||
yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, NULL);
|
||||
#endif
|
||||
|
||||
yajl_status state = yajl_parse(handle, (const unsigned char*)reply, (int)reply_length);
|
||||
if (state != yajl_status_ok)
|
||||
errx(EXIT_FAILURE, "Could not parse my own reply. That's weird. reply is %.*s", (int)reply_length, reply);
|
||||
|
||||
printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom);
|
||||
|
||||
#ifdef __linux__
|
||||
char exepath[PATH_MAX],
|
||||
destpath[PATH_MAX];
|
||||
ssize_t linksize;
|
||||
|
||||
snprintf(exepath, sizeof(exepath), "/proc/%d/exe", getpid());
|
||||
|
||||
if ((linksize = readlink(exepath, destpath, sizeof(destpath))) == -1)
|
||||
err(EXIT_FAILURE, "readlink(%s)", exepath);
|
||||
|
||||
/* readlink() does not NULL-terminate strings, so we have to. */
|
||||
destpath[linksize] = '\0';
|
||||
|
||||
printf("\n");
|
||||
printf("The i3 binary you just called: %s\n", destpath);
|
||||
|
||||
snprintf(exepath, sizeof(exepath), "/proc/%s/exe", pid_from_atom);
|
||||
|
||||
if ((linksize = readlink(exepath, destpath, sizeof(destpath))) == -1)
|
||||
err(EXIT_FAILURE, "readlink(%s)", exepath);
|
||||
|
||||
/* readlink() does not NULL-terminate strings, so we have to. */
|
||||
destpath[linksize] = '\0';
|
||||
|
||||
/* Check if "(deleted)" is the readlink result. If so, the running version
|
||||
* does not match the file on disk. */
|
||||
if (strstr(destpath, "(deleted)") != NULL)
|
||||
printf("RUNNING BINARY DIFFERENT FROM BINARY ON DISK!\n");
|
||||
|
||||
/* Since readlink() might put a "(deleted)" somewhere in the buffer and
|
||||
* stripping that out seems hackish and ugly, we read the process’s argv[0]
|
||||
* instead. */
|
||||
snprintf(exepath, sizeof(exepath), "/proc/%s/cmdline", pid_from_atom);
|
||||
|
||||
int fd;
|
||||
if ((fd = open(exepath, O_RDONLY)) == -1)
|
||||
err(EXIT_FAILURE, "open(%s)", exepath);
|
||||
if (read(fd, destpath, sizeof(destpath)) == -1)
|
||||
err(EXIT_FAILURE, "read(%s)", exepath);
|
||||
close(fd);
|
||||
|
||||
printf("The i3 binary you are running: %s\n", destpath);
|
||||
#endif
|
||||
|
||||
yajl_free(handle);
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
#undef I3__FILE__
|
||||
#define I3__FILE__ "ewmh.c"
|
||||
/*
|
||||
* vim:ts=4:sw=4:expandtab
|
||||
*
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue