Merge branch 'next' into master

This commit is contained in:
Michael Stapelberg 2016-03-06 16:17:27 +01:00
commit 988cc3ccaf
158 changed files with 5951 additions and 2458 deletions

18
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,18 @@
Output of `i3 --moreversion 2>&- || i3 --version`:
_REPLACE: i3 version output_
URL to a logfile as per http://i3wm.org/docs/debugging.html:
_REPLACE: URL to logfile_
**What I did:**
_REPLACE: e.g. "Im pressing Alt+j (focus left)"_
**What I saw:**
_REPLACE: e.g. "i3 changed focus to the window ABOVE the current window"_
**What I expected instead:**
_REPLACE: e.g. "Focus should be on the window to the left"_

View File

@ -1,72 +1,23 @@
sudo: required
dist: trusty
services:
- docker
language: c language: c
compiler: compiler:
- gcc - gcc
- clang - clang
addons: env:
apt: global:
sources: - BASENAME="i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh)"
- llvm-toolchain-precise-3.5 - secure: "B5IICA8MPx/FKaB50rTPqL8P1NU+Q0yuWl+lElL4+a9xSyLikfm3NzUPHoVwx8lNw2AVK6br7p0OmF7vMFjqAgrgc1cajTtEae5uFRKNUrWLpXM046YgNEYLLIHsQOjInxE+S4O6EFVzsUqsu8aeo2Xhq4sm4iUocG7e5isYgYo=" # DOCKER_PASS
# ubuntu-toolchain-r-test contains libstdc++6 >= 4.8 which libllvm3.5 needs. - secure: "EIvrq8PG7lRjidppG0RCv4F0X4GP3DT9F5+ixVuGPfhK/hZT3jYC2AVY9G+NnUcXVwQEpW92rlqpftQ/qZ13FoyWokC8ZyoyD06fr5FPCfoFF3OczZwAJzZYkObI/hE9+/hXcylx/Os6N4INd2My1ntGk3JPsWL9riopod5EjSg=" # DOCKER_EMAIL
- ubuntu-toolchain-r-test - secure: "hvhBunS4xXTgnIOsk/BPT7I7FrJhvVwCSt5PfxxvMqNaztOJI9BuK7ZrZ5Cy38KyHwlh3VHAH5AaCygJcPauoSQCV3bpnlbaWn3ruq2F0Q697Q5uNf73liXzyUqb9/Zvfvge4y4WWOhP5tVz1C6ZBe/NfhU7pqKLMA+6ads+99c=" # DOCKER_USER
packages:
- clang-format-3.5
- libllvm3.5
before_install:
# The travis VMs run on Ubuntu 12.04 which is very old and a huge pain to get
# into a state where we can build a recent version of i3 :(.
- "echo 'deb http://archive.ubuntu.com/ubuntu/ trusty main universe' | sudo tee /etc/apt/sources.list.d/trusty.list"
- "echo 'APT::Default-Release \"precise\";' | sudo tee /etc/apt/apt.conf.d/default-release"
- "echo 'Package: libc6' > /tmp/pin"
- "echo 'Pin: release n=trusty' >> /tmp/pin"
- "echo 'Pin-Priority: 999' >> /tmp/pin"
- "echo '' >> /tmp/pin"
- "echo 'Package: libxkbcommon*' >> /tmp/pin"
- "echo 'Pin: release n=trusty' >> /tmp/pin"
- "echo 'Pin-Priority: 999' >> /tmp/pin"
- "echo '' >> /tmp/pin"
- "echo 'Package: libyajl*' >> /tmp/pin"
- "echo 'Pin: release n=trusty' >> /tmp/pin"
- "echo 'Pin-Priority: 999' >> /tmp/pin"
- "echo '' >> /tmp/pin"
- "echo 'Package: libxcb-image*' >> /tmp/pin"
- "echo 'Pin: release n=trusty' >> /tmp/pin"
- "echo 'Pin-Priority: 999' >> /tmp/pin"
- "echo '' >> /tmp/pin"
- sudo cp /tmp/pin /etc/apt/preferences.d/trustypin
- sudo apt-get update
- sudo apt-get install -t trusty libc6 libc6-dev
- sudo apt-get install --no-install-recommends devscripts equivs xdotool
install: install:
- sudo mk-build-deps --install --remove --tool 'apt-get --no-install-recommends' debian/control - if [ -a .git/shallow ]; then git fetch --unshallow; fi
# Install as many dependencies as possible via apt because cpanm is not very reliable/easy to debug. - docker pull ${BASENAME} || ./travis/docker-build-and-push.sh
- sudo apt-get install --no-install-recommends libanyevent-perl libanyevent-i3-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl-modules libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libtest-use-ok-perl libipc-run-perl
- sudo /bin/sh -c 'cpanm -n -v X11::XCB || true'
- sudo /bin/sh -c 'cpanm -n -v AnyEvent::I3 || true'
script: script:
- CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror" make -j - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-safe-wrappers.sh
- (cd testcases && xvfb-run ./complete-run.pl --parallel=1 || (cat latest/complete-run.log; false)) - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-formatting.sh
- clang-format-3.5 -i $(find . -name "*.[ch]" | tr '\n' ' ') && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false) - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC -e CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror" ${BASENAME} make all mans -j ASAN=1
- | - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-spelling.pl
funcs='malloc|calloc|realloc|strdup|strndup|asprintf|write' - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/run-tests.sh
cstring='"([^"\\]|\\.)*"'
cchar="'[^\\\\]'|'\\\\.[^']*'"
regex="^([^'\"]|${cstring}|${cchar})*\<(${funcs})\>"
detected=0
while IFS= read -r file; do
if { cpp -w -fpreprocessed "$file" || exit "$?"; } | grep -E -- "$regex"; then
echo "^ $file calls a function that has a safe counterpart."
detected=1
fi
done << EOF
$(find -name '*.c' -not -name safewrappers.c -not -name strndup.c)
EOF
if [ "$detected" -ne 0 ]; then
echo
echo "Calls of functions that have safe counterparts were detected."
exit 1
fi

View File

@ -24,7 +24,7 @@
│ PCRE │ 8.12 │ 8.35 │ http://www.pcre.org/ │ │ PCRE │ 8.12 │ 8.35 │ http://www.pcre.org/ │
│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification │ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
│ pango │ 1.30.0 | 1.36.8 │ http://www.pango.org/ │ │ pango │ 1.30.0 | 1.36.8 │ http://www.pango.org/ │
│ cairo │ 1.12.2 │ 1.14.0 │ http://cairographics.org/ │ │ cairo │ 1.14.4 │ 1.14.4 │ http://cairographics.org/ │
└──────────────┴────────┴────────┴────────────────────────────────────────┘ └──────────────┴────────┴────────┴────────────────────────────────────────┘
¹ libsn = libstartup-notification ¹ libsn = libstartup-notification
² Pod::Simple is a Perl module required for converting the testsuite ² Pod::Simple is a Perl module required for converting the testsuite

View File

@ -1,4 +1,4 @@
Copyright © 2009-2011, Michael Stapelberg and contributors Copyright © 2009, Michael Stapelberg and contributors
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@ -40,7 +40,7 @@ start of i3 (it will automatically exit if it finds a config file).
If you have any questions, ideas, hints, problems or whatever, please do not If you have any questions, ideas, hints, problems or whatever, please do not
hesitate to contact me. I will help you out. Just drop me an E-Mail (find the hesitate to contact me. I will help you out. Just drop me an E-Mail (find the
address at http://michael.stapelberg.de/Kontakt, scroll down to bottom), address at https://michael.stapelberg.de/Impressum/, scroll down to bottom),
contact me using the same address in jabber or ask on our IRC channel: contact me using the same address in jabber or ask on our IRC channel:
(#i3 on irc.twice-irc.de). (#i3 on irc.twice-irc.de).

View File

@ -1,115 +0,0 @@
┌────────────────────────────┐
│ Release notes for i3 v4.11 │
└────────────────────────────┘
This is i3 v4.11. This version is considered stable. All users of i3 are
strongly encouraged to upgrade.
Aside from plenty of new features, there are several changes that might result
in behavioral changes, depending on your specific configuration and
environment. As usual, we tried hard to keep the current behavior, and when we
changed behavior, we strongly believe its for the better.
Keyboard binding handling has been made more correct, for details see:
https://github.com/i3/i3/commit/bf3cd41b5ddf1e757515ab5fbf811be56e5f69cc
┌────────────────────────────┐
│ Changes in i3 v4.11 │
└────────────────────────────┘
• docs/debugging: provide instructions on how to debug i3bar
• docs/debugging: added a note about sensitive data
• docs/userguide: add a note to both “exec”s about semicolon and comma
• docs/userguide: quoted strings need to be used, escaping isnt possible
• docs/userguide: make syntax of syntax descriptions consistent
• docs/userguide: recommend “exec exec” for correct signal handling
• docs/userguide: explain i3-config-wizards behavior
• i3-nagbar: open on the primary screen
• i3-config-wizard: respect XDG config directories
• i3-input: position i3-input at window with input focus
• i3bar: use a reasonable default sep_block_width if a separator_symbol is given
• i3bar: add binding mode indicator
• i3bar: add bindsym command (deprecates wheel_{up,down}_cmd)
• i3bar: make tray padding configurable
• makefiles: respect EXEC_PREFIX and PKG_CONFIG
• added a --toggle switch to mark: “mark [--toggle] <mark>”
• added “focus_on_window_activation” directive
• added “no_focus” directive
• added “move [container|window] [to] mark <str>” command
• added “move [window|container] [to] position mouse|cursor|pointer” command
• added “title_format” command
• added “resize set [width] [height]” command
• added “sticky” command (for floating containers)
• added “workspace” criterion
• added “window_type” criterion
• make center coordinates relative to current workspace
• draw marks in window decoration (configure with show_marks)
• only mark a window if only one window is matched
• make floating window mouse handling consistent with tiled windows
• add a --border flag to enable mouse binds to trigger on border click
• set the _NET_WM_STATE_HIDDEN atom on windows that are currently not visible
due to being in the non-focused tab of a stacked or tabbed container
• ignore InputHint when not in WM_HINTS
• display which config is used in i3 --moreversion
• support config file line continuation
• use WM_SIZE_HINTS when present to set the geometry of floating windows
• add “tray_output primary” to the default config
• use libxkbcommon for translating keysyms, support all XKB groups
• support special value “__focused__” in criteria
• support _NET_WM_VISIBLE_NAME
• make sure borders are never counted as adjacent to the edge for floating
containers
• support moving dock clients to another output
• let “focus” report success depending on whether a window was matched
• handle _NET_WM_STATE_STICKY (for floating containers)
• make “debuglog on” command persist over restarts
• randr: use root window in case of no randr outputs
• set proper WM_CLASS on frame windows
┌────────────────────────────┐
│ Bugfixes │
└────────────────────────────┘
• i3bar: only detect clicks within the statusline width
• i3bar: fix flickering shortened status bar on other output(s)
• i3bar: send custom-defined command upon click on the non-statusline part of
i3bar even if workspace_buttons is set to “no”.
• i3-config-wizard: Make window size and click coordinates dependent on font
• i3-save-tree: retain “rect” for floating cons
• move urgency hint when moving container
• fix percents when attaching a window to a ws creates a new split con
• cope with non-null-terminated x class properties
• get workspace name when renaming current workspace
• allow single-child non-default layout cons to be moved between outputs
• allow --whole-window right after 'bindsym' within binding modes
• remove windows from the save set when unmapping (fixes problems with e.g.
owncloud when restarting i3)
• serialize con_id with %p in run_binding()
• initialize workspace rect to the output's upon creation
• mkdirp: do not throw an error if directory exists
• grab all buttons when managing a window to also allow 'bindsym
--whole-window button4 …' to work correctly
• properly clear the urgency hint when set by i3
• layout restore: load floating containers correctly
• layout restore: remove remaining criteria when swallowing window
• layout restore: When appending a layout containing a marked container, make
sure that any other containers with the same mark are unmarked during
insertion of the new container.
• use the EWMH support window rather than the root window as an input focus fallback
• use the focused container to determine the target window_mode when using
floating mode_toggle
┌────────────────────────────┐
│ Thanks! │
└────────────────────────────┘
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
Andrzej Pronobis, Chris West (Faux), Deiz, Felix C. Stegerman, Georgiy Tugai,
hwangcc23, Ingo Bürk, Kacper Kowalik (Xarthisius), lasers, lambithal, Michael
Hofmann, Michael Tipton, Micha Rosenbaum, Nikita Mikhailov, Nils Schneider,
PopeLevi, rr-, shdown, Simon Nagl, Theo Buehler, Thomas Anderson, Tim Creech,
Tony Crisci
-- Michael Stapelberg, 2015-09-30

132
RELEASE-NOTES-4.12 Normal file
View File

@ -0,0 +1,132 @@
┌────────────────────────────┐
│ Release notes for i3 v4.12 │
└────────────────────────────┘
This is i3 v4.12. This version is considered stable. All users of i3 are
strongly encouraged to upgrade.
If cairo ≥ 1.14.4 is available, i3 and i3bar will use cairo for rendering
(instead of raw X11 drawing primitives). While this is currently optional,
having cairo ≥ 1.14.4 will be a hard requirement in future release.
This release contains a good number of detail improvements and fixes.
┌────────────────────────────┐
│ Changes in i3 v4.12 │
└────────────────────────────┘
• use https instead of git/http, update contact information, add GPG key
• docs/hacking-howto: fix old cfgparse.y reference to config_parser.c
• docs/ipc: added link to i3ipcpp (C++ library)
• docs/userguide: clarify no_focus documentation
• docs/userguide: add documentation for binding modes
• docs/userguide: fix rendering of __focused__
• docs/userguide: improve placement of explicit IDs for headings
• docs/userguide: make rendering of key bindings more consistent
• docs/userguide: clarify quoting of “exec” commands
• man/i3-nagbar: fix example invocation
• man/i3: add “floating window” to terminology
• i3-sensible-*: quote variables correctly
• i3-sensible-editor: add neovim
• i3-sensible-terminal: add termit, st
• i3bar: use cairo for all drawing operations
• i3bar: support per-statusblock border and background colors
• i3bar: support different bar background colors depending on whether the bar
is on the focused output or not
• i3bar: multiple tray_output directives on the same bar are now supported
• i3bar: support disabling the modifier by specifying “modifier none”
• use cairo for all drawing operations
• fix a number of memory leaks, thanks to AddressSanitizer
• no_focus is now suppressed for the first window of a workspace
• “workspace next/prev” now looks for numbered workspaces after reaching the
last workspace (it used to incorrectly only look at named workspaces)
• multiple marks can now be set on a single window (but a mark can still only
be present on one window at a time)
• the “unmark” command now supports criteria
• the “con_id” criterion now supports the special value __focused__
• the “workspace” command now supports the --no-auto-back-and-forth parameter
• the “move window to workspace” command now supports the
--no-auto-back-and-forth parameter
• the “resize grow|shrink width|height” command now works for a nested split
in the same direction
• support _NET_WM_USER_TIMEs special 0 value, indicating that a window
should not be focused
• use 32-bit visual by default if available. This reduces graphical glitches
when using transparency (which is still not officially supported)
• the “move position center” command now supports criteria
• specifying invalid match criteria now results in an error instead of
blindly applying the operation to the currently focused window
• allow mouse bindings to run on the root window
• support matching _NET_WM_WINDOW_TYPE_NOTIFICATION in criteria
• all criteria are now matched, even when con_id or con_mark are given (used
to be a special case)
• allow the “id” criterion to be specified in any base recognized by
strtol(), not only base 10
• non-true color displays are now supported again (e.g. the Raspberry Pi)
• the “split” command now has a “toggle” option
• the additional color class “decoration_border” was added
• title_format is now stored on containers instead of windows, allowing the
use of title_format on split containers
• On OpenBSD, i3 now uses pledge(2)
• support _NET_WM_DESKTOP (for pager applications like gnome-panel)
• floating workspaces are no longer available (they were not supported for a
while now)
• floating windows now carry the I3_FLOATING_WINDOW atom so that tools like
compositors can be configured to match on floating windows
┌────────────────────────────┐
│ Bugfixes │
└────────────────────────────┘
• i3bar: display short text only on the monitor(s) on which it is necessary
• i3bar: explicitly set cursor using libxcb-cursor if available
• i3bar: fix XEMBED messages
• i3-nagbar: explicitly set cursor using libxcb-cursor if available
• duplicated keybindings are now also detected when one uses bindcode but the
other(s) use(s) bindsym
• keymap fallback for servers without XKB (e.g. TightVNC) has been added
• using pango markup in mode names is now optional, fixing a regression in i3
v4.11 where modes which contained characters such as “<” would break.
• moving windows to a workspace by specifying a mark now works
• the root output is now used when any RandR request fails (for x2go)
• assignments are now marked as run before executing them, preventing endless
loops/crashes when assignments cause another assignment evaluation
• splitting/floating a dock container no longer crashes i3
• correctly compare modifier mask when identifying keybindings (fixes
bindings which use --release)
• no longer fail config validation when there is no newline at the end of
the config file
• scrollwheel buttons are now only grabbed when necessary, allowing the use
of “bindsym button*” or scrolling in windows without focusing them (in case
no “bindsym button*” is present)
• parse con_id in base 16 (affected FreeBSD only)
• fix crash when opening a large number of windows
• reject empty swallow definitions to avoid crashes
• dont remove SubstructureRedirect event mask temporarily (fixes i3bar
stopping after system suspend)
• move urgent flag before killing the parent to avoid a crash
• correctly validate “kill” command to avoid crashing when “kill” is invoked
on workspace containers
• actually accept the documented “workspace” token as an alternative to “→”
in assign statements
• remove _NET_WM_STATE on withdrawn windows to comply with the spec
• the “border” command now uses logical pixels (relevant for hi-dpi displays)
• “tray_output primary” does not properly fall back and hence was removed
from the default config again
• correctly determine focused workspace when moving workspace to output
• revert to default binding mode before reloading the config file
• correctly interpret _MOTIF_WM_HINTS (endianness-dependent)
┌────────────────────────────┐
│ Thanks! │
└────────────────────────────┘
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
Adaephon, Airblader, Alexis211, bendem, botovq, brianmillar, DavidMikeSimon,
dcoppa, Florian Merkel, fmthoma, frederik, hwangcc23, jolange, Juuso
Lapinlampi, kneitinger, lotheac, nicklan, norrland, pra85, romanblanco,
sur5r, tbu-, tyll, wodny
-- Michael Stapelberg, 2016-03-06

View File

@ -1,5 +1,6 @@
UNAME=$(shell uname) UNAME=$(shell uname)
DEBUG=1 DEBUG=1
ASAN=0
INSTALL=install INSTALL=install
LN=ln LN=ln
PKG_CONFIG=pkg-config PKG_CONFIG=pkg-config
@ -42,6 +43,11 @@ else
CFLAGS ?= -pipe -O2 -freorder-blocks-and-partition CFLAGS ?= -pipe -O2 -freorder-blocks-and-partition
endif endif
ifeq ($(ASAN),1)
CFLAGS += -fsanitize=address -DI3_ASAN_ENABLED
LDFLAGS += -fsanitize=address
endif
# Default LDFLAGS that users should be able to override # Default LDFLAGS that users should be able to override
LDFLAGS ?= $(as_needed_LDFLAG) LDFLAGS ?= $(as_needed_LDFLAG)
@ -109,15 +115,15 @@ 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-xinerama,xcb-xinerama)
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr) XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
# XCB cursor
XCB_CURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor)
XCB_CURSOR_LIBS := $(call ldflags_for_lib, xcb-cursor,xcb-cursor)
XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon) XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon)
XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon) XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon)
XKB_COMMON_X11_CFLAGS := $(call cflags_for_lib, xkbcommon-x11,xkbcommon-x11) XKB_COMMON_X11_CFLAGS := $(call cflags_for_lib, xkbcommon-x11,xkbcommon-x11)
XKB_COMMON_X11_LIBS := $(call ldflags_for_lib, xkbcommon-x11,xkbcommon-x11) XKB_COMMON_X11_LIBS := $(call ldflags_for_lib, xkbcommon-x11,xkbcommon-x11)
# Xcursor
XCURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor)
XCURSOR_LIBS := $(call ldflags_for_lib, xcb-cursor,xcb-cursor)
# yajl # yajl
YAJL_CFLAGS := $(call cflags_for_lib, yajl) YAJL_CFLAGS := $(call cflags_for_lib, yajl)
YAJL_LIBS := $(call ldflags_for_lib, yajl,yajl) YAJL_LIBS := $(call ldflags_for_lib, yajl,yajl)
@ -141,6 +147,9 @@ LIBSN_LIBS := $(call ldflags_for_lib, libstartup-notification-1.0,startup-noti
PANGO_CFLAGS := $(call cflags_for_lib, cairo) PANGO_CFLAGS := $(call cflags_for_lib, cairo)
PANGO_CFLAGS += $(call cflags_for_lib, pangocairo) PANGO_CFLAGS += $(call cflags_for_lib, pangocairo)
I3_CPPFLAGS += -DPANGO_SUPPORT=1 I3_CPPFLAGS += -DPANGO_SUPPORT=1
ifeq ($(shell $(PKG_CONFIG) --atleast-version=1.14.4 cairo 2>/dev/null && echo 1),1)
I3_CPPFLAGS += -DCAIRO_SUPPORT=1
endif
PANGO_LIBS := $(call ldflags_for_lib, cairo) PANGO_LIBS := $(call ldflags_for_lib, cairo)
PANGO_LIBS += $(call ldflags_for_lib, pangocairo) PANGO_LIBS += $(call ldflags_for_lib, pangocairo)

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
i3-wm (4.11.1-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Wed, 30 Sep 2015 09:03:26 +0200
i3-wm (4.11-1) unstable; urgency=medium i3-wm (4.11-1) unstable; urgency=medium
* New upstream release. * New upstream release.

2
debian/compat vendored
View File

@ -1 +1 @@
7 9

4
debian/control vendored
View File

@ -2,7 +2,7 @@ Source: i3-wm
Section: x11 Section: x11
Priority: extra Priority: extra
Maintainer: Michael Stapelberg <stapelberg@debian.org> Maintainer: Michael Stapelberg <stapelberg@debian.org>
Build-Depends: debhelper (>= 7.0.50~), Build-Depends: debhelper (>= 9),
libx11-dev, libx11-dev,
libxcb-util0-dev (>= 0.3.8), libxcb-util0-dev (>= 0.3.8),
libxcb-keysyms1-dev, libxcb-keysyms1-dev,
@ -24,7 +24,7 @@ Build-Depends: debhelper (>= 7.0.50~),
libcairo2-dev, libcairo2-dev,
libpango1.0-dev, libpango1.0-dev,
libpod-simple-perl libpod-simple-perl
Standards-Version: 3.9.5 Standards-Version: 3.9.7
Homepage: http://i3wm.org/ Homepage: http://i3wm.org/
Package: i3 Package: i3

63
debian/copyright vendored
View File

@ -1,30 +1,41 @@
This Debian package is based on a tarball downloaded from Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
http://i3wm.org/ Upstream-Name: i3
Upstream-Contact: Michael Stapelberg <michael@i3wm.org>
Source: https://i3wm.org/
Copyright: (C) 2009-2011 Michael Stapelberg <michael+i3 at stapelberg dot de> Files: *
All rights reserved. Copyright: 2009 Michael Stapelberg
License: BSD-3-clause
Redistribution and use in source and binary forms, with or without modification, Files: debian/*
are permitted provided that the following conditions are met: Copyright: 2009 Michael Stapelberg
License: BSD-3-clause
* Redistributions of source code must retain the above copyright notice, this License: BSD-3-clause
Copyright: © 2009 Michael Stapelberg <michael at i3wm dot org>
All rights reserved.
.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. list of conditions and the following disclaimer.
.
* Redistributions in binary form must reproduce the above copyright notice, this * Redistributions in binary form must reproduce the above copyright notice,
list of conditions and the following disclaimer in the documentation and/or other this list of conditions and the following disclaimer in the documentation
materials provided with the distribution. and/or other materials provided with the distribution.
.
* Neither the name of Michael Stapelberg, i3 nor the names of its contributors * Neither the name of Michael Stapelberg, i3 nor the names of its contributors
may be used to endorse or promote products derived from this software without may be used to endorse or promote products derived from this software
specific prior written permission. without specific prior written permission.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

2
debian/i3-wm.install vendored Normal file
View File

@ -0,0 +1,2 @@
etc
usr

42
debian/rules vendored
View File

@ -1,47 +1,17 @@
#!/usr/bin/make -f #!/usr/bin/make -f
# vi: ts=8 sw=8 noet # vi: ts=8 sw=8 noet
DPKG_EXPORT_BUILDFLAGS = 1 export V:=1
-include /usr/share/dpkg/buildflags.mk export DEB_BUILD_MAINT_OPTIONS = hardening=+all
ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
MAKEFLAGS += -j$(NUMJOBS)
endif
build: build-arch build-indep
build-arch: build-stamp
build-indep: build-stamp
build-stamp:
dh build
touch build-stamp
clean:
dh clean
install: build install-stamp
install-stamp:
dh install
touch install-stamp
binary-arch: install
dh binary-arch
binary-indep: install
dh binary-indep
binary: binary-arch binary-indep
override_dh_auto_build: override_dh_auto_build:
$(MAKE) dh_auto_build -- all docs mans
$(MAKE) -C man
$(MAKE) -C docs
override_dh_installchangelogs: override_dh_installchangelogs:
dh_installchangelogs RELEASE-NOTES-* dh_installchangelogs RELEASE-NOTES-*
override_dh_install:
$(MAKE) DESTDIR=$(CURDIR)/debian/i3-wm/ install
override_dh_strip: override_dh_strip:
dh_strip --dbg-package=i3-wm-dbg dh_strip --dbg-package=i3-wm-dbg
%:
dh $@ --parallel

56
debian/upstream/signing-key.asc vendored Normal file
View File

@ -0,0 +1,56 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
mQINBEoVG4cBEADX2160pBoUf2vSWKbUa8soEMscBFjmb/NajCxwX/BlD1sVNyDm
twZ74CNPS7X5GgNQoXCzkm7v18zOpON69/pwQ0C4T4P+dvewaDzi2+4/bZsXSor1
mA3C9lHcKDbpH7jHkN2AbMnY3Z4LD46LA1qfCISAAKtx1h4peBF6Xhu743dKXrBa
zg/TEJwWIWSyPKgIhur95yebD/Tws+gWlOfBKkF1v1PA+5sPmC8LyK5Rd1n9Sg1D
j//4sWl8A4EwM4QUzSliZME775klV4mOBGbsTnhNjCymgDiXVNjoWdEIHoNfDsut
E2czgSwsSrSPls/Kl1KuHyBiOWi4dl6MFaypcuSNEVNi5K+oJ7gmX/sy/TlF5Ofw
KoBEPrcvulVT8aAM3azMfb/Fgo+GcEEYljV1yvSg7jSjCHxXgMyh/yMfZcPkwajp
fNE5D7WAXgygpolM9dLIOBemDJxwWr0G7uhXNv3vSHpuUheb2REaJJwWHw1IuCmn
gigD5mebQWRSmbEl66ygOFkps9FEq6KSmbHkj7dIrSVmK5DtQRRI5fMPI+E+atul
Lnpgm/R2p2yvPKoS/pr9mwvKIf9F5C20wm1iAaGW1pTDSIl2y2ZpzcJIyS+jhyCX
3d6D7FNEFlI2p9Tnbt9aE04ASLlZFGjxNWweU8zAkNOr1MyPTiWrYtsCtwARAQAB
tCpNaWNoYWVsIFN0YXBlbGJlcmcgPG1pY2hhZWxAc3RhcGVsYmVyZy5kZT6JAkAE
EwEKACoCGy8CHgECF4AFCwkIBwMFFQoJCAsFFgIDAQACGQEFAlNdKVoFCRKuD04A
CgkQTnFg7UrI7h1HYw//R7WBr/MrqevbaB6Uh7Koy3rN1GqXXY7L4kQAO1XSrmC9
IQ/giwg7+655tDWq4cAjefiBWRv0I1WWqZwdgUGwfhzW20DBx2sPkGKZ29pcvU/k
LuMyWs49o2lcsb4cQqgDpH/uzi22fc4BhO91o/uZYOAXrrSlLuzkCa1SDCRymwdw
lIXIXktROd+r6Fpc1FAinOQgn5BQjf7gbSZSlqBLeYZdR+qSxZWufrhsVUy03nVx
mF9hc/aTFNYZHHHh0yFzYfBKisqsuwJW94uW80xw17HoBMSb10eNGEq5xWqh4Owu
8heJePlcoh2F5JnO6cFWoz1bHCZGjeeIm0OdPJXTLDQdcA5Hy4K3ADidqW0y+Iza
Wbs3TpLprLw91LaPcwzZf+vzRgsQCwPKODjhcetEaYGIKweCkNQRQCW6wEl7kRAw
/eG1wdn4YfEcnCz4ye1MW67au3omvBy41BNmGb20rEc9JIQ37HhAJy5MwuguuO1Z
xZWyu3fV39YLwvsa8EYFPb/DOUTmSCBCyvfTOCEC94Vl2kcPXicIpaRnCFZNqVEJ
FAMKY/tSVjPsBEXTFx3aiX3am4CCtc8R95z2DrYtW5UU/yA5o6lDnfRX6Smdl620
kTM/gFEgAI8+x56XsWJ/CnG//EbgKMy8u2u5y7x1SdpZFxLf722EryF0yPJt+im0
OU1pY2hhZWwgU3RhcGVsYmVyZyAoUkVOVC1BLUdVUlUpIDxtaWNoYWVsQHJlbnQt
YS1ndXJ1LmRlPokCPQQTAQoAJwIbLwIeAQIXgAULCQgHAwUVCgkICwUWAgMBAAUC
U10pWgUJEq4PTgAKCRBOcWDtSsjuHUTREACcBXnZwinxZ8S0DOl2OR7qm8ao4U/n
h71tkJX9klnXYY8KQD54tuGjYjCA+UvTOX7c0Rzj3WijgyRxefmOhPQzkk/zUheH
bsaYSbj2mCvA/IkMRe6G0+wyFU5ydssLVApx/+bwdL3CiIoFPwyHMgWPjYuijIts
UMbq7jtnF26l7O0GSY5uHSUQb7caz+Mu0CcF95h3oxRxHVAhHIMtwzkilbjbshEf
nxDH5L0s2hT0amkEB2jw3US2v+YrThk0ZQPoB+tgNLL2Li7yAuwbaEaK37aDTtkX
NdFiAwcOHrLhlD0SnNya4nEVwgnCu5B9f+OwalPq3a0+G394L+a+XHWWwXc6MlFz
WhzAMeE1uFJfbIIGEL/Q3URBbhIUf0xsZEagsjNExgYtJY5XJRitgyxPwAuxusia
VhfTmbr3Mr5yu7QEt1oACq3j0bNr7hzcPk+ckHYbsSvuoo21Ef5vhQetXpAzrot/
+62c7i1xcAvY7MVBT2f/7BC4cYvLXALhcvLAabzOcD8lBPEIBkxgJj42EckEzFzC
c9s5htWdhWYIAIIdIOxZejxfRiTvujg5CaJo+Fg/BL4TTlBgjU7ASzMQBfos3cII
F8O9Yc+W04uMyG9QPqk//rIUFdKgUyd4tzXkQrJs7Jom7duxJPpr5dDMLqTwINL5
LBBKw/lpA/nAkLQqTWljaGFlbCBTdGFwZWxiZXJnIDxzdGFwZWxiZXJnQGRlYmlh
bi5vcmc+iQI9BBMBCgAnAhsvBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheABQJTXSla
BQkSrg9OAAoJEE5xYO1KyO4dGBkP/353sh8feSyxJNXMdgAfe+okYE4B9iWS2zRy
GAEGKyliaMWDJLRhT4ln1glr05pFoy52Hxe4+NBpSYuZ9NV390HRmPxbFaUvHs74
HPkuABxy2XKH94IS1nrb+fleR00w0rLEin4aW3mtwKDHCJtNUW0/DNtC7Uz028SL
9TftaqJrsf+paTFJkZF0ShJ2A6XFxwlvBiVz4f2UDEi7GQuAaYI5V5ZosWIAxdYG
631tKf1EU9YtjPYdBk3YF4Q44AhVbe478Ji24Of0G/l+dGFRzbdARNayBwegJoMu
djXQrPfR1jNVSaw65/AinbbtadlULSTrvhoD6ommGeU6fQS3WtNBbF4T5SuNRBeK
QhWuLSOllmjWfERaj6omg8GXd1rctIFnUhT/dl6bmhooMPRUW1DNxVzqd7UFnqXE
+CaJ7cGfI/9/MITTG32QpjkVeowOcuoZ+qLoOu7dBbagDaEn8T6yFCLACyf9LGBm
MC9bNSGsMRCFV33N/PbVcgf6M0z46d0ysPgGiR5MX1O6CQkJGrwolfA5sK3VIsyp
PaCCHpAuPdEiWB57WleURqCIGWV2ccElyA4M4auDGt1SSNl0NfWu8vde6wNIPir/
DBqh1265QLrLS239UT0u5hYE5hkfiKP2dzgWKh9NT9xm2Dyjjv/PaqWlVedvsVIp
VRx/oRxr
=1n/p
-----END PGP PUBLIC KEY BLOCK-----

3
debian/watch vendored
View File

@ -1,2 +1,3 @@
version=3 version=3
http://i3wm.org/downloads/ /downloads/i3-(.*)\.tar\.bz2 opts=pgpsigurlmangle=s/$/.asc/ \
https://i3wm.org/downloads/ /downloads/i3-(.*)\.tar\.bz2

View File

@ -117,7 +117,7 @@ containers, searching containers, getting specific properties from containers,
src/config.c:: src/config.c::
Contains all functions handling the configuration file (calling the parser Contains all functions handling the configuration file (calling the parser
(src/cfgparse.y) with the correct path, switching key bindings mode). src/config_parser.c) with the correct path, switching key bindings mode).
src/debug.c:: src/debug.c::
Contains debugging functions to print unhandled X events. Contains debugging functions to print unhandled X events.

View File

@ -136,6 +136,10 @@ color::
when it is associated. when it is associated.
Colors are specified in hex (like in HTML), starting with a leading Colors are specified in hex (like in HTML), starting with a leading
hash sign. For example, +#ff0000+ means red. hash sign. For example, +#ff0000+ means red.
background::
Overrides the background color for this particular block.
border::
Overrides the border color for this particular block.
min_width:: min_width::
The minimum width (in pixels) of the block. If the content of the The minimum width (in pixels) of the block. If the content of the
+full_text+ key take less space than the specified min_width, the block +full_text+ key take less space than the specified min_width, the block
@ -207,6 +211,8 @@ An example of a block which uses all possible entries follows:
"full_text": "E: 10.0.0.1 (1000 Mbit/s)", "full_text": "E: 10.0.0.1 (1000 Mbit/s)",
"short_text": "10.0.0.1", "short_text": "10.0.0.1",
"color": "#00ff00", "color": "#00ff00",
"background": "#1c1c1c",
"border": "#ee0000",
"min_width": 300, "min_width": 300,
"align": "right", "align": "right",
"urgent": false, "urgent": false,

View File

@ -520,6 +520,14 @@ statusline::
Text color to be used for the statusline. Text color to be used for the statusline.
separator:: separator::
Text color to be used for the separator. Text color to be used for the separator.
focused_background::
Background color of the bar on the currently focused monitor output.
focused_statusline::
Text color to be used for the statusline on the currently focused
monitor output.
focused_separator::
Text color to be used for the separator on the currently focused
monitor output.
focused_workspace_text/focused_workspace_bg/focused_workspace_border:: focused_workspace_text/focused_workspace_bg/focused_workspace_border::
Text/background/border color for a workspace button when the workspace Text/background/border color for a workspace button when the workspace
has focus. has focus.
@ -717,11 +725,15 @@ This event consists of a single serialized map containing a property
This event consists of a single serialized map containing a property This event consists of a single serialized map containing a property
+change (string)+ which holds the name of current mode in use. The name +change (string)+ which holds the name of current mode in use. The name
is the same as specified in config when creating a mode. The default is the same as specified in config when creating a mode. The default
mode is simply named default. mode is simply named default. It contains a second property, +pango_markup+, which
defines whether pango markup shall be used for displaying this mode.
*Example:* *Example:*
--------------------------- ---------------------------
{ "change": "default" } {
"change": "default",
"pango_markup": true
}
--------------------------- ---------------------------
=== window event === window event
@ -814,6 +826,8 @@ know):
C:: C::
* i3 includes a headerfile +i3/ipc.h+ which provides you all constants. * i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
* https://github.com/acrisci/i3ipc-glib * https://github.com/acrisci/i3ipc-glib
C++::
* https://github.com/drmgc/i3ipcpp
Go:: Go::
* https://github.com/proxypoke/i3ipc * https://github.com/proxypoke/i3ipc
JavaScript:: JavaScript::

View File

@ -4,8 +4,9 @@ Michael Stapelberg <michael@i3wm.org>
March 2013 March 2013
This document contains all the information you need to configure and use the i3 This document contains all the information you need to configure and use the i3
window manager. If it does not, please check http://faq.i3wm.org/ first, then window manager. If it does not, please check https://www.reddit.com/r/i3wm/
contact us on IRC (preferred) or post your question(s) on the mailing list. first, then contact us on IRC (preferred) or post your question(s) on the
mailing list.
== Default keybindings == Default keybindings
@ -604,9 +605,10 @@ new_window pixel 3
--------------------- ---------------------
=== Hiding vertical borders [[_hiding_vertical_borders]]
=== Hiding borders adjacent to the screen edges
You can hide vertical borders adjacent to the screen edges using You can hide container borders adjacent to the screen edges using
+hide_edge_borders+. This is useful if you are using scrollbars, or do not want +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. to waste even two pixels in displayspace. Default is none.
@ -652,7 +654,7 @@ The valid criteria are the same as those for commands, see <<command_criteria>>.
=== Don't focus window upon opening === Don't focus window upon opening
When a new window appears, it will be focused. The +no_focus+ directive allows preventing When a new window appears, it will be focused. The +no_focus+ directive allows preventing
this from happening and can be used in combination with <<command_criteria>>. this from happening and must be used in combination with <<command_criteria>>.
Note that this does not apply to all cases, e.g., when feeding data into a running application Note that this does not apply to all cases, e.g., when feeding data into a running application
causing it to request being focused. To configure the behavior in such cases, refer to causing it to request being focused. To configure the behavior in such cases, refer to
@ -784,7 +786,7 @@ keyword. These commands will be run in order.
See <<command_chaining>> for details on the special meaning of +;+ (semicolon) See <<command_chaining>> for details on the special meaning of +;+ (semicolon)
and +,+ (comma): they chain commands together in i3, so you need to use quoted and +,+ (comma): they chain commands together in i3, so you need to use quoted
strings if they appear in your command. strings (as shown in <<exec_quoting>>) if they appear in your command.
*Syntax*: *Syntax*:
--------------------------------------- ---------------------------------------
@ -835,9 +837,9 @@ workspace "2: vim" output VGA1
You can change all colors which i3 uses to draw the window decorations. You can change all colors which i3 uses to draw the window decorations.
*Syntax*: *Syntax*:
------------------------------------------------------ --------------------------------------------------------------------
<colorclass> <border> <background> <text> <indicator> <colorclass> <border> <background> <text> <indicator> <child_border>
------------------------------------------------------ --------------------------------------------------------------------
Where colorclass can be one of: Where colorclass can be one of:
@ -862,20 +864,20 @@ client.background::
Colors are in HTML hex format (#rrggbb), see the following example: Colors are in HTML hex format (#rrggbb), see the following example:
*Examples (default colors)*: *Examples (default colors)*:
--------------------------------------------------------- ----------------------------------------------------------------------
# class border backgr. text indicator # class border backgr. text indicator child_border
client.focused #4c7899 #285577 #ffffff #2e9ef4 client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
client.focused_inactive #333333 #5f676a #ffffff #484e50 client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
client.unfocused #333333 #222222 #888888 #292d2e client.unfocused #333333 #222222 #888888 #292d2e #222222
client.urgent #2f343a #900000 #ffffff #900000 client.urgent #2f343a #900000 #ffffff #900000 #900000
client.placeholder #000000 #0c0c0c #ffffff #000000 client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
client.background #ffffff client.background #ffffff
--------------------------------------------------------- ----------------------------------------------------------------------
Note that for the window decorations, the color around the child window is the Note that for the window decorations, the color around the child window is the
background color, and the border color is only the two thin lines at the top of "child_border", and "border" color is only the two thin lines around the
the window. titlebar.
The indicator color is used for indicating where a new window will be opened. The indicator color is used for indicating where a new window will be opened.
For horizontal split containers, the right border will be painted in indicator For horizontal split containers, the right border will be painted in indicator
@ -1236,7 +1238,7 @@ the windows key). The default value for the hidden_state is hide.
------------------------- -------------------------
mode dock|hide|invisible mode dock|hide|invisible
hidden_state hide|show hidden_state hide|show
modifier <Modifier> modifier <Modifier>|none
------------------------ ------------------------
*Example*: *Example*:
@ -1248,7 +1250,8 @@ bar {
} }
---------------- ----------------
Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+). Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+). You can
also use "none" if you don't want any modifier to trigger this behavior.
=== Mouse button commands === Mouse button commands
@ -1369,6 +1372,11 @@ NetworkManager, VLC, Pidgin, etc. can place little icons.
You can configure on which output (monitor) the icons should be displayed or You can configure on which output (monitor) the icons should be displayed or
you can turn off the functionality entirely. you can turn off the functionality entirely.
You can use mutliple +tray_output+ directives in your config to specify a list
of outputs on which you want the tray to appear. The first available output in
that list as defined by the order of the directives will be used for the tray
output.
*Syntax*: *Syntax*:
--------------------------------- ---------------------------------
tray_output none|primary|<output> tray_output none|primary|<output>
@ -1529,6 +1537,15 @@ statusline::
Text color to be used for the statusline. Text color to be used for the statusline.
separator:: separator::
Text color to be used for the separator. Text color to be used for the separator.
focused_background::
Background color of the bar on the currently focused monitor output. If
not used, the color will be taken from +background+.
focused_statusline::
Text color to be used for the statusline on the currently focused
monitor output. If not used, the color will be taken from +statusline+.
focused_separator::
Text color to be used for the separator on the currently focused
monitor output. If not used, the color will be taken from +separator+.
focused_workspace:: focused_workspace::
Border, background and text color for a workspace button when the workspace Border, background and text color for a workspace button when the workspace
has focus. has focus.
@ -1643,7 +1660,7 @@ window_role::
window_type:: window_type::
Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are
+normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+, +normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+,
+popup_menu+ and +tooltip+. +popup_menu+, +tooltip+ and +notification+.
id:: id::
Compares the X11 window ID, which you can get via +xwininfo+ for example. Compares the X11 window ID, which you can get via +xwininfo+ for example.
title:: title::
@ -1659,10 +1676,13 @@ workspace::
the special value +\_\_focused__+ to match all windows in the currently the special value +\_\_focused__+ to match all windows in the currently
focused workspace. focused workspace.
con_mark:: con_mark::
Compares the mark set for this container, see <<vim_like_marks>>. Compares the marks set for this container, see <<vim_like_marks>>. A
match is made if any of the container's marks matches the specified
mark.
con_id:: con_id::
Compares the i3-internal container ID, which you can get via the IPC Compares the i3-internal container ID, which you can get via the IPC
interface. Handy for scripting. interface. Handy for scripting. Use the special value +\_\_focused__+
to match only the currently focused window.
The criteria +class+, +instance+, +role+, +title+, +workspace+ and +mark+ are The criteria +class+, +instance+, +role+, +title+, +workspace+ and +mark+ are
actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for
@ -1678,7 +1698,7 @@ searched in your +$PATH+.
See <<command_chaining>> for details on the special meaning of +;+ (semicolon) See <<command_chaining>> for details on the special meaning of +;+ (semicolon)
and +,+ (comma): they chain commands together in i3, so you need to use quoted and +,+ (comma): they chain commands together in i3, so you need to use quoted
strings if they appear in your command. strings (as shown in <<exec_quoting>>) if they appear in your command.
*Syntax*: *Syntax*:
-------------------------------- --------------------------------
@ -1702,6 +1722,27 @@ launching. So, if an application is not startup-notification aware (most GTK
and Qt using applications seem to be, though), you will end up with a watch and Qt using applications seem to be, though), you will end up with a watch
cursor for 60 seconds. cursor for 60 seconds.
[[exec_quoting]]
If the command to be executed contains a +;+ (semicolon) and/or a +,+ (comma),
the entire command must be quoted. For example, to have a keybinding for the
shell command +notify-send Hello, i3+, you would add an entry to your
configuration file like this:
*Example*:
------------------------------
# Execute a command with a comma in it
bindsym $mod+p exec "notify-send Hello, i3"
------------------------------
If however a command with a comma and/or semicolon itself requires quotes, you
must escape the internal quotation marks with double backslashes, like this:
*Example*:
------------------------------
# Execute a command with a comma, semicolon and internal quotes
bindsym $mod+p exec "notify-send \\"Hello, i3; from $USER\\""
------------------------------
=== Splitting containers === Splitting containers
The split command makes the current window a split container. Split containers The split command makes the current window a split container. Split containers
@ -1711,20 +1752,24 @@ get placed below the current one (splitv).
If you apply this command to a split container with the same orientation, If you apply this command to a split container with the same orientation,
nothing will happen. If you use a different orientation, the split containers nothing will happen. If you use a different orientation, the split containers
orientation will be changed (if it does not have more than one window). Use orientation will be changed (if it does not have more than one window).
+layout toggle split+ to change the layout of any split container from splitv The +toggle+ option will toggle the orientation of the split container if it
to splith or vice-versa. contains a single window. Otherwise it makes the current window a split
container with opposite orientation compared to the parent container.
Use +layout toggle split+ to change the layout of any split container from
splitv to splith or vice-versa.
*Syntax*: *Syntax*:
------------------------- --------------------------------
split vertical|horizontal split vertical|horizontal|toggle
------------------------- --------------------------------
*Example*: *Example*:
------------------------------ -------------------------------
bindsym $mod+v split vertical bindsym $mod+v split vertical
bindsym $mod+h split horizontal bindsym $mod+h split horizontal
------------------------------ bindsym $mod+t split toggle
-------------------------------
=== Manipulating layout === Manipulating layout
@ -1881,8 +1926,11 @@ for_window [instance=notepad] sticky enable
=== Changing (named) workspaces/moving to workspaces === Changing (named) workspaces/moving to workspaces
To change to a specific workspace, use the +workspace+ command, followed by the To change to a specific workspace, use the +workspace+ command, followed by the
number or name of the workspace. To move containers to specific workspaces, use number or name of the workspace. Pass the optional flag
+move container to workspace+. +--no-auto-back-and-forth+ to disable <<back_and_forth>> for this specific call
only.
To move containers to specific workspaces, use +move container to workspace+.
You can also switch to the next and previous workspace with the commands You can also switch to the next and previous workspace with the commands
+workspace next+ and +workspace prev+, which is handy, for example, if you have +workspace next+ and +workspace prev+, which is handy, for example, if you have
@ -1893,6 +1941,10 @@ container to workspace next+, +move container to workspace prev+ to move a
container to the next/previous workspace and +move container to workspace current+ container to the next/previous workspace and +move container to workspace current+
(the last one makes sense only when used with criteria). (the last one makes sense only when used with criteria).
+workspace next+ cycles through either numbered or named workspaces. But when it
reaches the last numbered/named workspace, it looks for named workspaces after
exhausting numbered ones and looks for numbered ones after exhausting named ones.
See <<move_to_outputs>> for how to move a container/workspace to a different See <<move_to_outputs>> for how to move a container/workspace to a different
RandR output. RandR output.
@ -1906,16 +1958,16 @@ back_and_forth+; likewise, you can move containers to the previously focused
workspace using +move container to workspace back_and_forth+. workspace using +move container to workspace back_and_forth+.
*Syntax*: *Syntax*:
----------------------------------- --------------------------------------------------------------------------------
workspace next|prev|next_on_output|prev_on_output workspace next|prev|next_on_output|prev_on_output
workspace back_and_forth workspace back_and_forth
workspace <name> workspace [--no-auto-back-and-forth] <name>
workspace number <name> workspace [--no-auto-back-and-forth] number <name>
move [window|container] [to] workspace <name> move [--no-auto-back-and-forth] [window|container] [to] workspace <name>
move [window|container] [to] workspace number <name> move [--no-auto-back-and-forth] [window|container] [to] workspace number <name>
move [window|container] [to] workspace prev|next|current move [window|container] [to] workspace prev|next|current
----------------------------------- --------------------------------------------------------------------------------
*Examples*: *Examples*:
------------------------- -------------------------
@ -2109,24 +2161,36 @@ for this purpose: It lets you input a command and sends the command to i3. It
can also prefix this command and display a custom prompt for the input dialog. can also prefix this command and display a custom prompt for the input dialog.
The additional +--toggle+ option will remove the mark if the window already has The additional +--toggle+ option will remove the mark if the window already has
this mark, add it if the window has none or replace the current mark if it has this mark or add it otherwise. Note that you may need to use this in
another mark. combination with +--add+ (see below) as any other marks will otherwise be
removed.
By default, a window can only have one mark. You can use the +--add+ flag to
put more than one mark on a window.
Refer to <<show_marks>> if you don't want marks to be shown in the window decoration. Refer to <<show_marks>> if you don't want marks to be shown in the window decoration.
*Syntax*: *Syntax*:
------------------------------ ----------------------------------------------
mark [--toggle] <identifier> mark [--add|--replace] [--toggle] <identifier>
[con_mark="identifier"] focus [con_mark="identifier"] focus
unmark <identifier> unmark <identifier>
------------------------------ ----------------------------------------------
*Example (in a terminal)*: *Example (in a terminal)*:
------------------------------ ---------------------------------------------------------
$ i3-msg mark irssi # marks the focused container
$ i3-msg '[con_mark="irssi"] focus' mark irssi
$ i3-msg unmark irssi
------------------------------ # focus the container with the mark "irssi"
'[con_mark="irssi"] focus'
# remove the mark "irssi" from whichever container has it
unmark irssi
# remove all marks on all firefox windows
[class="(?i)firefox"] unmark
---------------------------------------------------------
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
TODO: make i3-input replace %s TODO: make i3-input replace %s
@ -2153,7 +2217,10 @@ https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup]
and the following placeholders which will be replaced: and the following placeholders which will be replaced:
+%title+:: +%title+::
The X11 window title (_NET_WM_NAME or WM_NAME as fallback). For normal windows, this is the X11 window title (_NET_WM_NAME or WM_NAME
as fallback). When used on containers without a window (e.g., a split
container inside a tabbed/stacked layout), this will be the tree
representation of the container (e.g., "H[xterm xterm]").
+%class+:: +%class+::
The X11 window class (second part of WM_CLASS). This corresponds to the The X11 window class (second part of WM_CLASS). This corresponds to the
+class+ criterion, see <<command_criteria>>. +class+ criterion, see <<command_criteria>>.
@ -2189,6 +2256,10 @@ and +border none+ to make the client borderless.
There is also +border toggle+ which will toggle the different border styles. There is also +border toggle+ which will toggle the different border styles.
Note that "pixel" refers to logical pixel. On HiDPI displays, a logical pixel
may be represented by multiple physical pixels, so +pixel 1+ might not
necessarily translate into a single pixel row wide border.
*Syntax*: *Syntax*:
----------------------------------------------- -----------------------------------------------
border normal|pixel [<n>] border normal|pixel [<n>]

View File

@ -109,7 +109,7 @@ for my $line (@lines) {
# Second step: Generate the enum values for all states. # Second step: Generate the enum values for all states.
# It is important to keep the order the same, so we store the keys once. # It is important to keep the order the same, so we store the keys once.
# We sort descendingly by length to be able to replace occurences of the state # We sort descendingly by length to be able to replace occurrences of the state
# name even when one states name is included in another ones (like FOR_WINDOW # name even when one states name is included in another ones (like FOR_WINDOW
# is in FOR_WINDOW_COMMAND). # is in FOR_WINDOW_COMMAND).
my @keys = sort { (length($b) <=> length($a)) or ($a cmp $b) } keys %states; my @keys = sort { (length($b) <=> length($a)) or ($a cmp $b) } keys %states;

View File

@ -479,7 +479,7 @@ static int handle_expose() {
if (current_step == STEP_WELCOME) { if (current_step == STEP_WELCOME) {
/* restore font color */ /* restore font color */
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000"));
txt(logical_px(10), 2, "You have not configured i3 yet."); txt(logical_px(10), 2, "You have not configured i3 yet.");
txt(logical_px(10), 3, "Do you want me to generate a config at"); txt(logical_px(10), 3, "Do you want me to generate a config at");
@ -493,16 +493,16 @@ static int handle_expose() {
txt(logical_px(85), 8, "No, I will use the defaults"); txt(logical_px(85), 8, "No, I will use the defaults");
/* green */ /* green */
set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000")); set_font_colors(pixmap_gc, draw_util_hex_to_color("#00FF00"), draw_util_hex_to_color("#000000"));
txt(logical_px(25), 6, "<Enter>"); txt(logical_px(25), 6, "<Enter>");
/* red */ /* red */
set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000")); set_font_colors(pixmap_gc, draw_util_hex_to_color("#FF0000"), draw_util_hex_to_color("#000000"));
txt(logical_px(31), 8, "<ESC>"); txt(logical_px(31), 8, "<ESC>");
} }
if (current_step == STEP_GENERATE) { if (current_step == STEP_GENERATE) {
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000"));
txt(logical_px(10), 2, "Please choose either:"); txt(logical_px(10), 2, "Please choose either:");
txt(logical_px(85), 4, "Win as default modifier"); txt(logical_px(85), 4, "Win as default modifier");
@ -519,7 +519,7 @@ static int handle_expose() {
/* the selected modifier */ /* the selected modifier */
set_font(&bold_font); set_font(&bold_font);
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000"));
if (modifier == MOD_Mod4) if (modifier == MOD_Mod4)
txt(logical_px(10), 4, "-> <Win>"); txt(logical_px(10), 4, "-> <Win>");
else else
@ -527,11 +527,11 @@ static int handle_expose() {
/* green */ /* green */
set_font(&font); set_font(&font);
set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000")); set_font_colors(pixmap_gc, draw_util_hex_to_color("#00FF00"), draw_util_hex_to_color("#000000"));
txt(logical_px(25), 9, "<Enter>"); txt(logical_px(25), 9, "<Enter>");
/* red */ /* red */
set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000")); set_font_colors(pixmap_gc, draw_util_hex_to_color("#FF0000"), draw_util_hex_to_color("#000000"));
txt(logical_px(31), 10, "<ESC>"); txt(logical_px(31), 10, "<ESC>");
} }

View File

@ -138,6 +138,7 @@ int main(int argc, char *argv[]) {
if (verbose) if (verbose)
printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n", printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n",
header->offset_next_write, header->offset_last_wrap, header->size, shmname); header->offset_next_write, header->offset_last_wrap, header->size, shmname);
free(shmname);
walk = logbuffer + header->offset_next_write; walk = logbuffer + header->offset_next_write;
/* We first need to print old content in case there was at least one /* We first need to print old content in case there was at least one

View File

@ -137,16 +137,16 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner);
/* restore font color */ /* restore font color */
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000")); set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000"));
/* draw the prompt … */ /* draw the prompt … */
if (prompt != NULL) { if (prompt != NULL) {
draw_text(prompt, pixmap, pixmap_gc, logical_px(4), logical_px(4), logical_px(492)); draw_text(prompt, pixmap, pixmap_gc, NULL, logical_px(4), logical_px(4), logical_px(492));
} }
/* … and the text */ /* … and the text */
if (input_position > 0) { if (input_position > 0) {
i3String *input = i3string_from_ucs2(glyphs_ucs, input_position); i3String *input = i3string_from_ucs2(glyphs_ucs, input_position);
draw_text(input, pixmap, pixmap_gc, prompt_offset + logical_px(4), logical_px(4), logical_px(492)); draw_text(input, pixmap, pixmap_gc, NULL, prompt_offset + logical_px(4), logical_px(4), logical_px(492));
i3string_free(input); i3string_free(input);
} }
@ -176,14 +176,14 @@ static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_rel
static void finish_input() { static void finish_input() {
char *command = (char *)concat_strings(glyphs_utf8, input_position); char *command = (char *)concat_strings(glyphs_utf8, input_position);
/* count the occurences of %s in the string */ /* count the occurrences of %s in the string */
int c; int c;
int len = strlen(format); int len = strlen(format);
int cnt = 0; int cnt = 0;
for (c = 0; c < (len - 1); c++) for (c = 0; c < (len - 1); c++)
if (format[c] == '%' && format[c + 1] == 's') if (format[c] == '%' && format[c + 1] == 's')
cnt++; cnt++;
printf("occurences = %d\n", cnt); printf("occurrences = %d\n", cnt);
/* allocate space for the output */ /* allocate space for the output */
int inputlen = strlen(command); int inputlen = strlen(command);

View File

@ -119,6 +119,10 @@ static yajl_callbacks reply_callbacks = {
}; };
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
#if defined(__OpenBSD__)
if (pledge("stdio rpath unix", NULL) == -1)
err(EXIT_FAILURE, "pledge");
#endif
char *env_socket_path = getenv("I3SOCK"); char *env_socket_path = getenv("I3SOCK");
if (env_socket_path) if (env_socket_path)
socket_path = sstrdup(env_socket_path); socket_path = sstrdup(env_socket_path);

View File

@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-nagbar
i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c) i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c)
i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h) i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h)
i3_nagbar_CFLAGS = $(XCB_CFLAGS) $(XCB_WM_CFLAGS) $(PANGO_CFLAGS) i3_nagbar_CFLAGS = $(XCB_CFLAGS) $(XCB_CURSOR_CFLAGS) $(XCB_WM_CFLAGS) $(PANGO_CFLAGS)
i3_nagbar_LIBS = $(XCB_LIBS) $(XCB_WM_LIBS) $(PANGO_LIBS) i3_nagbar_LIBS = $(XCB_LIBS) $(XCB_CURSOR_LIBS) $(XCB_WM_LIBS) $(PANGO_LIBS)
i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o) i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o)

View File

@ -28,10 +28,15 @@
#include <xcb/xcb_aux.h> #include <xcb/xcb_aux.h>
#include <xcb/xcb_event.h> #include <xcb/xcb_event.h>
#include <xcb/randr.h> #include <xcb/randr.h>
#include <xcb/xcb_cursor.h>
#include "libi3.h" #include "libi3.h"
#include "i3-nagbar.h" #include "i3-nagbar.h"
/** This is the equivalent of XC_left_ptr. Im not sure why xcb doesnt have a
* constant for that. */
#define XCB_CURSOR_LEFT_PTR 68
static char *argv0 = NULL; static char *argv0 = NULL;
typedef struct { typedef struct {
@ -51,11 +56,11 @@ static button_t *buttons;
static int buttoncnt; static int buttoncnt;
/* Result of get_colorpixel() for the various colors. */ /* Result of get_colorpixel() for the various colors. */
static uint32_t color_background; /* background of the bar */ static color_t color_background; /* background of the bar */
static uint32_t color_button_background; /* background for buttons */ static color_t color_button_background; /* background for buttons */
static uint32_t color_border; /* color of the button border */ static color_t color_border; /* color of the button border */
static uint32_t color_border_bottom; /* color of the bottom border */ static color_t color_border_bottom; /* color of the bottom border */
static uint32_t color_text; /* color of the text */ static color_t color_text; /* color of the text */
xcb_window_t root; xcb_window_t root;
xcb_connection_t *conn; xcb_connection_t *conn;
@ -191,12 +196,12 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
*/ */
static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
/* re-draw the background */ /* re-draw the background */
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_background}); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_background.colorpixel});
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &rect); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &rect);
/* restore font color */ /* restore font color */
set_font_colors(pixmap_gc, color_text, color_background); set_font_colors(pixmap_gc, color_text, color_background);
draw_text(prompt, pixmap, pixmap_gc, draw_text(prompt, pixmap, pixmap_gc, NULL,
logical_px(4) + logical_px(4), logical_px(4) + logical_px(4),
logical_px(4) + logical_px(4), logical_px(4) + logical_px(4),
rect.width - logical_px(4) - logical_px(4)); rect.width - logical_px(4) - logical_px(4));
@ -210,14 +215,14 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
w += logical_px(8); w += logical_px(8);
int y = rect.width; int y = rect.width;
uint32_t values[3]; uint32_t values[3];
values[0] = color_button_background; values[0] = color_button_background.colorpixel;
values[1] = line_width; values[1] = line_width;
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
xcb_rectangle_t close = {y - w - (2 * line_width), 0, w + (2 * line_width), rect.height}; xcb_rectangle_t close = {y - w - (2 * line_width), 0, w + (2 * line_width), rect.height};
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border}); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border.colorpixel});
xcb_point_t points[] = { xcb_point_t points[] = {
{y - w - (2 * line_width), line_width / 2}, {y - w - (2 * line_width), line_width / 2},
{y - (line_width / 2), line_width / 2}, {y - (line_width / 2), line_width / 2},
@ -245,11 +250,11 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
/* account for left/right padding, which seems to be set to 12px (total) below */ /* account for left/right padding, which seems to be set to 12px (total) below */
w += logical_px(12); w += logical_px(12);
y -= logical_px(30); y -= logical_px(30);
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_button_background}); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_button_background.colorpixel});
close = (xcb_rectangle_t){y - w - (2 * line_width), logical_px(2), w + (2 * line_width), rect.height - logical_px(6)}; close = (xcb_rectangle_t){y - w - (2 * line_width), logical_px(2), w + (2 * line_width), rect.height - logical_px(6)};
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close); xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border}); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border.colorpixel});
buttons[c].x = y - w - (2 * line_width); buttons[c].x = y - w - (2 * line_width);
buttons[c].width = w; buttons[c].width = w;
xcb_point_t points2[] = { xcb_point_t points2[] = {
@ -260,11 +265,11 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
{y - w - (2 * line_width), (line_width / 2) + logical_px(2)}}; {y - w - (2 * line_width), (line_width / 2) + logical_px(2)}};
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2); xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2);
values[0] = color_text; values[0] = color_text.colorpixel;
values[1] = color_button_background; values[1] = color_button_background.colorpixel;
set_font_colors(pixmap_gc, color_text, color_button_background); set_font_colors(pixmap_gc, color_text, color_button_background);
/* the x term seems to set left/right padding */ /* the x term seems to set left/right padding */
draw_text(buttons[c].label, pixmap, pixmap_gc, draw_text(buttons[c].label, pixmap, pixmap_gc, NULL,
y - w - line_width + logical_px(6), y - w - line_width + logical_px(6),
logical_px(4) + logical_px(3), logical_px(4) + logical_px(3),
rect.width - y + w + line_width - logical_px(6)); rect.width - y + w + line_width - logical_px(6));
@ -274,7 +279,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
/* border line at the bottom */ /* border line at the bottom */
line_width = logical_px(2); line_width = logical_px(2);
values[0] = color_border_bottom; values[0] = color_border_bottom.colorpixel;
values[1] = line_width; values[1] = line_width;
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values); xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
xcb_point_t bottom[] = { xcb_point_t bottom[] = {
@ -448,25 +453,49 @@ int main(int argc, char *argv[]) {
if (bar_type == TYPE_ERROR) { if (bar_type == TYPE_ERROR) {
/* Red theme for error messages */ /* Red theme for error messages */
color_button_background = get_colorpixel("#680a0a"); color_button_background = draw_util_hex_to_color("#680a0a");
color_background = get_colorpixel("#900000"); color_background = draw_util_hex_to_color("#900000");
color_text = get_colorpixel("#ffffff"); color_text = draw_util_hex_to_color("#ffffff");
color_border = get_colorpixel("#d92424"); color_border = draw_util_hex_to_color("#d92424");
color_border_bottom = get_colorpixel("#470909"); color_border_bottom = draw_util_hex_to_color("#470909");
} else { } else {
/* Yellowish theme for warnings */ /* Yellowish theme for warnings */
color_button_background = get_colorpixel("#ffc100"); color_button_background = draw_util_hex_to_color("#ffc100");
color_background = get_colorpixel("#ffa8000"); color_background = draw_util_hex_to_color("#ffa8000");
color_text = get_colorpixel("#000000"); color_text = draw_util_hex_to_color("#000000");
color_border = get_colorpixel("#ab7100"); color_border = draw_util_hex_to_color("#ab7100");
color_border_bottom = get_colorpixel("#ab7100"); color_border_bottom = draw_util_hex_to_color("#ab7100");
} }
font = load_font(pattern, true); font = load_font(pattern, true);
set_font(&font); set_font(&font);
#if defined(__OpenBSD__)
if (pledge("stdio rpath wpath cpath getpw proc exec", NULL) == -1)
err(EXIT_FAILURE, "pledge");
#endif
xcb_rectangle_t win_pos = get_window_position(); xcb_rectangle_t win_pos = get_window_position();
xcb_cursor_t cursor;
xcb_cursor_context_t *cursor_ctx;
if (xcb_cursor_context_new(conn, root_screen, &cursor_ctx) == 0) {
cursor = xcb_cursor_load_cursor(cursor_ctx, "left_ptr");
xcb_cursor_context_free(cursor_ctx);
} else {
cursor = xcb_generate_id(conn);
i3Font cursor_font = load_font("cursor", false);
xcb_create_glyph_cursor(
conn,
cursor,
cursor_font.specific.xcb.id,
cursor_font.specific.xcb.id,
XCB_CURSOR_LEFT_PTR,
XCB_CURSOR_LEFT_PTR + 1,
0, 0, 0,
65535, 65535, 65535);
}
/* Open an input window */ /* Open an input window */
win = xcb_generate_id(conn); win = xcb_generate_id(conn);
@ -479,13 +508,14 @@ int main(int argc, char *argv[]) {
0, /* x11 border = 0, we draw our own */ 0, /* x11 border = 0, we draw our own */
XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_WINDOW_CLASS_INPUT_OUTPUT,
XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */ XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_CURSOR,
(uint32_t[]){ (uint32_t[]){
0, /* back pixel: black */ 0, /* back pixel: black */
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE}); XCB_EVENT_MASK_BUTTON_RELEASE,
cursor});
/* Map the window (make it visible) */ /* Map the window (make it visible) */
xcb_map_window(conn, win); xcb_map_window(conn, win);

View File

@ -9,8 +9,8 @@
# mechanism to find the preferred editor # mechanism to find the preferred editor
# Hopefully one of these is installed (no flamewars about preference please!): # Hopefully one of these is installed (no flamewars about preference please!):
for editor in $VISUAL $EDITOR nano vim vi emacs pico qe mg jed gedit mc-edit; do for editor in "$VISUAL" "$EDITOR" nano nvim vim vi emacs pico qe mg jed gedit mc-edit; do
if command -v $editor > /dev/null 2>&1; then if command -v "$editor" > /dev/null 2>&1; then
exec $editor "$@" exec "$editor" "$@"
fi fi
done done

View File

@ -11,8 +11,8 @@
# Hopefully one of these is installed (no flamewars about preference please!): # Hopefully one of these is installed (no flamewars about preference please!):
# We don't use 'more' because it will exit if the file is too short. # We don't use 'more' because it will exit if the file is too short.
# Worst case scenario we'll open the file in your editor. # Worst case scenario we'll open the file in your editor.
for pager in $PAGER less most w3m pg i3-sensible-editor; do for pager in "$PAGER" less most w3m pg i3-sensible-editor; do
if command -v $pager > /dev/null 2>&1; then if command -v "$pager" > /dev/null 2>&1; then
exec $pager "$@" exec "$pager" "$@"
fi fi
done done

View File

@ -8,9 +8,9 @@
# We welcome patches that add distribution-specific mechanisms to find the # We welcome patches that add distribution-specific mechanisms to find the
# preferred terminal emulator. On Debian, there is the x-terminal-emulator # preferred terminal emulator. On Debian, there is the x-terminal-emulator
# symlink for example. # symlink for example.
for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal mate-terminal terminology; do for terminal in "$TERMINAL" x-terminal-emulator urxvt rxvt termit terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal mate-terminal terminology st; do
if command -v $terminal > /dev/null 2>&1; then if command -v "$terminal" > /dev/null 2>&1; then
exec $terminal "$@" exec "$terminal" "$@"
fi fi
done done

View File

@ -165,7 +165,6 @@ bindsym Mod1+r mode "resize"
# finds out, if available) # finds out, if available)
bar { bar {
status_command i3status status_command i3status
tray_output primary
} }
####################################################################### #######################################################################

View File

@ -152,5 +152,4 @@ bindcode $mod+27 mode "resize"
# finds out, if available) # finds out, if available)
bar { bar {
status_command i3status status_command i3status
tray_output primary
} }

View File

@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar
i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_SOURCES := $(wildcard i3bar/src/*.c)
i3bar_HEADERS := $(wildcard i3bar/include/*.h) i3bar_HEADERS := $(wildcard i3bar/include/*.h)
i3bar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) i3bar_CFLAGS = $(XCB_CFLAGS) $(XCB_CURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
i3bar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS) i3bar_LIBS = $(XCB_LIBS) $(XCB_CURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS)
i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)

View File

@ -31,6 +31,14 @@ typedef enum {
ALIGN_RIGHT ALIGN_RIGHT
} blockalign_t; } blockalign_t;
/* This data structure describes the way a status block should be rendered. These
* variables are updated each time the statusline is re-rendered. */
struct status_block_render_desc {
uint32_t width;
uint32_t x_offset;
uint32_t x_append;
};
/* This data structure represents one JSON dictionary, multiple of these make /* This data structure represents one JSON dictionary, multiple of these make
* up one status line. */ * up one status line. */
struct status_block { struct status_block {
@ -38,6 +46,8 @@ struct status_block {
i3String *short_text; i3String *short_text;
char *color; char *color;
char *background;
char *border;
/* min_width can be specified either as a numeric value (in pixels) or as a /* min_width can be specified either as a numeric value (in pixels) or as a
* string. For strings, we set min_width to the measured text width of * string. For strings, we set min_width to the measured text width of
@ -49,16 +59,14 @@ struct status_block {
bool urgent; bool urgent;
bool no_separator; bool no_separator;
bool is_markup; bool pango_markup;
/* The amount of pixels necessary to render a separater after the block. */ /* The amount of pixels necessary to render a separater after the block. */
uint32_t sep_block_width; uint32_t sep_block_width;
/* The amount of pixels necessary to render this block. These variables are /* Continuously-updated information on how to render this status block. */
* only temporarily used in refresh_statusline(). */ struct status_block_render_desc full_render;
uint32_t width; struct status_block_render_desc short_render;
uint32_t x_offset;
uint32_t x_append;
/* Optional */ /* Optional */
char *name; char *name;

View File

@ -29,6 +29,12 @@ typedef struct binding_t {
TAILQ_ENTRY(binding_t) bindings; TAILQ_ENTRY(binding_t) bindings;
} binding_t; } binding_t;
typedef struct tray_output_t {
char *output;
TAILQ_ENTRY(tray_output_t) tray_outputs;
} tray_output_t;
typedef struct config_t { typedef struct config_t {
int modifier; int modifier;
TAILQ_HEAD(bindings_head, binding_t) bindings; TAILQ_HEAD(bindings_head, binding_t) bindings;
@ -42,7 +48,7 @@ typedef struct config_t {
char *command; char *command;
char *fontname; char *fontname;
i3String *separator_symbol; i3String *separator_symbol;
char *tray_output; TAILQ_HEAD(tray_outputs_head, tray_output_t) tray_outputs;
int tray_padding; int tray_padding;
int num_outputs; int num_outputs;
char **outputs; char **outputs;

View File

@ -10,6 +10,7 @@
#pragma once #pragma once
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <cairo/cairo-xcb.h>
#include "common.h" #include "common.h"
@ -36,6 +37,12 @@ void init_outputs(void);
*/ */
i3_output* get_output_by_name(char* name); i3_output* get_output_by_name(char* name);
/*
* Returns true if the output has the currently focused workspace
*
*/
bool output_has_focus(i3_output* output);
struct i3_output { struct i3_output {
char* name; /* Name of the output */ char* name; /* Name of the output */
bool active; /* If the output is active */ bool active; /* If the output is active */
@ -44,9 +51,16 @@ struct i3_output {
int ws; /* The number of the currently visible ws */ int ws; /* The number of the currently visible ws */
rect rect; /* The rect (relative to the root window) */ rect rect; /* The rect (relative to the root window) */
xcb_window_t bar; /* The id of the bar of the output */ /* Off-screen buffer for preliminary rendering of the bar. */
xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */ surface_t buffer;
xcb_gcontext_t bargc; /* The graphical context of the bar */ /* Off-screen buffer for pre-rendering the statusline, separated to make clipping easier. */
surface_t statusline_buffer;
/* How much of statusline_buffer's horizontal space was used on last statusline render. */
int statusline_width;
/* Whether statusline block short texts where used on last statusline render. */
bool statusline_short_text;
/* The actual window on which we draw. */
surface_t bar;
struct ws_head* workspaces; /* The workspaces on this output */ struct ws_head* workspaces; /* The workspaces on this output */
struct tc_head* trayclients; /* The tray clients on this output */ struct tc_head* trayclients; /* The tray clients on this output */

View File

@ -15,7 +15,7 @@
#undef MIN #undef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MIN(x, y) ((x) < (y) ? (x) : (y))
#define STARTS_WITH(string, len, needle) ((len >= strlen(needle)) && strncasecmp(string, needle, strlen(needle)) == 0) #define STARTS_WITH(string, len, needle) (((len) >= strlen((needle))) && strncasecmp((string), (needle), strlen((needle))) == 0)
/* Securely free p */ /* Securely free p */
#define FREE(p) \ #define FREE(p) \

View File

@ -24,10 +24,17 @@
#define XEMBED_MAPPED (1 << 0) #define XEMBED_MAPPED (1 << 0)
#define XEMBED_EMBEDDED_NOTIFY 0 #define XEMBED_EMBEDDED_NOTIFY 0
/* We define xcb_request_failed as a macro to include the relevant line number */
#define xcb_request_failed(cookie, err_msg) _xcb_request_failed(cookie, err_msg, __LINE__)
int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line);
struct xcb_color_strings_t { struct xcb_color_strings_t {
char *bar_fg; char *bar_fg;
char *bar_bg; char *bar_bg;
char *sep_fg; char *sep_fg;
char *focus_bar_fg;
char *focus_bar_bg;
char *focus_sep_fg;
char *active_ws_fg; char *active_ws_fg;
char *active_ws_bg; char *active_ws_bg;
char *active_ws_border; char *active_ws_border;

View File

@ -75,6 +75,8 @@ static void clear_statusline(struct statusline_head *head, bool free_resources)
FREE(first->name); FREE(first->name);
FREE(first->instance); FREE(first->instance);
FREE(first->min_width_str); FREE(first->min_width_str);
FREE(first->background);
FREE(first->border);
} }
TAILQ_REMOVE(head, first, blocks); TAILQ_REMOVE(head, first, blocks);
@ -205,8 +207,16 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) {
sasprintf(&(ctx->block.color), "%.*s", len, val); sasprintf(&(ctx->block.color), "%.*s", len, val);
return 1; return 1;
} }
if (strcasecmp(ctx->last_map_key, "background") == 0) {
sasprintf(&(ctx->block.background), "%.*s", len, val);
return 1;
}
if (strcasecmp(ctx->last_map_key, "border") == 0) {
sasprintf(&(ctx->block.border), "%.*s", len, val);
return 1;
}
if (strcasecmp(ctx->last_map_key, "markup") == 0) { if (strcasecmp(ctx->last_map_key, "markup") == 0) {
ctx->block.is_markup = (len == strlen("pango") && !strncasecmp((const char *)val, "pango", strlen("pango"))); ctx->block.pango_markup = (len == strlen("pango") && !strncasecmp((const char *)val, "pango", strlen("pango")));
return 1; return 1;
} }
if (strcasecmp(ctx->last_map_key, "align") == 0) { if (strcasecmp(ctx->last_map_key, "align") == 0) {
@ -220,24 +230,15 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) {
return 1; return 1;
} }
if (strcasecmp(ctx->last_map_key, "min_width") == 0) { if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
char *copy = (char *)smalloc(len + 1); sasprintf(&(ctx->block.min_width_str), "%.*s", len, val);
strncpy(copy, (const char *)val, len);
copy[len] = 0;
ctx->block.min_width_str = copy;
return 1; return 1;
} }
if (strcasecmp(ctx->last_map_key, "name") == 0) { if (strcasecmp(ctx->last_map_key, "name") == 0) {
char *copy = (char *)smalloc(len + 1); sasprintf(&(ctx->block.name), "%.*s", len, val);
strncpy(copy, (const char *)val, len);
copy[len] = 0;
ctx->block.name = copy;
return 1; return 1;
} }
if (strcasecmp(ctx->last_map_key, "instance") == 0) { if (strcasecmp(ctx->last_map_key, "instance") == 0) {
char *copy = (char *)smalloc(len + 1); sasprintf(&(ctx->block.instance), "%.*s", len, val);
strncpy(copy, (const char *)val, len);
copy[len] = 0;
ctx->block.instance = copy;
return 1; return 1;
} }
@ -275,15 +276,15 @@ static int stdin_end_map(void *context) {
if (new_block->min_width_str) { if (new_block->min_width_str) {
i3String *text = i3string_from_utf8(new_block->min_width_str); i3String *text = i3string_from_utf8(new_block->min_width_str);
i3string_set_markup(text, new_block->is_markup); i3string_set_markup(text, new_block->pango_markup);
new_block->min_width = (uint32_t)predict_text_width(text); new_block->min_width = (uint32_t)predict_text_width(text);
i3string_free(text); i3string_free(text);
} }
i3string_set_markup(new_block->full_text, new_block->is_markup); i3string_set_markup(new_block->full_text, new_block->pango_markup);
if (new_block->short_text != NULL) if (new_block->short_text != NULL)
i3string_set_markup(new_block->short_text, new_block->is_markup); i3string_set_markup(new_block->short_text, new_block->pango_markup);
TAILQ_INSERT_TAIL(&statusline_buffer, new_block, blocks); TAILQ_INSERT_TAIL(&statusline_buffer, new_block, blocks);
return 1; return 1;

View File

@ -21,6 +21,7 @@
static char *cur_key; static char *cur_key;
static bool parsing_bindings; static bool parsing_bindings;
static bool parsing_tray_outputs;
/* /*
* Parse a key. * Parse a key.
@ -30,19 +31,22 @@ static bool parsing_bindings;
*/ */
static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) { static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
FREE(cur_key); FREE(cur_key);
sasprintf(&(cur_key), "%.*s", keyLen, keyVal);
cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1)); if (strcmp(cur_key, "bindings") == 0) {
strncpy(cur_key, (const char *)keyVal, keyLen);
cur_key[keyLen] = '\0';
if (strcmp(cur_key, "bindings") == 0)
parsing_bindings = true; parsing_bindings = true;
}
if (strcmp(cur_key, "tray_outputs") == 0) {
parsing_tray_outputs = true;
}
return 1; return 1;
} }
static int config_end_array_cb(void *params_) { static int config_end_array_cb(void *params_) {
parsing_bindings = false; parsing_bindings = false;
parsing_tray_outputs = false;
return 1; return 1;
} }
@ -93,6 +97,14 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
return 0; return 0;
} }
if (parsing_tray_outputs) {
DLOG("Adding tray_output = %.*s to the list.\n", len, val);
tray_output_t *tray_output = scalloc(1, sizeof(tray_output_t));
sasprintf(&(tray_output->output), "%.*s", len, val);
TAILQ_INSERT_TAIL(&(config.tray_outputs), tray_output, tray_outputs);
return 1;
}
if (!strcmp(cur_key, "mode")) { if (!strcmp(cur_key, "mode")) {
DLOG("mode = %.*s, len = %d\n", len, val, len); DLOG("mode = %.*s, len = %d\n", len, val, len);
config.hide_on_modifier = (len == 4 && !strncmp((const char *)val, "dock", strlen("dock")) ? M_DOCK config.hide_on_modifier = (len == 4 && !strncmp((const char *)val, "dock", strlen("dock")) ? M_DOCK
@ -109,6 +121,11 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
if (!strcmp(cur_key, "modifier")) { if (!strcmp(cur_key, "modifier")) {
DLOG("modifier = %.*s\n", len, val); DLOG("modifier = %.*s\n", len, val);
if (len == 4 && !strncmp((const char *)val, "none", strlen("none"))) {
config.modifier = XCB_NONE;
return 1;
}
if (len == 5 && !strncmp((const char *)val, "shift", strlen("shift"))) { if (len == 5 && !strncmp((const char *)val, "shift", strlen("shift"))) {
config.modifier = ShiftMask; config.modifier = ShiftMask;
return 1; return 1;
@ -128,16 +145,12 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
case '3': case '3':
config.modifier = Mod3Mask; config.modifier = Mod3Mask;
return 1; return 1;
/*
case '4':
config.modifier = Mod4Mask;
return 1;
*/
case '5': case '5':
config.modifier = Mod5Mask; config.modifier = Mod5Mask;
return 1; return 1;
} }
} }
config.modifier = Mod4Mask; config.modifier = Mod4Mask;
return 1; return 1;
} }
@ -198,10 +211,13 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
return 1; return 1;
} }
/* We keep the old single tray_output working for users who only restart i3bar
* after updating. */
if (!strcmp(cur_key, "tray_output")) { if (!strcmp(cur_key, "tray_output")) {
DLOG("tray_output %.*s\n", len, val); DLOG("Found deprecated key tray_output %.*s.\n", len, val);
FREE(config.tray_output); tray_output_t *tray_output = scalloc(1, sizeof(tray_output_t));
sasprintf(&config.tray_output, "%.*s", len, val); sasprintf(&(tray_output->output), "%.*s", len, val);
TAILQ_INSERT_TAIL(&(config.tray_outputs), tray_output, tray_outputs);
return 1; return 1;
} }
@ -217,6 +233,9 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
COLOR(statusline, bar_fg); COLOR(statusline, bar_fg);
COLOR(background, bar_bg); COLOR(background, bar_bg);
COLOR(separator, sep_fg); COLOR(separator, sep_fg);
COLOR(focused_statusline, focus_bar_fg);
COLOR(focused_background, focus_bar_bg);
COLOR(focused_separator, focus_sep_fg);
COLOR(focused_workspace_border, focus_ws_border); COLOR(focused_workspace_border, focus_ws_border);
COLOR(focused_workspace_bg, focus_ws_bg); COLOR(focused_workspace_bg, focus_ws_bg);
COLOR(focused_workspace_text, focus_ws_fg); COLOR(focused_workspace_text, focus_ws_fg);
@ -317,6 +336,7 @@ void parse_config_json(char *json) {
handle = yajl_alloc(&outputs_callbacks, NULL, NULL); handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
TAILQ_INIT(&(config.bindings)); TAILQ_INIT(&(config.bindings));
TAILQ_INIT(&(config.tray_outputs));
state = yajl_parse(handle, (const unsigned char *)json, strlen(json)); state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
@ -346,6 +366,9 @@ void free_colors(struct xcb_color_strings_t *colors) {
FREE_COLOR(bar_fg); FREE_COLOR(bar_fg);
FREE_COLOR(bar_bg); FREE_COLOR(bar_bg);
FREE_COLOR(sep_fg); FREE_COLOR(sep_fg);
FREE_COLOR(focus_bar_fg);
FREE_COLOR(focus_bar_bg);
FREE_COLOR(focus_sep_fg);
FREE_COLOR(active_ws_fg); FREE_COLOR(active_ws_fg);
FREE_COLOR(active_ws_bg); FREE_COLOR(active_ws_bg);
FREE_COLOR(active_ws_border); FREE_COLOR(active_ws_border);

View File

@ -17,6 +17,9 @@
#include <sys/un.h> #include <sys/un.h>
#include <i3/ipc.h> #include <i3/ipc.h>
#include <ev.h> #include <ev.h>
#ifdef I3_ASAN_ENABLED
#include <sanitizer/lsan_interface.h>
#endif
#include "common.h" #include "common.h"
@ -63,7 +66,6 @@ void got_output_reply(char *reply) {
DLOG("Parsing outputs JSON...\n"); DLOG("Parsing outputs JSON...\n");
parse_outputs_json(reply); parse_outputs_json(reply);
DLOG("Reconfiguring windows...\n"); DLOG("Reconfiguring windows...\n");
realloc_sl_buffer();
reconfig_windows(false); reconfig_windows(false);
i3_output *o_walk; i3_output *o_walk;
@ -175,7 +177,6 @@ void got_bar_config_update(char *event) {
/* update fonts and colors */ /* update fonts and colors */
init_xcb_late(config.fontname); init_xcb_late(config.fontname);
init_colors(&(config.colors)); init_colors(&(config.colors));
realloc_sl_buffer();
draw_bars(false); draw_bars(false);
} }
@ -214,6 +215,9 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
/* EOF received. Since i3 will restart i3bar instances as appropriate, /* EOF received. Since i3 will restart i3bar instances as appropriate,
* we exit here. */ * we exit here. */
DLOG("EOF received, exiting...\n"); DLOG("EOF received, exiting...\n");
#ifdef I3_ASAN_ENABLED
__lsan_do_leak_check();
#endif
clean_xcb(); clean_xcb();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }

View File

@ -137,6 +137,8 @@ int main(int argc, char **argv) {
if (socket_path == NULL) { if (socket_path == NULL) {
socket_path = atom_sock_path; socket_path = atom_sock_path;
} else {
free(atom_sock_path);
} }
if (socket_path == NULL) { if (socket_path == NULL) {
@ -149,6 +151,7 @@ int main(int argc, char **argv) {
/* Request the bar configuration. When it arrives, we fill the config array. */ /* Request the bar configuration. When it arrives, we fill the config array. */
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG, config.bar_id); i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG, config.bar_id);
} }
free(socket_path);
/* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main loop. /* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main loop.
* We only need those watchers on the stack, so putting them on the stack saves us * We only need those watchers on the stack, so putting them on the stack saves us

View File

@ -20,6 +20,8 @@
struct mode_json_params { struct mode_json_params {
char *json; char *json;
char *cur_key; char *cur_key;
char *name;
bool pango_markup;
mode *mode; mode *mode;
}; };
@ -31,17 +33,31 @@ static int mode_string_cb(void *params_, const unsigned char *val, size_t len) {
struct mode_json_params *params = (struct mode_json_params *)params_; struct mode_json_params *params = (struct mode_json_params *)params_;
if (!strcmp(params->cur_key, "change")) { if (!strcmp(params->cur_key, "change")) {
/* Save the name */ sasprintf(&(params->name), "%.*s", len, val);
params->mode->name = i3string_from_markup_with_length((const char *)val, len);
/* Save its rendered width */
params->mode->width = predict_text_width(params->mode->name);
DLOG("Got mode change: %s\n", i3string_as_utf8(params->mode->name));
FREE(params->cur_key); FREE(params->cur_key);
return 1; return 1;
} }
FREE(params->cur_key);
return 0;
}
/*
* Parse a boolean.
*
*/
static int mode_boolean_cb(void *params_, int val) {
struct mode_json_params *params = (struct mode_json_params *)params_;
if (strcmp(params->cur_key, "pango_markup") == 0) {
DLOG("Setting pango_markup to %d.\n", val);
params->pango_markup = val;
FREE(params->cur_key);
return 1;
}
FREE(params->cur_key);
return 0; return 0;
} }
@ -54,10 +70,21 @@ static int mode_string_cb(void *params_, const unsigned char *val, size_t len) {
static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) { static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
struct mode_json_params *params = (struct mode_json_params *)params_; struct mode_json_params *params = (struct mode_json_params *)params_;
FREE(params->cur_key); FREE(params->cur_key);
sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
return 1;
}
params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1)); static int mode_end_map_cb(void *params_) {
strncpy(params->cur_key, (const char *)keyVal, keyLen); struct mode_json_params *params = (struct mode_json_params *)params_;
params->cur_key[keyLen] = '\0';
/* Save the name */
params->mode->name = i3string_from_utf8(params->name);
i3string_set_markup(params->mode->name, params->pango_markup);
/* Save its rendered width */
params->mode->width = predict_text_width(params->mode->name);
DLOG("Got mode change: %s\n", i3string_as_utf8(params->mode->name));
FREE(params->cur_key);
return 1; return 1;
} }
@ -65,7 +92,9 @@ static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t ke
/* A datastructure to pass all these callbacks to yajl */ /* A datastructure to pass all these callbacks to yajl */
static yajl_callbacks mode_callbacks = { static yajl_callbacks mode_callbacks = {
.yajl_string = mode_string_cb, .yajl_string = mode_string_cb,
.yajl_boolean = mode_boolean_cb,
.yajl_map_key = mode_map_key_cb, .yajl_map_key = mode_map_key_cb,
.yajl_end_map = mode_end_map_cb,
}; };
/* /*

View File

@ -108,9 +108,8 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
struct outputs_json_params *params = (struct outputs_json_params *)params_; struct outputs_json_params *params = (struct outputs_json_params *)params_;
if (!strcmp(params->cur_key, "current_workspace")) { if (!strcmp(params->cur_key, "current_workspace")) {
char *copy = smalloc(sizeof(const unsigned char) * (len + 1)); char *copy = NULL;
strncpy(copy, (const char *)val, len); sasprintf(&copy, "%.*s", len, val);
copy[len] = '\0';
char *end; char *end;
errno = 0; errno = 0;
@ -118,7 +117,8 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
if (errno == 0 && if (errno == 0 &&
(end && *end == '\0')) (end && *end == '\0'))
params->outputs_walk->ws = parsed_num; params->outputs_walk->ws = parsed_num;
free(copy);
FREE(copy);
FREE(params->cur_key); FREE(params->cur_key);
return 1; return 1;
} }
@ -127,14 +127,9 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
return 0; return 0;
} }
char *name = smalloc(sizeof(const unsigned char) * (len + 1)); sasprintf(&(params->outputs_walk->name), "%.*s", len, val);
strncpy(name, (const char *)val, len);
name[len] = '\0';
params->outputs_walk->name = name;
FREE(params->cur_key); FREE(params->cur_key);
return 1; return 1;
} }
@ -149,9 +144,16 @@ static int outputs_start_map_cb(void *params_) {
if (params->cur_key == NULL) { if (params->cur_key == NULL) {
new_output = smalloc(sizeof(i3_output)); new_output = smalloc(sizeof(i3_output));
new_output->name = NULL; new_output->name = NULL;
new_output->active = false;
new_output->primary = false;
new_output->visible = false;
new_output->ws = 0, new_output->ws = 0,
new_output->statusline_width = 0;
new_output->statusline_short_text = false;
memset(&new_output->rect, 0, sizeof(rect)); memset(&new_output->rect, 0, sizeof(rect));
new_output->bar = XCB_NONE; memset(&new_output->bar, 0, sizeof(surface_t));
memset(&new_output->buffer, 0, sizeof(surface_t));
memset(&new_output->statusline_buffer, 0, sizeof(surface_t));
new_output->workspaces = smalloc(sizeof(struct ws_head)); new_output->workspaces = smalloc(sizeof(struct ws_head));
TAILQ_INIT(new_output->workspaces); TAILQ_INIT(new_output->workspaces);
@ -227,11 +229,7 @@ static int outputs_end_map_cb(void *params_) {
static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) { static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
struct outputs_json_params *params = (struct outputs_json_params *)params_; struct outputs_json_params *params = (struct outputs_json_params *)params_;
FREE(params->cur_key); FREE(params->cur_key);
sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
strncpy(params->cur_key, (const char *)keyVal, keyLen);
params->cur_key[keyLen] = '\0';
return 1; return 1;
} }
@ -304,3 +302,17 @@ i3_output *get_output_by_name(char *name) {
return walk; return walk;
} }
/*
* Returns true if the output has the currently focused workspace
*
*/
bool output_has_focus(i3_output *output) {
i3_ws *ws_walk;
TAILQ_FOREACH(ws_walk, output->workspaces, tailq) {
if (ws_walk->focused) {
return true;
}
}
return false;
}

View File

@ -102,8 +102,6 @@ static int workspaces_integer_cb(void *params_, long long val) {
static int workspaces_string_cb(void *params_, const unsigned char *val, size_t len) { static int workspaces_string_cb(void *params_, const unsigned char *val, size_t len) {
struct workspaces_json_params *params = (struct workspaces_json_params *)params_; struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
char *output_name;
if (!strcmp(params->cur_key, "name")) { if (!strcmp(params->cur_key, "name")) {
const char *ws_name = (const char *)val; const char *ws_name = (const char *)val;
params->workspaces_walk->canonical_name = sstrndup(ws_name, len); params->workspaces_walk->canonical_name = sstrndup(ws_name, len);
@ -147,11 +145,11 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
if (!strcmp(params->cur_key, "output")) { if (!strcmp(params->cur_key, "output")) {
/* We add the ws to the TAILQ of the output, it belongs to */ /* We add the ws to the TAILQ of the output, it belongs to */
output_name = smalloc(sizeof(const unsigned char) * (len + 1)); char *output_name = NULL;
strncpy(output_name, (const char *)val, len); sasprintf(&output_name, "%.*s", len, val);
output_name[len] = '\0';
i3_output *target = get_output_by_name(output_name); i3_output *target = get_output_by_name(output_name);
if (target) { if (target != NULL) {
params->workspaces_walk->output = target; params->workspaces_walk->output = target;
TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces, TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces,
@ -201,11 +199,7 @@ static int workspaces_start_map_cb(void *params_) {
static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) { static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
struct workspaces_json_params *params = (struct workspaces_json_params *)params_; struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
FREE(params->cur_key); FREE(params->cur_key);
sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
strncpy(params->cur_key, (const char *)keyVal, keyLen);
params->cur_key[keyLen] = '\0';
return 1; return 1;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +1,2 @@
xmacro(_NET_SUPPORTED) #include "atoms_NET_SUPPORTED.xmacro"
xmacro(_NET_SUPPORTING_WM_CHECK) #include "atoms_rest.xmacro"
xmacro(_NET_WM_NAME)
xmacro(_NET_WM_VISIBLE_NAME)
xmacro(_NET_WM_MOVERESIZE)
xmacro(_NET_WM_STATE_STICKY)
xmacro(_NET_WM_STATE_FULLSCREEN)
xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
xmacro(_NET_WM_STATE_MODAL)
xmacro(_NET_WM_STATE_HIDDEN)
xmacro(_NET_WM_STATE)
xmacro(_NET_WM_WINDOW_TYPE)
xmacro(_NET_WM_WINDOW_TYPE_NORMAL)
xmacro(_NET_WM_WINDOW_TYPE_DOCK)
xmacro(_NET_WM_WINDOW_TYPE_DIALOG)
xmacro(_NET_WM_WINDOW_TYPE_UTILITY)
xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR)
xmacro(_NET_WM_WINDOW_TYPE_SPLASH)
xmacro(_NET_WM_WINDOW_TYPE_MENU)
xmacro(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
xmacro(_NET_WM_WINDOW_TYPE_POPUP_MENU)
xmacro(_NET_WM_WINDOW_TYPE_TOOLTIP)
xmacro(_NET_WM_DESKTOP)
xmacro(_NET_WM_STRUT_PARTIAL)
xmacro(_NET_CLIENT_LIST)
xmacro(_NET_CLIENT_LIST_STACKING)
xmacro(_NET_CURRENT_DESKTOP)
xmacro(_NET_NUMBER_OF_DESKTOPS)
xmacro(_NET_DESKTOP_NAMES)
xmacro(_NET_DESKTOP_VIEWPORT)
xmacro(_NET_ACTIVE_WINDOW)
xmacro(_NET_CLOSE_WINDOW)
xmacro(_NET_STARTUP_ID)
xmacro(_NET_WORKAREA)
xmacro(WM_PROTOCOLS)
xmacro(WM_DELETE_WINDOW)
xmacro(UTF8_STRING)
xmacro(WM_STATE)
xmacro(WM_CLIENT_LEADER)
xmacro(WM_TAKE_FOCUS)
xmacro(WM_WINDOW_ROLE)
xmacro(I3_SOCKET_PATH)
xmacro(I3_CONFIG_PATH)
xmacro(I3_SYNC)
xmacro(I3_SHMLOG_PATH)
xmacro(I3_PID)
xmacro(_NET_REQUEST_FRAME_EXTENTS)
xmacro(_NET_FRAME_EXTENTS)
xmacro(_MOTIF_WM_HINTS)

View File

@ -0,0 +1,33 @@
xmacro(_NET_SUPPORTED)
xmacro(_NET_SUPPORTING_WM_CHECK)
xmacro(_NET_WM_NAME)
xmacro(_NET_WM_VISIBLE_NAME)
xmacro(_NET_WM_MOVERESIZE)
xmacro(_NET_WM_STATE_STICKY)
xmacro(_NET_WM_STATE_FULLSCREEN)
xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
xmacro(_NET_WM_STATE_MODAL)
xmacro(_NET_WM_STATE_HIDDEN)
xmacro(_NET_WM_STATE)
xmacro(_NET_WM_WINDOW_TYPE)
xmacro(_NET_WM_WINDOW_TYPE_NORMAL)
xmacro(_NET_WM_WINDOW_TYPE_DOCK)
xmacro(_NET_WM_WINDOW_TYPE_DIALOG)
xmacro(_NET_WM_WINDOW_TYPE_UTILITY)
xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR)
xmacro(_NET_WM_WINDOW_TYPE_SPLASH)
xmacro(_NET_WM_WINDOW_TYPE_MENU)
xmacro(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
xmacro(_NET_WM_WINDOW_TYPE_POPUP_MENU)
xmacro(_NET_WM_WINDOW_TYPE_TOOLTIP)
xmacro(_NET_WM_WINDOW_TYPE_NOTIFICATION)
xmacro(_NET_WM_DESKTOP)
xmacro(_NET_WM_STRUT_PARTIAL)
xmacro(_NET_CLIENT_LIST)
xmacro(_NET_CLIENT_LIST_STACKING)
xmacro(_NET_CURRENT_DESKTOP)
xmacro(_NET_NUMBER_OF_DESKTOPS)
xmacro(_NET_DESKTOP_NAMES)
xmacro(_NET_DESKTOP_VIEWPORT)
xmacro(_NET_ACTIVE_WINDOW)
xmacro(_NET_CLOSE_WINDOW)

19
include/atoms_rest.xmacro Normal file
View File

@ -0,0 +1,19 @@
xmacro(_NET_WM_USER_TIME)
xmacro(_NET_STARTUP_ID)
xmacro(_NET_WORKAREA)
xmacro(WM_PROTOCOLS)
xmacro(WM_DELETE_WINDOW)
xmacro(UTF8_STRING)
xmacro(WM_STATE)
xmacro(WM_CLIENT_LEADER)
xmacro(WM_TAKE_FOCUS)
xmacro(WM_WINDOW_ROLE)
xmacro(I3_SOCKET_PATH)
xmacro(I3_CONFIG_PATH)
xmacro(I3_SYNC)
xmacro(I3_SHMLOG_PATH)
xmacro(I3_PID)
xmacro(I3_FLOATING_WINDOW)
xmacro(_NET_REQUEST_FRAME_EXTENTS)
xmacro(_NET_FRAME_EXTENTS)
xmacro(_MOTIF_WM_HINTS)

View File

@ -15,7 +15,7 @@ extern pid_t command_error_nagbar_pid;
* The name of the default mode. * The name of the default mode.
* *
*/ */
const char *DEFAULT_BINDING_MODE; extern const char *DEFAULT_BINDING_MODE;
/** /**
* Adds a binding from config parameters given as strings and returns a * Adds a binding from config parameters given as strings and returns a
@ -25,7 +25,7 @@ const char *DEFAULT_BINDING_MODE;
*/ */
Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
const char *release, const char *border, const char *whole_window, const char *release, const char *border, const char *whole_window,
const char *command, const char *mode); const char *command, const char *mode, bool pango_markup);
/** /**
* Grab the bound keys (tell X to send us keypress events for those keycodes) * Grab the bound keys (tell X to send us keypress events for those keycodes)
@ -33,6 +33,13 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch
*/ */
void grab_all_keys(xcb_connection_t *conn); void grab_all_keys(xcb_connection_t *conn);
/**
* Release the button grabs on all managed windows and regrab them,
* reevaluating which buttons need to be grabbed.
*
*/
void regrab_all_buttons(xcb_connection_t *conn);
/** /**
* Returns a pointer to the Binding that matches the given xcb event or NULL if * Returns a pointer to the Binding that matches the given xcb event or NULL if
* no such binding exists. * no such binding exists.
@ -95,3 +102,12 @@ CommandResult *run_binding(Binding *bind, Con *con);
* *
*/ */
bool load_keymap(void); bool load_keymap(void);
/**
* Returns true if the current config has any binding to a scroll wheel button
* (4 or 5) which is a whole-window binding.
* We need this to figure out whether we should grab all buttons or just 1-3
* when managing a window. See #2049.
*
*/
bool bindings_should_grab_scrollwheel_buttons(void);

View File

@ -49,16 +49,16 @@ void cmd_move_con_to_workspace(I3_CMD, const char *which);
void cmd_move_con_to_workspace_back_and_forth(I3_CMD); void cmd_move_con_to_workspace_back_and_forth(I3_CMD);
/** /**
* Implementation of 'move [window|container] [to] workspace <name>'. * Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace <name>'.
* *
*/ */
void cmd_move_con_to_workspace_name(I3_CMD, const char *name); void cmd_move_con_to_workspace_name(I3_CMD, const char *name, const char *no_auto_back_and_forth);
/** /**
* Implementation of 'move [window|container] [to] workspace number <number>'. * Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace number <number>'.
* *
*/ */
void cmd_move_con_to_workspace_number(I3_CMD, const char *which); void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *no_auto_back_and_forth);
/** /**
* Implementation of 'resize set <px> [px] <px> [px]'. * Implementation of 'resize set <px> [px] <px> [px]'.
@ -76,7 +76,7 @@ void cmd_resize(I3_CMD, const char *way, const char *direction, long resize_px,
* Implementation of 'border normal|pixel [<n>]', 'border none|1pixel|toggle'. * Implementation of 'border normal|pixel [<n>]', 'border none|1pixel|toggle'.
* *
*/ */
void cmd_border(I3_CMD, const char *border_style_str, const char *border_width); void cmd_border(I3_CMD, const char *border_style_str, long border_width);
/** /**
* Implementation of 'nop <comment>'. * Implementation of 'nop <comment>'.
@ -97,10 +97,10 @@ void cmd_append_layout(I3_CMD, const char *path);
void cmd_workspace(I3_CMD, const char *which); void cmd_workspace(I3_CMD, const char *which);
/** /**
* Implementation of 'workspace number <number>' * Implementation of 'workspace [--no-auto-back-and-forth] number <number>'
* *
*/ */
void cmd_workspace_number(I3_CMD, const char *which); void cmd_workspace_number(I3_CMD, const char *which, const char *no_auto_back_and_forth);
/** /**
* Implementation of 'workspace back_and_forth'. * Implementation of 'workspace back_and_forth'.
@ -109,16 +109,16 @@ void cmd_workspace_number(I3_CMD, const char *which);
void cmd_workspace_back_and_forth(I3_CMD); void cmd_workspace_back_and_forth(I3_CMD);
/** /**
* Implementation of 'workspace <name>' * Implementation of 'workspace [--no-auto-back-and-forth] <name>'
* *
*/ */
void cmd_workspace_name(I3_CMD, const char *name); void cmd_workspace_name(I3_CMD, const char *name, const char *no_auto_back_and_forth);
/** /**
* Implementation of 'mark [--toggle] <mark>' * Implementation of 'mark [--add|--replace] [--toggle] <mark>'
* *
*/ */
void cmd_mark(I3_CMD, const char *mark, const char *toggle); void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle);
/** /**
* Implementation of 'unmark [mark]' * Implementation of 'unmark [mark]'
@ -157,7 +157,7 @@ void cmd_floating(I3_CMD, const char *floating_mode);
void cmd_move_workspace_to_output(I3_CMD, const char *name); void cmd_move_workspace_to_output(I3_CMD, const char *name);
/** /**
* Implementation of 'split v|h|vertical|horizontal'. * Implementation of 'split v|h|t|vertical|horizontal|toggle'.
* *
*/ */
void cmd_split(I3_CMD, const char *direction); void cmd_split(I3_CMD, const char *direction);

View File

@ -30,6 +30,12 @@ Con *con_new(Con *parent, i3Window *window);
*/ */
void con_focus(Con *con); void con_focus(Con *con);
/**
* Closes the given container.
*
*/
void con_close(Con *con, kill_window_t kill_window);
/** /**
* Returns true when this node is a leaf node (has no children) * Returns true when this node is a leaf node (has no children)
* *
@ -152,26 +158,34 @@ Con *con_by_frame_id(xcb_window_t frame);
*/ */
Con *con_by_mark(const char *mark); Con *con_by_mark(const char *mark);
/**
* Returns true if and only if the given containers holds the mark.
*
*/
bool con_has_mark(Con *con, const char *mark);
/** /**
* Toggles the mark on a container. * Toggles the mark on a container.
* If the container already has this mark, the mark is removed. * If the container already has this mark, the mark is removed.
* Otherwise, the mark is assigned to the container. * Otherwise, the mark is assigned to the container.
* *
*/ */
void con_mark_toggle(Con *con, const char *mark); void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode);
/** /**
* Assigns a mark to the container. * Assigns a mark to the container.
* *
*/ */
void con_mark(Con *con, const char *mark); void con_mark(Con *con, const char *mark, mark_mode_t mode);
/** /*
* If mark is NULL, this removes all existing marks. * Removes marks from containers.
* If con is NULL, all containers are considered.
* If name is NULL, this removes all existing marks.
* Otherwise, it will only remove the given mark (if it is present). * Otherwise, it will only remove the given mark (if it is present).
* *
*/ */
void con_unmark(const char *mark); void con_unmark(Con *con, const char *name);
/** /**
* Returns the first container below 'con' which wants to swallow this window * Returns the first container below 'con' which wants to swallow this window
@ -270,7 +284,7 @@ orientation_t con_orientation(Con *con);
/** /**
* Returns the container which will be focused next when the given container * Returns the container which will be focused next when the given container
* is not available anymore. Called in tree_close and con_move_to_workspace * is not available anymore. Called in tree_close_internal and con_move_to_workspace
* to properly restore focus. * to properly restore focus.
* *
*/ */
@ -419,3 +433,9 @@ char *con_get_tree_representation(Con *con);
* *
*/ */
void con_force_split_parents_redraw(Con *con); void con_force_split_parents_redraw(Con *con);
/**
* Returns the window title considering the current title format.
*
*/
i3String *con_parse_title_format(Con *con);

View File

@ -50,10 +50,11 @@ struct context {
* *
*/ */
struct Colortriple { struct Colortriple {
uint32_t border; color_t border;
uint32_t background; color_t background;
uint32_t text; color_t text;
uint32_t indicator; color_t indicator;
color_t child_border;
}; };
/** /**
@ -77,6 +78,7 @@ struct Variable {
*/ */
struct Mode { struct Mode {
char *name; char *name;
bool pango_markup;
struct bindings_head *bindings; struct bindings_head *bindings;
SLIST_ENTRY(Mode) modes; SLIST_ENTRY(Mode) modes;
@ -92,7 +94,7 @@ struct Config {
i3Font font; i3Font font;
char *ipc_socket_path; char *ipc_socket_path;
const char *restart_state_path; char *restart_state_path;
layout_t default_layout; layout_t default_layout;
int container_stack_limit; int container_stack_limit;
@ -201,7 +203,7 @@ struct Config {
/* Color codes are stored here */ /* Color codes are stored here */
struct config_client { struct config_client {
uint32_t background; color_t background;
struct Colortriple focused; struct Colortriple focused;
struct Colortriple focused_inactive; struct Colortriple focused_inactive;
struct Colortriple unfocused; struct Colortriple unfocused;
@ -247,9 +249,10 @@ struct Barconfig {
* simplicity (since we store just strings). */ * simplicity (since we store just strings). */
char **outputs; char **outputs;
/** Output on which the tray should be shown. The special value of 'no' /* List of outputs on which the tray is allowed to be shown, in order.
* disables the tray (its enabled by default). */ * The special value "none" disables it (per default, it will be shown) and
char *tray_output; * the special value "primary" enabled it on the primary output. */
TAILQ_HEAD(tray_outputs_head, tray_output_t) tray_outputs;
/* Padding around the tray icons. */ /* Padding around the tray icons. */
int tray_padding; int tray_padding;
@ -322,6 +325,10 @@ struct Barconfig {
char *statusline; char *statusline;
char *separator; char *separator;
char *focused_background;
char *focused_statusline;
char *focused_separator;
char *focused_workspace_border; char *focused_workspace_border;
char *focused_workspace_bg; char *focused_workspace_bg;
char *focused_workspace_text; char *focused_workspace_text;
@ -361,6 +368,12 @@ struct Barbinding {
TAILQ_ENTRY(Barbinding) bindings; TAILQ_ENTRY(Barbinding) bindings;
}; };
struct tray_output_t {
char *output;
TAILQ_ENTRY(tray_output_t) tray_outputs;
};
/** /**
* Finds the configuration file to use (either the one specified by * Finds the configuration file to use (either the one specified by
* override_configpath), the users one or the system default) and calls * override_configpath), the users one or the system default) and calls

View File

@ -59,14 +59,14 @@ CFGFUN(no_focus);
CFGFUN(ipc_socket, const char *path); CFGFUN(ipc_socket, const char *path);
CFGFUN(restart_state, const char *path); CFGFUN(restart_state, const char *path);
CFGFUN(popup_during_fullscreen, const char *value); CFGFUN(popup_during_fullscreen, const char *value);
CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator); CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator, const char *child_border);
CFGFUN(color_single, const char *colorclass, const char *color); CFGFUN(color_single, const char *colorclass, const char *color);
CFGFUN(floating_modifier, const char *modifiers); CFGFUN(floating_modifier, const char *modifiers);
CFGFUN(new_window, const char *windowtype, const char *border, const long width); CFGFUN(new_window, const char *windowtype, const char *border, const long width);
CFGFUN(workspace, const char *workspace, const char *output); CFGFUN(workspace, const char *workspace, const char *output);
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command); CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command);
CFGFUN(enter_mode, const char *mode); CFGFUN(enter_mode, const char *pango_markup, const char *mode);
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command); CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command);
CFGFUN(bar_font, const char *font); CFGFUN(bar_font, const char *font);

View File

@ -31,6 +31,11 @@ struct ConfigResultIR {
struct ConfigResultIR *parse_config(const char *input, struct context *context); struct ConfigResultIR *parse_config(const char *input, struct context *context);
/**
* launch nagbar to indicate errors in the configuration file.
*/
void start_config_error_nagbar(const char *configpath, bool has_errors);
/** /**
* Parses the given file by first replacing the variables, then calling * Parses the given file by first replacing the variables, then calling
* parse_config and launching i3-nagbar if use_nagbar is true. * parse_config and launching i3-nagbar if use_nagbar is true.

View File

@ -46,6 +46,7 @@ typedef struct Con Con;
typedef struct Match Match; typedef struct Match Match;
typedef struct Assignment Assignment; typedef struct Assignment Assignment;
typedef struct Window i3Window; typedef struct Window i3Window;
typedef struct mark_t mark_t;
/****************************************************************************** /******************************************************************************
* Helper types * Helper types
@ -61,7 +62,7 @@ typedef enum { BS_NORMAL = 0,
BS_NONE = 1, BS_NONE = 1,
BS_PIXEL = 2 } border_style_t; BS_PIXEL = 2 } border_style_t;
/** parameter to specify whether tree_close() and x_window_kill() should kill /** parameter to specify whether tree_close_internal() and x_window_kill() should kill
* only this specific window or the whole X11 client */ * only this specific window or the whole X11 client */
typedef enum { DONT_KILL_WINDOW = 0, typedef enum { DONT_KILL_WINDOW = 0,
KILL_WINDOW = 1, KILL_WINDOW = 1,
@ -74,6 +75,9 @@ typedef enum { ADJ_NONE = 0,
ADJ_UPPER_SCREEN_EDGE = (1 << 2), ADJ_UPPER_SCREEN_EDGE = (1 << 2),
ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t; ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
typedef enum { MM_REPLACE,
MM_ADD } mark_mode_t;
/** /**
* Container layouts. See Con::layout. * Container layouts. See Con::layout.
*/ */
@ -175,7 +179,7 @@ struct deco_render_params {
struct width_height con_rect; struct width_height con_rect;
struct width_height con_window_rect; struct width_height con_window_rect;
Rect con_deco_rect; Rect con_deco_rect;
uint32_t background; color_t background;
layout_t parent_layout; layout_t parent_layout;
bool con_is_leaf; bool con_is_leaf;
}; };
@ -372,8 +376,6 @@ struct Window {
/** The name of the window. */ /** The name of the window. */
i3String *name; i3String *name;
/** The format with which the window's name should be displayed. */
char *title_format;
/** The WM_WINDOW_ROLE of this window (for example, the pidgin buddy window /** The WM_WINDOW_ROLE of this window (for example, the pidgin buddy window
* sets "buddy list"). Useful to match specific windows in assignments or * sets "buddy list"). Useful to match specific windows in assignments or
@ -396,6 +398,9 @@ struct Window {
/** The _NET_WM_WINDOW_TYPE for this window. */ /** The _NET_WM_WINDOW_TYPE for this window. */
xcb_atom_t window_type; xcb_atom_t window_type;
/** The _NET_WM_DESKTOP for this window. */
uint32_t wm_desktop;
/** Whether the window says it is a dock window */ /** Whether the window says it is a dock window */
enum { W_NODOCK = 0, enum { W_NODOCK = 0,
W_DOCK_TOP = 1, W_DOCK_TOP = 1,
@ -432,6 +437,9 @@ struct Window {
* *
*/ */
struct Match { struct Match {
/* Set if a criterion was specified incorrectly. */
char *error;
struct regex *title; struct regex *title;
struct regex *application; struct regex *application;
struct regex *class; struct regex *class;
@ -523,6 +531,12 @@ typedef enum { CF_NONE = 0,
CF_OUTPUT = 1, CF_OUTPUT = 1,
CF_GLOBAL = 2 } fullscreen_mode_t; CF_GLOBAL = 2 } fullscreen_mode_t;
struct mark_t {
char *name;
TAILQ_ENTRY(mark_t) marks;
};
/** /**
* A 'Con' represents everything from the X11 root window down to a single X11 window. * A 'Con' represents everything from the X11 root window down to a single X11 window.
* *
@ -541,11 +555,10 @@ struct Con {
* change. */ * change. */
uint8_t ignore_unmap; uint8_t ignore_unmap;
/* ids/pixmap/graphics context for the frame window */ /* The surface used for the frame window. */
surface_t frame;
surface_t frame_buffer;
bool pixmap_recreated; bool pixmap_recreated;
xcb_window_t frame;
xcb_pixmap_t pixmap;
xcb_gcontext_t pm_gc;
enum { enum {
CT_ROOT = 0, CT_ROOT = 0,
@ -562,21 +575,30 @@ struct Con {
struct Con *parent; struct Con *parent;
/* The position and size for this con. These coordinates are absolute. Note
* that the rect of a container does not include the decoration. */
struct Rect rect; struct Rect rect;
/* The position and size of the actual client window. These coordinates are
* relative to the container's rect. */
struct Rect window_rect; struct Rect window_rect;
/* The position and size of the container's decoration. These coordinates
* are relative to the container's parent's rect. */
struct Rect deco_rect; struct Rect deco_rect;
/** the geometry this window requested when getting mapped */ /** the geometry this window requested when getting mapped */
struct Rect geometry; struct Rect geometry;
char *name; char *name;
/** The format with which the window's name should be displayed. */
char *title_format;
/* a sticky-group is an identifier which bundles several containers to a /* a sticky-group is an identifier which bundles several containers to a
* group. The contents are shared between all of them, that is they are * group. The contents are shared between all of them, that is they are
* displayed on whichever of the containers is currently visible */ * displayed on whichever of the containers is currently visible */
char *sticky_group; char *sticky_group;
/* user-definable mark to jump to this container later */ /* user-definable marks to jump to this container later */
char *mark; TAILQ_HEAD(marks_head, mark_t) marks_head;
/* cached to decide whether a redraw is needed */ /* cached to decide whether a redraw is needed */
bool mark_changed; bool mark_changed;

View File

@ -36,6 +36,13 @@ void ewmh_update_desktop_names(void);
*/ */
void ewmh_update_desktop_viewport(void); void ewmh_update_desktop_viewport(void);
/**
* Updates _NET_WM_DESKTOP for all windows.
* A request will only be made if the cached value differs from the calculated value.
*
*/
void ewmh_update_wm_desktop(void);
/** /**
* Updates _NET_ACTIVE_WINDOW with the currently focused window. * Updates _NET_ACTIVE_WINDOW with the currently focused window.
* *
@ -96,3 +103,21 @@ void ewmh_setup_hints(void);
* *
*/ */
void ewmh_update_workarea(void); void ewmh_update_workarea(void);
/**
* Returns the workspace container as enumerated by the EWMH desktop model.
* Returns NULL if no workspace could be found for the index.
*
* This is the reverse of ewmh_get_workspace_index.
*
*/
Con *ewmh_get_workspace_by_index(uint32_t idx);
/**
* Returns the EWMH desktop index for the workspace the given container is on.
* Returns NET_WM_DESKTOP_NONE if the desktop index cannot be determined.
*
* This is the reverse of ewmh_get_workspace_by_index.
*
*/
uint32_t ewmh_get_workspace_index(Con *con);

View File

@ -35,7 +35,6 @@ extern struct rlimit original_rlimit_core;
extern bool debug_build; extern bool debug_build;
/** The number of file descriptors passed via socket activation. */ /** The number of file descriptors passed via socket activation. */
extern int listen_fds; extern int listen_fds;
extern xcb_connection_t *conn;
extern int conn_screen; extern int conn_screen;
/** /**
* The EWMH support window that is used to indicate that an EWMH-compliant * The EWMH support window that is used to indicate that an EWMH-compliant
@ -61,7 +60,6 @@ extern TAILQ_HEAD(autostarts_always_head, Autostart) autostarts_always;
extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments; extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments;
extern TAILQ_HEAD(assignments_head, Assignment) assignments; extern TAILQ_HEAD(assignments_head, Assignment) assignments;
extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins; extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins;
extern xcb_screen_t *root_screen;
/* Color depth, visual id and colormap to use when creating windows and /* Color depth, visual id and colormap to use when creating windows and
* pixmaps. Will use 32 bit depth and an appropriate visual, if available, * pixmaps. Will use 32 bit depth and an appropriate visual, if available,

View File

@ -20,9 +20,19 @@
#if PANGO_SUPPORT #if PANGO_SUPPORT
#include <pango/pango.h> #include <pango/pango.h>
#endif #endif
#ifdef CAIRO_SUPPORT
#include <cairo/cairo-xcb.h>
#endif
#define DEFAULT_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) #define DEFAULT_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
/**
* XCB connection and root screen
*
*/
extern xcb_connection_t *conn;
extern xcb_screen_t *root_screen;
/** /**
* Opaque data structure for storing strings. * Opaque data structure for storing strings.
* *
@ -243,7 +253,7 @@ bool i3string_is_markup(i3String *str);
/** /**
* Set whether the i3String should use Pango markup. * Set whether the i3String should use Pango markup.
*/ */
void i3string_set_markup(i3String *str, bool is_markup); void i3string_set_markup(i3String *str, bool pango_markup);
/** /**
* Escape pango markup characters in the given string. * Escape pango markup characters in the given string.
@ -382,11 +392,24 @@ char *convert_ucs2_to_utf8(xcb_char2b_t *text, size_t num_glyphs);
*/ */
xcb_char2b_t *convert_utf8_to_ucs2(char *input, size_t *real_strlen); xcb_char2b_t *convert_utf8_to_ucs2(char *input, size_t *real_strlen);
/* Represents a color split by color channel. */
typedef struct color_t {
double red;
double green;
double blue;
double alpha;
/* The colorpixel we use for direct XCB calls. */
uint32_t colorpixel;
} color_t;
#define COLOR_TRANSPARENT ((color_t){.red = 0.0, .green = 0.0, .blue = 0.0, .colorpixel = 0})
/** /**
* Defines the colors to be used for the forthcoming draw_text calls. * Defines the colors to be used for the forthcoming draw_text calls.
* *
*/ */
void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background); void set_font_colors(xcb_gcontext_t gc, color_t foreground, color_t background);
/** /**
* Returns true if and only if the current font is a pango font. * Returns true if and only if the current font is a pango font.
@ -402,8 +425,8 @@ bool font_is_pango(void);
* Text must be specified as an i3String. * Text must be specified as an i3String.
* *
*/ */
void draw_text(i3String *text, xcb_drawable_t drawable, void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc,
xcb_gcontext_t gc, int x, int y, int max_width); xcb_visualtype_t *visual, int x, int y, int max_width);
/** /**
* ASCII version of draw_text to print static strings. * ASCII version of draw_text to print static strings.
@ -480,3 +503,106 @@ char *get_config_path(const char *override_configpath, bool use_system_paths);
*/ */
int mkdirp(const char *path, mode_t mode); int mkdirp(const char *path, mode_t mode);
#endif #endif
/** Helper structure for usage in format_placeholders(). */
typedef struct placeholder_t {
/* The placeholder to be replaced, e.g., "%title". */
char *name;
/* The value this placeholder should be replaced with. */
char *value;
} placeholder_t;
/**
* Replaces occurrences of the defined placeholders in the format string.
*
*/
char *format_placeholders(char *format, placeholder_t *placeholders, int num);
#ifdef CAIRO_SUPPORT
/* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989
* and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */
#define CAIRO_SURFACE_FLUSH(surface) \
do { \
cairo_surface_flush(surface); \
cairo_surface_flush(surface); \
} while (0)
#endif
/* A wrapper grouping an XCB drawable and both a graphics context
* and the corresponding cairo objects representing it. */
typedef struct surface_t {
/* The drawable which is being represented. */
xcb_drawable_t id;
/* A classic XCB graphics context. */
xcb_gcontext_t gc;
xcb_visualtype_t *visual_type;
int width;
int height;
#ifdef CAIRO_SUPPORT
/* A cairo surface representing the drawable. */
cairo_surface_t *surface;
/* The cairo object representing the drawable. In general,
* this is what one should use for any drawing operation. */
cairo_t *cr;
#endif
} surface_t;
/**
* Initialize the surface to represent the given drawable.
*
*/
void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_drawable_t drawable,
xcb_visualtype_t *visual, int width, int height);
/**
* Resize the surface to the given size.
*
*/
void draw_util_surface_set_size(surface_t *surface, int width, int height);
/**
* Destroys the surface.
*
*/
void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface);
/**
* Parses the given color in hex format to an internal color representation.
* Note that the input must begin with a hash sign, e.g., "#3fbc59".
*
*/
color_t draw_util_hex_to_color(const char *color);
/**
* Draw the given text using libi3.
* This function also marks the surface dirty which is needed if other means of
* drawing are used. This will be the case when using XCB to draw text.
*
*/
void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width);
/**
* Draws a filled rectangle.
* This function is a convenience wrapper and takes care of flushing the
* surface as well as restoring the cairo state.
*
*/
void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t color, double x, double y, double w, double h);
/**
* Clears a surface with the given color.
*
*/
void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t color);
/**
* Copies a surface onto another surface.
*
*/
void draw_util_copy_surface(xcb_connection_t *conn, surface_t *src, surface_t *dest, double src_x, double src_y,
double dest_x, double dest_y, double width, double height);

View File

@ -10,6 +10,23 @@
*/ */
#pragma once #pragma once
/* This is used to keep a state to pass around when rendering a con in render_con(). */
typedef struct render_params {
/* A copy of the coordinates of the container which is being rendered. */
int x;
int y;
/* The computed height for decorations. */
int deco_height;
/* Container rect, subtract container border. This is the actually usable space
* inside this container for clients. */
Rect rect;
/* The number of children of the container which is being rendered. */
int children;
/* A precalculated list of sizes of each child. */
int *sizes;
} render_params;
/** /**
* "Renders" the given container (and its children), meaning that all rects are * "Renders" the given container (and its children), meaning that all rects are
* updated correctly. Note that this function does not call any xcb_* * updated correctly. Note that this function does not call any xcb_*

View File

@ -56,12 +56,6 @@ bool level_down(void);
*/ */
void tree_render(void); void tree_render(void);
/**
* Closes the current container using tree_close().
*
*/
void tree_close_con(kill_window_t kill_window);
/** /**
* Changes focus in the given way (next/previous) and given orientation * Changes focus in the given way (next/previous) and given orientation
* (horizontal/vertical). * (horizontal/vertical).
@ -78,11 +72,11 @@ void tree_next(char way, orientation_t orientation);
* recursively while deleting a containers children. * recursively while deleting a containers children.
* *
* The force_set_focus flag is specified in the case of killing a floating * The force_set_focus flag is specified in the case of killing a floating
* window: tree_close() will be invoked for the CT_FLOATINGCON (the parent * window: tree_close_internal() will be invoked for the CT_FLOATINGCON (the parent
* container) and focus should be set there. * container) and focus should be set there.
* *
*/ */
bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus); bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus);
/** /**
* Loads tree from ~/.i3/_restart.json (used for in-place restarts). * Loads tree from ~/.i3/_restart.json (used for in-place restarts).

View File

@ -20,7 +20,7 @@
if (pointer == NULL) \ if (pointer == NULL) \
die(__VA_ARGS__); \ die(__VA_ARGS__); \
} }
#define STARTS_WITH(string, needle) (strncasecmp(string, needle, strlen(needle)) == 0) #define STARTS_WITH(string, needle) (strncasecmp((string), (needle), strlen((needle))) == 0)
#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_NEXT(elm, field) : NULL) #define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_NEXT(elm, field) : NULL)
#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_PREV(elm, field) : NULL) #define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_PREV(elm, field) : NULL)
#define FOR_TABLE(workspace) \ #define FOR_TABLE(workspace) \
@ -130,6 +130,13 @@ void *memmem(const void *l, size_t l_len, const void *s, size_t s_len);
#endif #endif
/**
* Escapes the given string if a pango font is currently used.
* If the string has to be escaped, the input string will be free'd.
*
*/
char *pango_escape_markup(char *input);
/** /**
* Starts an i3-nagbar instance with the given parameters. Takes care of * Starts an i3-nagbar instance with the given parameters. Takes care of
* handling SIGCHLD and killing i3-nagbar when i3 exits. * handling SIGCHLD and killing i3-nagbar when i3 exits.

View File

@ -9,6 +9,12 @@
*/ */
#pragma once #pragma once
/**
* Frees an i3Window and all its members.
*
*/
void window_free(i3Window *win);
/** /**
* Updates the WM_CLASS (consisting of the class and instance) for the * Updates the WM_CLASS (consisting of the class and instance) for the
* given window. * given window.
@ -81,10 +87,3 @@ void window_update_hints(i3Window *win, xcb_get_property_reply_t *prop, bool *ur
* *
*/ */
void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style); void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style);
/**
* Returns the window title considering the current title format.
* If no format is set, this will simply return the window's name.
*
*/
i3String *window_parse_title_format(i3Window *win);

View File

@ -14,6 +14,14 @@
#include "tree.h" #include "tree.h"
#include "randr.h" #include "randr.h"
/* We use NET_WM_DESKTOP_NONE for cases where we cannot determine the EWMH
* desktop index for a window. We cannot use a negative value like -1 since we
* need to use uint32_t as we actually need the full range of it. This is
* technically dangerous, but it's safe to assume that we will never have more
* than 4294967279 workspaces open at a time. */
#define NET_WM_DESKTOP_NONE 0xFFFFFFF0
#define NET_WM_DESKTOP_ALL 0xFFFFFFFF
/** /**
* Returns a pointer to the workspace with the given number (starting at 0), * Returns a pointer to the workspace with the given number (starting at 0),
* creating the workspace if necessary (by allocating the necessary amount of * creating the workspace if necessary (by allocating the necessary amount of

View File

@ -140,6 +140,12 @@ void xcb_set_root_cursor(int cursor);
*/ */
uint16_t get_visual_depth(xcb_visualid_t visual_id); uint16_t get_visual_depth(xcb_visualid_t visual_id);
/**
* Get visual type specified by visualid
*
*/
xcb_visualtype_t *get_visualtype_by_id(xcb_visualid_t visual_id);
/** /**
* Get visualid with specified depth * Get visualid with specified depth
* *
@ -160,3 +166,9 @@ void xcb_add_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom
* *
*/ */
void xcb_remove_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom); void xcb_remove_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom);
/**
* Grab the specified buttons on a window when managing it.
*
*/
void xcb_grab_buttons(xcb_connection_t *conn, xcb_window_t window, bool bind_scrollwheel);

View File

@ -8,8 +8,6 @@
#include "libi3.h" #include "libi3.h"
#include <math.h> #include <math.h>
extern xcb_screen_t *root_screen;
/* /*
* Convert a logical amount of pixels (e.g. 2 pixels on a standard 96 DPI * Convert a logical amount of pixels (e.g. 2 pixels on a standard 96 DPI
* screen) to a corresponding amount of physical pixels on a standard or retina * screen) to a corresponding amount of physical pixels on a standard or retina

252
libi3/draw_util.c Normal file
View File

@ -0,0 +1,252 @@
/*
* vim:ts=4:sw=4:expandtab
*
* © 2015 Ingo Bürk and contributors (see also: LICENSE)
*
* draw.c: Utility for drawing.
*
*/
#include <stdlib.h>
#include <err.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#ifdef CAIRO_SUPPORT
#include <cairo/cairo-xcb.h>
#endif
#include "libi3.h"
/* The default visual_type to use if none is specified when creating the surface. Must be defined globally. */
xcb_visualtype_t *visual_type;
/* Forward declarations */
static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surface, color_t color);
#define RETURN_UNLESS_SURFACE_INITIALIZED(surface) \
do { \
if ((surface)->id == XCB_NONE) { \
ELOG("Surface %p is not initialized, skipping drawing.\n", surface); \
return; \
} \
} while (0)
/*
* Initialize the surface to represent the given drawable.
*
*/
void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_drawable_t drawable,
xcb_visualtype_t *visual, int width, int height) {
surface->id = drawable;
surface->visual_type = ((visual == NULL) ? visual_type : visual);
surface->width = width;
surface->height = height;
surface->gc = xcb_generate_id(conn);
xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(conn, surface->gc, surface->id, 0, NULL);
xcb_generic_error_t *error = xcb_request_check(conn, gc_cookie);
if (error != NULL) {
ELOG("Could not create graphical context. Error code: %d. Please report this bug.\n", error->error_code);
}
#ifdef CAIRO_SUPPORT
surface->surface = cairo_xcb_surface_create(conn, surface->id, surface->visual_type, width, height);
surface->cr = cairo_create(surface->surface);
#endif
}
/*
* Destroys the surface.
*
*/
void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface) {
xcb_free_gc(conn, surface->gc);
#ifdef CAIRO_SUPPORT
cairo_surface_destroy(surface->surface);
cairo_destroy(surface->cr);
/* We need to explicitly set these to NULL to avoid assertion errors in
* cairo when calling this multiple times. This can happen, for example,
* when setting the border of a window to none and then closing it. */
surface->surface = NULL;
surface->cr = NULL;
#endif
}
/*
* Resize the surface to the given size.
*
*/
void draw_util_surface_set_size(surface_t *surface, int width, int height) {
surface->width = width;
surface->height = height;
#ifdef CAIRO_SUPPORT
cairo_xcb_surface_set_size(surface->surface, width, height);
#endif
}
/*
* Parses the given color in hex format to an internal color representation.
* Note that the input must begin with a hash sign, e.g., "#3fbc59".
*
*/
color_t draw_util_hex_to_color(const char *color) {
char alpha[2];
if (strlen(color) == strlen("#rrggbbaa")) {
alpha[0] = color[7];
alpha[1] = color[8];
} else {
alpha[0] = alpha[1] = 'F';
}
char groups[4][3] = {
{color[1], color[2], '\0'},
{color[3], color[4], '\0'},
{color[5], color[6], '\0'},
{alpha[0], alpha[1], '\0'}};
return (color_t){
.red = strtol(groups[0], NULL, 16) / 255.0,
.green = strtol(groups[1], NULL, 16) / 255.0,
.blue = strtol(groups[2], NULL, 16) / 255.0,
.alpha = strtol(groups[3], NULL, 16) / 255.0,
.colorpixel = get_colorpixel(color)};
}
/*
* Set the given color as the source color on the surface.
*
*/
static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surface, color_t color) {
RETURN_UNLESS_SURFACE_INITIALIZED(surface);
#ifdef CAIRO_SUPPORT
cairo_set_source_rgba(surface->cr, color.red, color.green, color.blue, color.alpha);
#else
uint32_t colorpixel = color.colorpixel;
xcb_change_gc(conn, surface->gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND,
(uint32_t[]){colorpixel, colorpixel});
#endif
}
/**
* Draw the given text using libi3.
* This function also marks the surface dirty which is needed if other means of
* drawing are used. This will be the case when using XCB to draw text.
*
*/
void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) {
RETURN_UNLESS_SURFACE_INITIALIZED(surface);
#ifdef CAIRO_SUPPORT
/* Flush any changes before we draw the text as this might use XCB directly. */
CAIRO_SURFACE_FLUSH(surface->surface);
#endif
set_font_colors(surface->gc, fg_color, bg_color);
draw_text(text, surface->id, surface->gc, surface->visual_type, x, y, max_width);
#ifdef CAIRO_SUPPORT
/* Notify cairo that we (possibly) used another way to draw on the surface. */
cairo_surface_mark_dirty(surface->surface);
#endif
}
/**
* Draws a filled rectangle.
* This function is a convenience wrapper and takes care of flushing the
* surface as well as restoring the cairo state.
*
*/
void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t color, double x, double y, double w, double h) {
RETURN_UNLESS_SURFACE_INITIALIZED(surface);
#ifdef CAIRO_SUPPORT
cairo_save(surface->cr);
/* Using the SOURCE operator will copy both color and alpha information directly
* onto the surface rather than blending it. This is a bit more efficient and
* allows better color control for the user when using opacity. */
cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE);
draw_util_set_source_color(conn, surface, color);
cairo_rectangle(surface->cr, x, y, w, h);
cairo_fill(surface->cr);
/* Make sure we flush the surface for any text drawing operations that could follow.
* Since we support drawing text via XCB, we need this. */
CAIRO_SURFACE_FLUSH(surface->surface);
cairo_restore(surface->cr);
#else
draw_util_set_source_color(conn, surface, color);
xcb_rectangle_t rect = {x, y, w, h};
xcb_poly_fill_rectangle(conn, surface->id, surface->gc, 1, &rect);
#endif
}
/**
* Clears a surface with the given color.
*
*/
void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t color) {
RETURN_UNLESS_SURFACE_INITIALIZED(surface);
#ifdef CAIRO_SUPPORT
cairo_save(surface->cr);
/* Using the SOURCE operator will copy both color and alpha information directly
* onto the surface rather than blending it. This is a bit more efficient and
* allows better color control for the user when using opacity. */
cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE);
draw_util_set_source_color(conn, surface, color);
cairo_paint(surface->cr);
/* Make sure we flush the surface for any text drawing operations that could follow.
* Since we support drawing text via XCB, we need this. */
CAIRO_SURFACE_FLUSH(surface->surface);
cairo_restore(surface->cr);
#else
draw_util_set_source_color(conn, surface, color);
xcb_rectangle_t rect = {0, 0, surface->width, surface->height};
xcb_poly_fill_rectangle(conn, surface->id, surface->gc, 1, &rect);
#endif
}
/**
* Copies a surface onto another surface.
*
*/
void draw_util_copy_surface(xcb_connection_t *conn, surface_t *src, surface_t *dest, double src_x, double src_y,
double dest_x, double dest_y, double width, double height) {
RETURN_UNLESS_SURFACE_INITIALIZED(src);
RETURN_UNLESS_SURFACE_INITIALIZED(dest);
#ifdef CAIRO_SUPPORT
cairo_save(dest->cr);
/* Using the SOURCE operator will copy both color and alpha information directly
* onto the surface rather than blending it. This is a bit more efficient and
* allows better color control for the user when using opacity. */
cairo_set_operator(dest->cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_surface(dest->cr, src->surface, dest_x - src_x, dest_y - src_y);
cairo_rectangle(dest->cr, dest_x, dest_y, width, height);
cairo_fill(dest->cr);
/* Make sure we flush the surface for any text drawing operations that could follow.
* Since we support drawing text via XCB, we need this. */
CAIRO_SURFACE_FLUSH(src->surface);
CAIRO_SURFACE_FLUSH(dest->surface);
cairo_restore(dest->cr);
#else
xcb_copy_area(conn, src->id, dest->id, dest->gc, (int16_t)src_x, (int16_t)src_y,
(int16_t)dest_x, (int16_t)dest_y, (uint16_t)width, (uint16_t)height);
#endif
}

View File

@ -12,16 +12,13 @@
#include <stdbool.h> #include <stdbool.h>
#include <err.h> #include <err.h>
#if PANGO_SUPPORT
#include <cairo/cairo-xcb.h> #include <cairo/cairo-xcb.h>
#if PANGO_SUPPORT
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
#endif #endif
#include "libi3.h" #include "libi3.h"
extern xcb_connection_t *conn;
extern xcb_screen_t *root_screen;
static const i3Font *savedFont = NULL; static const i3Font *savedFont = NULL;
#if PANGO_SUPPORT #if PANGO_SUPPORT
@ -102,12 +99,12 @@ static bool load_pango_font(i3Font *font, const char *desc) {
* *
*/ */
static void draw_text_pango(const char *text, size_t text_len, static void draw_text_pango(const char *text, size_t text_len,
xcb_drawable_t drawable, int x, int y, xcb_drawable_t drawable, xcb_visualtype_t *visual, int x, int y,
int max_width, bool is_markup) { int max_width, bool pango_markup) {
/* Create the Pango layout */ /* Create the Pango layout */
/* root_visual_type is cached in load_pango_font */ /* root_visual_type is cached in load_pango_font */
cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable, cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable,
root_visual_type, x + max_width, y + savedFont->height); visual, x + max_width, y + savedFont->height);
cairo_t *cr = cairo_create(surface); cairo_t *cr = cairo_create(surface);
PangoLayout *layout = create_layout_with_dpi(cr); PangoLayout *layout = create_layout_with_dpi(cr);
gint height; gint height;
@ -117,12 +114,13 @@ static void draw_text_pango(const char *text, size_t text_len,
pango_layout_set_wrap(layout, PANGO_WRAP_CHAR); pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
if (is_markup) if (pango_markup)
pango_layout_set_markup(layout, text, text_len); pango_layout_set_markup(layout, text, text_len);
else else
pango_layout_set_text(layout, text, text_len); pango_layout_set_text(layout, text, text_len);
/* Do the drawing */ /* Do the drawing */
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue); cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue);
pango_cairo_update_layout(cr, layout); pango_cairo_update_layout(cr, layout);
pango_layout_get_pixel_size(layout, NULL, &height); pango_layout_get_pixel_size(layout, NULL, &height);
@ -142,7 +140,7 @@ static void draw_text_pango(const char *text, size_t text_len,
* Calculate the text width using Pango rendering. * Calculate the text width using Pango rendering.
* *
*/ */
static int predict_text_width_pango(const char *text, size_t text_len, bool is_markup) { static int predict_text_width_pango(const char *text, size_t text_len, bool pango_markup) {
/* Create a dummy Pango layout */ /* Create a dummy Pango layout */
/* root_visual_type is cached in load_pango_font */ /* 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_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1);
@ -153,7 +151,7 @@ static int predict_text_width_pango(const char *text, size_t text_len, bool is_m
gint width; gint width;
pango_layout_set_font_description(layout, savedFont->specific.pango_desc); pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
if (is_markup) if (pango_markup)
pango_layout_set_markup(layout, text, text_len); pango_layout_set_markup(layout, text, text_len);
else else
pango_layout_set_text(layout, text, text_len); pango_layout_set_text(layout, text, text_len);
@ -226,6 +224,7 @@ i3Font load_font(const char *pattern, const bool fallback) {
info_cookie = xcb_query_font(conn, font.specific.xcb.id); info_cookie = xcb_query_font(conn, font.specific.xcb.id);
/* Check if we managed to open 'fixed' */ /* Check if we managed to open 'fixed' */
free(error);
error = xcb_request_check(conn, font_cookie); error = xcb_request_check(conn, font_cookie);
/* Fall back to '-misc-*' if opening 'fixed' fails. */ /* Fall back to '-misc-*' if opening 'fixed' fails. */
@ -236,12 +235,16 @@ i3Font load_font(const char *pattern, const bool fallback) {
strlen(pattern), pattern); strlen(pattern), pattern);
info_cookie = xcb_query_font(conn, font.specific.xcb.id); info_cookie = xcb_query_font(conn, font.specific.xcb.id);
free(error);
if ((error = xcb_request_check(conn, font_cookie)) != NULL) if ((error = xcb_request_check(conn, font_cookie)) != NULL)
errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks " errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks "
"(fixed or -misc-*): X11 error %d", "(fixed or -misc-*): X11 error %d",
error->error_code); error->error_code);
} }
} }
if (error != NULL) {
free(error);
}
font.pattern = sstrdup(pattern); font.pattern = sstrdup(pattern);
LOG("Using X font %s\n", pattern); LOG("Using X font %s\n", pattern);
@ -312,7 +315,7 @@ void free_font(void) {
* Defines the colors to be used for the forthcoming draw_text calls. * Defines the colors to be used for the forthcoming draw_text calls.
* *
*/ */
void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background) { void set_font_colors(xcb_gcontext_t gc, color_t foreground, color_t background) {
assert(savedFont != NULL); assert(savedFont != NULL);
switch (savedFont->type) { switch (savedFont->type) {
@ -322,16 +325,16 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background
case FONT_TYPE_XCB: { case FONT_TYPE_XCB: {
/* Change the font and colors in the GC */ /* Change the font and colors in the GC */
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT; uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
uint32_t values[] = {foreground, background, savedFont->specific.xcb.id}; uint32_t values[] = {foreground.colorpixel, background.colorpixel, savedFont->specific.xcb.id};
xcb_change_gc(conn, gc, mask, values); xcb_change_gc(conn, gc, mask, values);
break; break;
} }
#if PANGO_SUPPORT #if PANGO_SUPPORT
case FONT_TYPE_PANGO: case FONT_TYPE_PANGO:
/* Save the foreground font */ /* Save the foreground font */
pango_font_red = ((foreground >> 16) & 0xff) / 255.0; pango_font_red = foreground.red;
pango_font_green = ((foreground >> 8) & 0xff) / 255.0; pango_font_green = foreground.green;
pango_font_blue = (foreground & 0xff) / 255.0; pango_font_blue = foreground.blue;
break; break;
#endif #endif
default: default:
@ -391,9 +394,12 @@ static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawabl
* Text must be specified as an i3String. * Text must be specified as an i3String.
* *
*/ */
void draw_text(i3String *text, xcb_drawable_t drawable, void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc,
xcb_gcontext_t gc, int x, int y, int max_width) { xcb_visualtype_t *visual, int x, int y, int max_width) {
assert(savedFont != NULL); assert(savedFont != NULL);
if (visual == NULL) {
visual = root_visual_type;
}
switch (savedFont->type) { switch (savedFont->type) {
case FONT_TYPE_NONE: case FONT_TYPE_NONE:
@ -407,7 +413,7 @@ void draw_text(i3String *text, xcb_drawable_t drawable,
case FONT_TYPE_PANGO: case FONT_TYPE_PANGO:
/* Render the text using Pango */ /* Render the text using Pango */
draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
drawable, x, y, max_width, i3string_is_markup(text)); drawable, visual, x, y, max_width, i3string_is_markup(text));
return; return;
#endif #endif
default: default:
@ -432,7 +438,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable,
if (text_len > 255) { if (text_len > 255) {
/* The text is too long to draw it directly to X */ /* The text is too long to draw it directly to X */
i3String *str = i3string_from_utf8(text); i3String *str = i3string_from_utf8(text);
draw_text(str, drawable, gc, x, y, max_width); draw_text(str, drawable, gc, NULL, x, y, max_width);
i3string_free(str); i3string_free(str);
} else { } else {
/* X11 coordinates for fonts start at the baseline */ /* X11 coordinates for fonts start at the baseline */
@ -446,7 +452,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable,
case FONT_TYPE_PANGO: case FONT_TYPE_PANGO:
/* Render the text using Pango */ /* Render the text using Pango */
draw_text_pango(text, strlen(text), draw_text_pango(text, strlen(text),
drawable, x, y, max_width, false); drawable, root_visual_type, x, y, max_width, false);
return; return;
#endif #endif
default: default:

View File

@ -0,0 +1,67 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "libi3.h"
#ifndef STARTS_WITH
#define STARTS_WITH(string, needle) (strncasecmp((string), (needle), strlen((needle))) == 0)
#endif
/*
* Replaces occurrences of the defined placeholders in the format string.
*
*/
char *format_placeholders(char *format, placeholder_t *placeholders, int num) {
if (format == NULL)
return NULL;
/* We have to first iterate over the string to see how much buffer space
* we need to allocate. */
int buffer_len = strlen(format) + 1;
for (char *walk = format; *walk != '\0'; walk++) {
for (int i = 0; i < num; i++) {
if (!STARTS_WITH(walk, placeholders[i].name))
continue;
buffer_len = buffer_len - strlen(placeholders[i].name) + strlen(placeholders[i].value);
walk += strlen(placeholders[i].name) - 1;
break;
}
}
/* Now we can parse the format string. */
char buffer[buffer_len];
char *outwalk = buffer;
for (char *walk = format; *walk != '\0'; walk++) {
if (*walk != '%') {
*(outwalk++) = *walk;
continue;
}
bool matched = false;
for (int i = 0; i < num; i++) {
if (!STARTS_WITH(walk, placeholders[i].name)) {
continue;
}
matched = true;
outwalk += sprintf(outwalk, "%s", placeholders[i].value);
walk += strlen(placeholders[i].name) - 1;
break;
}
if (!matched)
*(outwalk++) = *walk;
}
*outwalk = '\0';
return sstrdup(buffer);
}

View File

@ -7,32 +7,77 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include "queue.h"
#include "libi3.h" #include "libi3.h"
struct Colorpixel {
char *hex;
uint32_t pixel;
SLIST_ENTRY(Colorpixel)
colorpixels;
};
SLIST_HEAD(colorpixel_head, Colorpixel)
colorpixels;
/* /*
* Returns the colorpixel to use for the given hex color (think of HTML). Only * Returns the colorpixel to use for the given hex color (think of HTML).
* works for true-color (vast majority of cases) at the moment, avoiding a
* roundtrip to X11.
* *
* The hex_color has to start with #, for example #FF00FF. * The hex_color has to start with #, for example #FF00FF.
* *
* NOTE that get_colorpixel() does _NOT_ check the given color code for validity. * NOTE that get_colorpixel() does _NOT_ check the given color code for validity.
* This has to be done by the caller. * This has to be done by the caller.
* *
* NOTE that this function may in the future rely on a global xcb_connection_t
* variable called 'conn' to be present.
*
*/ */
uint32_t get_colorpixel(const char *hex) { uint32_t get_colorpixel(const char *hex) {
char strgroups[3][3] = {{hex[1], hex[2], '\0'}, char strgroups[3][3] = {
{hex[1], hex[2], '\0'},
{hex[3], hex[4], '\0'}, {hex[3], hex[4], '\0'},
{hex[5], hex[6], '\0'}}; {hex[5], hex[6], '\0'}};
uint8_t r = strtol(strgroups[0], NULL, 16); uint8_t r = strtol(strgroups[0], NULL, 16);
uint8_t g = strtol(strgroups[1], NULL, 16); uint8_t g = strtol(strgroups[1], NULL, 16);
uint8_t b = strtol(strgroups[2], NULL, 16); uint8_t b = strtol(strgroups[2], NULL, 16);
/* We set the first 8 bits high to have 100% opacity in case of a 32 bit /* Shortcut: if our screen is true color, no need to do a roundtrip to X11 */
* color depth visual. */ if (root_screen == NULL || root_screen->root_depth == 24 || root_screen->root_depth == 32) {
return (0xFF << 24) | (r << 16 | g << 8 | b); return (0xFF << 24) | (r << 16 | g << 8 | b);
}
/* Lookup this colorpixel in the cache */
struct Colorpixel *colorpixel;
SLIST_FOREACH(colorpixel, &(colorpixels), colorpixels) {
if (strcmp(colorpixel->hex, hex) == 0)
return colorpixel->pixel;
}
#define RGB_8_TO_16(i) (65535 * ((i)&0xFF) / 255)
int r16 = RGB_8_TO_16(r);
int g16 = RGB_8_TO_16(g);
int b16 = RGB_8_TO_16(b);
xcb_alloc_color_reply_t *reply;
reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, root_screen->default_colormap,
r16, g16, b16),
NULL);
if (!reply) {
LOG("Could not allocate color\n");
exit(1);
}
uint32_t pixel = reply->pixel;
free(reply);
/* Store the result in the cache */
struct Colorpixel *cache_pixel = scalloc(1, sizeof(struct Colorpixel));
cache_pixel->hex = sstrdup(hex);
cache_pixel->pixel = pixel;
SLIST_INSERT_HEAD(&(colorpixels), cache_pixel, colorpixels);
return pixel;
} }

View File

@ -12,8 +12,6 @@
#include "libi3.h" #include "libi3.h"
extern xcb_connection_t *conn;
/* /*
* All-in-one function which returns the modifier mask (XCB_MOD_MASK_*) for the * All-in-one function which returns the modifier mask (XCB_MOD_MASK_*) for the
* given keysymbol, for example for XCB_NUM_LOCK (usually configured to mod2). * given keysymbol, for example for XCB_NUM_LOCK (usually configured to mod2).

View File

@ -31,14 +31,15 @@
char *root_atom_contents(const char *atomname, xcb_connection_t *provided_conn, int screen) { char *root_atom_contents(const char *atomname, xcb_connection_t *provided_conn, int screen) {
xcb_intern_atom_cookie_t atom_cookie; xcb_intern_atom_cookie_t atom_cookie;
xcb_intern_atom_reply_t *atom_reply; xcb_intern_atom_reply_t *atom_reply;
char *content; char *content = NULL;
size_t content_max_words = 256; size_t content_max_words = 256;
xcb_connection_t *conn = provided_conn; xcb_connection_t *conn = provided_conn;
if (provided_conn == NULL && if (provided_conn == NULL &&
((conn = xcb_connect(NULL, &screen)) == NULL || ((conn = xcb_connect(NULL, &screen)) == NULL ||
xcb_connection_has_error(conn))) xcb_connection_has_error(conn))) {
return NULL; return NULL;
}
atom_cookie = xcb_intern_atom(conn, 0, strlen(atomname), atomname); atom_cookie = xcb_intern_atom(conn, 0, strlen(atomname), atomname);
@ -46,8 +47,9 @@ char *root_atom_contents(const char *atomname, xcb_connection_t *provided_conn,
xcb_window_t root = root_screen->root; xcb_window_t root = root_screen->root;
atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL); atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL);
if (atom_reply == NULL) if (atom_reply == NULL) {
return NULL; goto out_conn;
}
xcb_get_property_cookie_t prop_cookie; xcb_get_property_cookie_t prop_cookie;
xcb_get_property_reply_t *prop_reply; xcb_get_property_reply_t *prop_reply;
@ -55,8 +57,7 @@ char *root_atom_contents(const char *atomname, xcb_connection_t *provided_conn,
XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words); XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL); prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
if (prop_reply == NULL) { if (prop_reply == NULL) {
free(atom_reply); goto out_atom;
return NULL;
} }
if (xcb_get_property_value_length(prop_reply) > 0 && prop_reply->bytes_after > 0) { if (xcb_get_property_value_length(prop_reply) > 0 && prop_reply->bytes_after > 0) {
/* We received an incomplete value. Ask again but with a properly /* We received an incomplete value. Ask again but with a properly
@ -68,14 +69,11 @@ char *root_atom_contents(const char *atomname, xcb_connection_t *provided_conn,
XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words); XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL); prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
if (prop_reply == NULL) { if (prop_reply == NULL) {
free(atom_reply); goto out_atom;
return NULL;
} }
} }
if (xcb_get_property_value_length(prop_reply) == 0) { if (xcb_get_property_value_length(prop_reply) == 0) {
free(atom_reply); goto out;
free(prop_reply);
return NULL;
} }
if (prop_reply->type == XCB_ATOM_CARDINAL) { if (prop_reply->type == XCB_ATOM_CARDINAL) {
/* We treat a CARDINAL as a >= 32-bit unsigned int. The only CARDINAL /* We treat a CARDINAL as a >= 32-bit unsigned int. The only CARDINAL
@ -85,9 +83,13 @@ char *root_atom_contents(const char *atomname, xcb_connection_t *provided_conn,
sasprintf(&content, "%.*s", xcb_get_property_value_length(prop_reply), sasprintf(&content, "%.*s", xcb_get_property_value_length(prop_reply),
(char *)xcb_get_property_value(prop_reply)); (char *)xcb_get_property_value(prop_reply));
} }
out:
free(prop_reply);
out_atom:
free(atom_reply);
out_conn:
if (provided_conn == NULL) if (provided_conn == NULL)
xcb_disconnect(conn); xcb_disconnect(conn);
free(atom_reply);
free(prop_reply);
return content; return content;
} }

View File

@ -24,7 +24,7 @@ struct _i3String {
xcb_char2b_t *ucs2; xcb_char2b_t *ucs2;
size_t num_glyphs; size_t num_glyphs;
size_t num_bytes; size_t num_bytes;
bool is_markup; bool pango_markup;
}; };
/* /*
@ -52,7 +52,7 @@ i3String *i3string_from_markup(const char *from_markup) {
i3String *str = i3string_from_utf8(from_markup); i3String *str = i3string_from_utf8(from_markup);
/* Set the markup flag */ /* Set the markup flag */
str->is_markup = true; str->pango_markup = true;
return str; return str;
} }
@ -86,7 +86,7 @@ i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_b
i3String *str = i3string_from_utf8_with_length(from_markup, num_bytes); i3String *str = i3string_from_utf8_with_length(from_markup, num_bytes);
/* set the markup flag */ /* set the markup flag */
str->is_markup = true; str->pango_markup = true;
return str; return str;
} }
@ -118,7 +118,7 @@ i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs) {
*/ */
i3String *i3string_copy(i3String *str) { i3String *i3string_copy(i3String *str) {
i3String *copy = i3string_from_utf8(i3string_as_utf8(str)); i3String *copy = i3string_from_utf8(i3string_as_utf8(str));
copy->is_markup = str->is_markup; copy->pango_markup = str->pango_markup;
return copy; return copy;
} }
@ -178,14 +178,14 @@ size_t i3string_get_num_bytes(i3String *str) {
* Whether the given i3String is in Pango markup. * Whether the given i3String is in Pango markup.
*/ */
bool i3string_is_markup(i3String *str) { bool i3string_is_markup(i3String *str) {
return str->is_markup; return str->pango_markup;
} }
/* /*
* Set whether the i3String should use Pango markup. * Set whether the i3String should use Pango markup.
*/ */
void i3string_set_markup(i3String *str, bool is_markup) { void i3string_set_markup(i3String *str, bool pango_markup) {
str->is_markup = is_markup; str->pango_markup = pango_markup;
} }
/* /*

View File

@ -7,7 +7,7 @@ template::[header-declarations]
<refentrytitle>{mantitle}</refentrytitle> <refentrytitle>{mantitle}</refentrytitle>
<manvolnum>{manvolnum}</manvolnum> <manvolnum>{manvolnum}</manvolnum>
<refmiscinfo class="source">i3</refmiscinfo> <refmiscinfo class="source">i3</refmiscinfo>
<refmiscinfo class="version">4.11</refmiscinfo> <refmiscinfo class="version">4.12</refmiscinfo>
<refmiscinfo class="manual">i3 Manual</refmiscinfo> <refmiscinfo class="manual">i3 Manual</refmiscinfo>
</refmeta> </refmeta>
<refnamediv> <refnamediv>

View File

@ -26,7 +26,7 @@ Specify the path to the i3 IPC socket (it should not be necessary to use this
option, i3-input will figure out the path on its own). option, i3-input will figure out the path on its own).
-F <format>:: -F <format>::
Every occurence of "%s" in the <format> string is replaced by the user input, Every occurrence of "%s" in the <format> string is replaced by the user input,
and the result is sent to i3 as a command. Default value is "%s". and the result is sent to i3 as a command. Default value is "%s".
-l <limit>:: -l <limit>::

View File

@ -89,7 +89,7 @@ i3-msg -t get_tree
=== I3SOCK === I3SOCK
If no ipc-socket is specified on the commandline, this variable is used If no ipc-socket is specified on the commandline, this variable is used
to determine the path, at wich the unix domain socket is expected, on which to determine the path, at which the unix domain socket is expected, on which
to connect to i3. to connect to i3.
== SEE ALSO == SEE ALSO

View File

@ -44,7 +44,7 @@ after modifying the configuration file.
------------------------------------------------ ------------------------------------------------
i3-nagbar -m 'You have an error in your i3 config file!' \ i3-nagbar -m 'You have an error in your i3 config file!' \
-b 'edit config' 'xterm $EDITOR ~/.i3/config' -b 'edit config' 'i3-sensible-editor ~/.config/i3/config'
------------------------------------------------ ------------------------------------------------
== SEE ALSO == SEE ALSO

View File

@ -25,6 +25,7 @@ It tries to start one of the following (in that order):
* x-terminal-emulator (only present on Debian and derivatives) * x-terminal-emulator (only present on Debian and derivatives)
* urxvt * urxvt
* rxvt * rxvt
* termit
* terminator * terminator
* Eterm * Eterm
* aterm * aterm
@ -32,6 +33,11 @@ It tries to start one of the following (in that order):
* gnome-terminal * gnome-terminal
* roxterm * roxterm
* xfce4-terminal * xfce4-terminal
* termite
* lxterminal
* mate-terminal
* terminology
* st
Please dont complain about the order: If the user has any preference, they will Please dont complain about the order: If the user has any preference, they will
have $TERMINAL set or modified their i3 configuration file. have $TERMINAL set or modified their i3 configuration file.

View File

@ -77,6 +77,12 @@ i3 keeps your layout in a tree data structure.
Window:: Window::
An X11 window, like the Firefox browser window or a terminal emulator. An X11 window, like the Firefox browser window or a terminal emulator.
Floating Window::
A window which "floats" on top of other windows. This style is used by i3 to
display X11 windows with type "dialog", such as the "Print" or "Open File"
dialog boxes in many GUI applications. Use of floating windows can be
fine-tuned with the for_window command (see HTML userguide).
Split container:: Split container::
A split container contains multiple other split containers or windows. A split container contains multiple other split containers or windows.
+ +

View File

@ -86,15 +86,15 @@ state BORDER:
border_style = 'normal', 'pixel' border_style = 'normal', 'pixel'
-> BORDER_WIDTH -> BORDER_WIDTH
border_style = 'none', 'toggle' border_style = 'none', 'toggle'
-> call cmd_border($border_style, "0") -> call cmd_border($border_style, 0)
border_style = '1pixel' border_style = '1pixel'
-> call cmd_border($border_style, "1") -> call cmd_border($border_style, 1)
state BORDER_WIDTH: state BORDER_WIDTH:
end end
-> call cmd_border($border_style, "2") -> call cmd_border($border_style, 2)
border_width = word border_width = number
-> call cmd_border($border_style, $border_width) -> call cmd_border($border_style, &border_width)
# layout default|stacked|stacking|tabbed|splitv|splith # layout default|stacked|stacking|tabbed|splitv|splith
# layout toggle [split|all] # layout toggle [split|all]
@ -117,9 +117,11 @@ state APPEND_LAYOUT:
# workspace next|prev|next_on_output|prev_on_output # workspace next|prev|next_on_output|prev_on_output
# workspace back_and_forth # workspace back_and_forth
# workspace <name> # workspace [--no-auto-back-and-forth] <name>
# workspace number <number> # workspace [--no-auto-back-and-forth] number <number>
state WORKSPACE: state WORKSPACE:
no_auto_back_and_forth = '--no-auto-back-and-forth'
->
direction = 'next_on_output', 'prev_on_output', 'next', 'prev' direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
-> call cmd_workspace($direction) -> call cmd_workspace($direction)
'back_and_forth' 'back_and_forth'
@ -127,11 +129,11 @@ state WORKSPACE:
'number' 'number'
-> WORKSPACE_NUMBER -> WORKSPACE_NUMBER
workspace = string workspace = string
-> call cmd_workspace_name($workspace) -> call cmd_workspace_name($workspace, $no_auto_back_and_forth)
state WORKSPACE_NUMBER: state WORKSPACE_NUMBER:
workspace = string workspace = string
-> call cmd_workspace_number($workspace) -> call cmd_workspace_number($workspace, $no_auto_back_and_forth)
# focus left|right|up|down # focus left|right|up|down
# focus output <output> # focus output <output>
@ -189,9 +191,9 @@ state STICKY:
action = 'enable', 'disable', 'toggle' action = 'enable', 'disable', 'toggle'
-> call cmd_sticky($action) -> call cmd_sticky($action)
# split v|h|vertical|horizontal # split v|h|t|vertical|horizontal|toggle
state SPLIT: state SPLIT:
direction = 'horizontal', 'vertical', 'v', 'h' direction = 'horizontal', 'vertical', 'toggle', 'v', 'h', 't'
-> call cmd_split($direction) -> call cmd_split($direction)
# floating enable|disable|toggle # floating enable|disable|toggle
@ -199,12 +201,14 @@ state FLOATING:
floating = 'enable', 'disable', 'toggle' floating = 'enable', 'disable', 'toggle'
-> call cmd_floating($floating) -> call cmd_floating($floating)
# mark [--toggle] <mark> # mark [--add|--replace] [--toggle] <mark>
state MARK: state MARK:
mode = '--add', '--replace'
->
toggle = '--toggle' toggle = '--toggle'
-> ->
mark = string mark = string
-> call cmd_mark($mark, $toggle) -> call cmd_mark($mark, $mode, $toggle)
# unmark [mark] # unmark [mark]
state UNMARK: state UNMARK:
@ -304,6 +308,8 @@ state MOVE:
-> ->
'to' 'to'
-> ->
no_auto_back_and_forth = '--no-auto-back-and-forth'
->
'workspace' 'workspace'
-> MOVE_WORKSPACE -> MOVE_WORKSPACE
'output' 'output'
@ -341,11 +347,11 @@ state MOVE_WORKSPACE:
'number' 'number'
-> MOVE_WORKSPACE_NUMBER -> MOVE_WORKSPACE_NUMBER
workspace = string workspace = string
-> call cmd_move_con_to_workspace_name($workspace) -> call cmd_move_con_to_workspace_name($workspace, $no_auto_back_and_forth)
state MOVE_WORKSPACE_NUMBER: state MOVE_WORKSPACE_NUMBER:
number = string number = string
-> call cmd_move_con_to_workspace_number($number) -> call cmd_move_con_to_workspace_number($number, $no_auto_back_and_forth)
state MOVE_TO_OUTPUT: state MOVE_TO_OUTPUT:
output = string output = string

View File

@ -146,6 +146,8 @@ state ASSIGN:
state ASSIGN_WORKSPACE: state ASSIGN_WORKSPACE:
'→' '→'
-> ->
'workspace'
->
workspace = string workspace = string
-> call cfg_assign($workspace) -> call cfg_assign($workspace)
@ -280,9 +282,15 @@ state COLOR_TEXT:
state COLOR_INDICATOR: state COLOR_INDICATOR:
indicator = word indicator = word
-> call cfg_color($colorclass, $border, $background, $text, $indicator) -> COLOR_CHILD_BORDER
end end
-> call cfg_color($colorclass, $border, $background, $text, NULL) -> call cfg_color($colorclass, $border, $background, $text, NULL, NULL)
state COLOR_CHILD_BORDER:
child_border = word
-> call cfg_color($colorclass, $border, $background, $text, $indicator, $child_border)
end
-> call cfg_color($colorclass, $border, $background, $text, $indicator, NULL)
# <exec|exec_always> [--no-startup-id] command # <exec|exec_always> [--no-startup-id] command
state EXEC: state EXEC:
@ -326,8 +334,10 @@ state BINDCOMMAND:
################################################################################ ################################################################################
state MODENAME: state MODENAME:
pango_markup = '--pango_markup'
->
modename = word modename = word
-> call cfg_enter_mode($modename); MODEBRACE -> call cfg_enter_mode($pango_markup, $modename); MODEBRACE
state MODEBRACE: state MODEBRACE:
end end
@ -443,7 +453,7 @@ state BAR_ID:
-> call cfg_bar_id($bar_id); BAR -> call cfg_bar_id($bar_id); BAR
state BAR_MODIFIER: state BAR_MODIFIER:
modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift' modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift', 'none', 'off'
-> call cfg_bar_modifier($modifier); BAR -> call cfg_bar_modifier($modifier); BAR
state BAR_WHEEL_UP_CMD: state BAR_WHEEL_UP_CMD:
@ -518,7 +528,7 @@ state BAR_COLORS:
end -> end ->
'#' -> BAR_COLORS_IGNORE_LINE '#' -> BAR_COLORS_IGNORE_LINE
'set' -> BAR_COLORS_IGNORE_LINE 'set' -> BAR_COLORS_IGNORE_LINE
colorclass = 'background', 'statusline', 'separator' colorclass = 'background', 'statusline', 'separator', 'focused_background', 'focused_statusline', 'focused_separator'
-> BAR_COLORS_SINGLE -> BAR_COLORS_SINGLE
colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace', 'binding_mode' colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace', 'binding_mode'
-> BAR_COLORS_BORDER -> BAR_COLORS_BORDER

View File

@ -1,14 +1,14 @@
#!/bin/zsh #!/bin/zsh
# This script is used to prepare a new release of i3. # This script is used to prepare a new release of i3.
export RELEASE_VERSION="4.10.4" export RELEASE_VERSION="4.11"
export PREVIOUS_VERSION="4.10.3" export PREVIOUS_VERSION="4.10.4"
export RELEASE_BRANCH="master" export RELEASE_BRANCH="next"
if [ ! -e "../i3.github.io" ] if [ ! -e "../i3.github.io" ]
then then
echo "../i3.github.io does not exist." echo "../i3.github.io does not exist."
echo "Use git clone git://github.com/i3/i3.github.io" echo "Use git clone https://github.com/i3/i3.github.io"
exit 1 exit 1
fi fi
@ -41,7 +41,7 @@ STARTDIR=$PWD
TMPDIR=$(mktemp -d) TMPDIR=$(mktemp -d)
cd $TMPDIR cd $TMPDIR
if ! wget http://i3wm.org/downloads/i3-${PREVIOUS_VERSION}.tar.bz2; then if ! wget https://i3wm.org/downloads/i3-${PREVIOUS_VERSION}.tar.bz2; then
echo "Could not download i3-${PREVIOUS_VERSION}.tar.bz2 (required for comparing files)." echo "Could not download i3-${PREVIOUS_VERSION}.tar.bz2 (required for comparing files)."
exit 1 exit 1
fi fi

View File

@ -27,7 +27,7 @@ const char *DEFAULT_BINDING_MODE = "default";
* the list of modes. * the list of modes.
* *
*/ */
static struct Mode *mode_from_name(const char *name) { static struct Mode *mode_from_name(const char *name, bool pango_markup) {
struct Mode *mode; struct Mode *mode;
/* Try to find the mode in the list of modes and return it */ /* Try to find the mode in the list of modes and return it */
@ -39,6 +39,7 @@ static struct Mode *mode_from_name(const char *name) {
/* If the mode was not found, create a new one */ /* If the mode was not found, create a new one */
mode = scalloc(1, sizeof(struct Mode)); mode = scalloc(1, sizeof(struct Mode));
mode->name = sstrdup(name); mode->name = sstrdup(name);
mode->pango_markup = pango_markup;
mode->bindings = scalloc(1, sizeof(struct bindings_head)); mode->bindings = scalloc(1, sizeof(struct bindings_head));
TAILQ_INIT(mode->bindings); TAILQ_INIT(mode->bindings);
SLIST_INSERT_HEAD(&modes, mode, modes); SLIST_INSERT_HEAD(&modes, mode, modes);
@ -54,7 +55,7 @@ static struct Mode *mode_from_name(const char *name) {
*/ */
Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
const char *release, const char *border, const char *whole_window, const char *release, const char *border, const char *whole_window,
const char *command, const char *modename) { const char *command, const char *modename, bool pango_markup) {
Binding *new_binding = scalloc(1, sizeof(Binding)); Binding *new_binding = scalloc(1, sizeof(Binding));
DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release); DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release);
new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS); new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
@ -91,7 +92,7 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch
if (group_bits_set > 1) if (group_bits_set > 1)
ELOG("Keybinding has more than one Group specified, but your X server is always in precisely one group. The keybinding can never trigger.\n"); ELOG("Keybinding has more than one Group specified, but your X server is always in precisely one group. The keybinding can never trigger.\n");
struct Mode *mode = mode_from_name(modename); struct Mode *mode = mode_from_name(modename, pango_markup);
TAILQ_INSERT_TAIL(mode->bindings, new_binding, bindings); TAILQ_INSERT_TAIL(mode->bindings, new_binding, bindings);
return new_binding; return new_binding;
@ -145,6 +146,27 @@ void grab_all_keys(xcb_connection_t *conn) {
} }
} }
/*
* Release the button grabs on all managed windows and regrab them,
* reevaluating which buttons need to be grabbed.
*
*/
void regrab_all_buttons(xcb_connection_t *conn) {
bool grab_scrollwheel = bindings_should_grab_scrollwheel_buttons();
xcb_grab_server(conn);
Con *con;
TAILQ_FOREACH(con, &all_cons, all_cons) {
if (con->window == NULL)
continue;
xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, con->window->id, XCB_BUTTON_MASK_ANY);
xcb_grab_buttons(conn, con->window->id, grab_scrollwheel);
}
xcb_ungrab_server(conn);
}
/* /*
* Returns a pointer to the Binding with the specified modifiers and * Returns a pointer to the Binding with the specified modifiers and
* keycode or NULL if no such binding exists. * keycode or NULL if no such binding exists.
@ -164,18 +186,28 @@ static Binding *get_binding(i3_event_state_mask_t state_filtered, bool is_releas
} }
} }
const uint32_t xkb_group_state = (state_filtered & 0xFFFF0000);
const uint32_t modifiers_state = (state_filtered & 0x0000FFFF);
TAILQ_FOREACH(bind, bindings, bindings) { TAILQ_FOREACH(bind, bindings, bindings) {
bool state_matches; const uint32_t xkb_group_mask = (bind->event_state_mask & 0xFFFF0000);
if (bind->event_state_mask == 0) { /* modifiers_mask is a special case: a value of 0 does not mean “match all”,
* but rather match exactly when no modifiers are present. */
const uint32_t modifiers_mask = (bind->event_state_mask & 0x0000FFFF);
const bool groups_match = ((xkb_group_state & xkb_group_mask) == xkb_group_mask);
bool mods_match;
if (modifiers_mask == 0) {
/* Verify no modifiers are pressed. A bitwise AND would lead to /* Verify no modifiers are pressed. A bitwise AND would lead to
* false positives, see issue #2002. */ * false positives, see issue #2002. */
state_matches = (state_filtered == 0); mods_match = (modifiers_state == 0);
} else { } else {
state_matches = ((state_filtered & bind->event_state_mask) == bind->event_state_mask); mods_match = ((modifiers_state & modifiers_mask) == modifiers_mask);
} }
const bool state_matches = (groups_match && mods_match);
DLOG("binding with event_state_mask 0x%x, state_filtered 0x%x, match: %s\n", DLOG("binding groups_match = %s, mods_match = %s, state_matches = %s\n",
bind->event_state_mask, state_filtered, (state_matches ? "yes" : "no")); (groups_match ? "yes" : "no"),
(mods_match ? "yes" : "no"),
(state_matches ? "yes" : "no"));
/* First compare the state_filtered (unless this is a /* First compare the state_filtered (unless this is a
* B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease * B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
* event) */ * event) */
@ -327,6 +359,7 @@ void translate_keysyms(void) {
return; return;
} }
bool has_errors = false;
Binding *bind; Binding *bind;
TAILQ_FOREACH(bind, bindings, bindings) { TAILQ_FOREACH(bind, bindings, bindings) {
if (bind->input_type == B_MOUSE) { if (bind->input_type == B_MOUSE) {
@ -397,6 +430,21 @@ void translate_keysyms(void) {
sasprintf(&tmp, "%s %d", keycodes, bind->translated_to[n]); sasprintf(&tmp, "%s %d", keycodes, bind->translated_to[n]);
free(keycodes); free(keycodes);
keycodes = tmp; keycodes = tmp;
/* check for duplicate bindings */
Binding *check;
TAILQ_FOREACH(check, bindings, bindings) {
if (check == bind)
continue;
if (check->symbol != NULL)
continue;
if (check->keycode != bind->translated_to[n] ||
check->event_state_mask != bind->event_state_mask ||
check->release != bind->release)
continue;
has_errors = true;
ELOG("Duplicate keybinding in config file:\n keysym = %s, keycode = %d, state_mask = 0x%x\n", bind->symbol, check->keycode, bind->event_state_mask);
}
} }
DLOG("state=0x%x, cfg=\"%s\", sym=0x%x → keycodes%s (%d)\n", DLOG("state=0x%x, cfg=\"%s\", sym=0x%x → keycodes%s (%d)\n",
bind->event_state_mask, bind->symbol, keysym, keycodes, bind->number_keycodes); bind->event_state_mask, bind->symbol, keysym, keycodes, bind->number_keycodes);
@ -405,6 +453,10 @@ void translate_keysyms(void) {
xkb_state_unref(dummy_state); xkb_state_unref(dummy_state);
xkb_state_unref(dummy_state_no_shift); xkb_state_unref(dummy_state_no_shift);
if (has_errors) {
start_config_error_nagbar(current_configpath, true);
}
} }
/* /*
@ -426,7 +478,8 @@ void switch_mode(const char *new_mode) {
grab_all_keys(conn); grab_all_keys(conn);
char *event_msg; char *event_msg;
sasprintf(&event_msg, "{\"change\":\"%s\"}", mode->name); sasprintf(&event_msg, "{\"change\":\"%s\", \"pango_markup\":%s}",
mode->name, (mode->pango_markup ? "true" : "false"));
ipc_send_event("mode", I3_IPC_EVENT_MODE, event_msg); ipc_send_event("mode", I3_IPC_EVENT_MODE, event_msg);
FREE(event_msg); FREE(event_msg);
@ -523,8 +576,6 @@ void check_for_duplicate_bindings(struct context *context) {
/* Check if one is using keysym while the other is using bindsym. /* Check if one is using keysym while the other is using bindsym.
* If so, skip. */ * If so, skip. */
/* XXX: It should be checked at a later place (when translating the
* keysym to keycodes) if there are any duplicates */
if ((bind->symbol == NULL && current->symbol != NULL) || if ((bind->symbol == NULL && current->symbol != NULL) ||
(bind->symbol != NULL && current->symbol == NULL)) (bind->symbol != NULL && current->symbol == NULL))
continue; continue;
@ -588,8 +639,8 @@ void binding_free(Binding *bind) {
/* /*
* Runs the given binding and handles parse errors. If con is passed, it will * Runs the given binding and handles parse errors. If con is passed, it will
* execute the command binding with that container selected by criteria. * execute the command binding with that container selected by criteria.
* Returns a CommandResult for running the binding's command. Caller should * Returns a CommandResult for running the binding's command. Free with
* render tree if needs_tree_render is true. Free with command_result_free(). * command_result_free().
* *
*/ */
CommandResult *run_binding(Binding *bind, Con *con) { CommandResult *run_binding(Binding *bind, Con *con) {
@ -758,3 +809,33 @@ bool load_keymap(void) {
return true; return true;
} }
/*
* Returns true if the current config has any binding to a scroll wheel button
* (4 or 5) which is a whole-window binding.
* We need this to figure out whether we should grab all buttons or just 1-3
* when managing a window. See #2049.
*
*/
bool bindings_should_grab_scrollwheel_buttons(void) {
Binding *bind;
TAILQ_FOREACH(bind, bindings, bindings) {
/* We are only interested in whole window mouse bindings. */
if (bind->input_type != B_MOUSE || !bind->whole_window)
continue;
char *endptr;
long button = strtol(bind->symbol + (sizeof("button") - 1), &endptr, 10);
if (button == LONG_MAX || button == LONG_MIN || button < 0 || *endptr != '\0' || endptr == bind->symbol) {
ELOG("Could not parse button number, skipping this binding. Please report this bug in i3.\n");
continue;
}
/* If the binding is for either scrollwheel button, we need to grab everything. */
if (button == 4 || button == 5) {
return true;
}
}
return false;
}

View File

@ -198,11 +198,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time); xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time);
xcb_flush(conn); xcb_flush(conn);
if (result->needs_tree_render)
tree_render();
command_result_free(result); command_result_free(result);
return 0; return 0;
} }
} }
@ -356,13 +352,24 @@ int handle_button_press(xcb_button_press_event_t *event) {
last_timestamp = event->time; last_timestamp = event->time;
const uint32_t mod = config.floating_modifier; const uint32_t mod = (config.floating_modifier & 0xFFFF);
const bool mod_pressed = (mod != 0 && (event->state & mod) == mod); const bool mod_pressed = (mod != 0 && (event->state & mod) == mod);
DLOG("floating_mod = %d, detail = %d\n", mod_pressed, event->detail); DLOG("floating_mod = %d, detail = %d\n", mod_pressed, event->detail);
if ((con = con_by_window_id(event->event))) if ((con = con_by_window_id(event->event)))
return route_click(con, event, mod_pressed, CLICK_INSIDE); return route_click(con, event, mod_pressed, CLICK_INSIDE);
if (!(con = con_by_frame_id(event->event))) { if (!(con = con_by_frame_id(event->event))) {
/* Run bindings on the root window as well, see #2097. We only run it
* if --whole-window was set as that's the equivalent for a normal
* window. */
if (event->event == root) {
Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event);
if (bind != NULL && bind->whole_window) {
CommandResult *result = run_binding(bind, NULL);
command_result_free(result);
}
}
/* If the root window is clicked, find the relevant output from the /* If the root window is clicked, find the relevant output from the
* click coordinates and focus the output's active workspace. */ * click coordinates and focus the output's active workspace. */
if (event->event == root && event->response_type == XCB_BUTTON_PRESS) { if (event->event == root && event->response_type == XCB_BUTTON_PRESS) {

View File

@ -12,6 +12,10 @@
#include <float.h> #include <float.h>
#include <stdarg.h> #include <stdarg.h>
#ifdef I3_ASAN_ENABLED
#include <sanitizer/lsan_interface.h>
#endif
#include "all.h" #include "all.h"
#include "shmlog.h" #include "shmlog.h"
@ -42,6 +46,16 @@
} \ } \
} while (0) } while (0)
/** If an error occured during parsing of the criteria, we want to exit instead
* of relying on fallback behavior. See #2091. */
#define HANDLE_INVALID_MATCH \
do { \
if (current_match->error != NULL) { \
yerror("Invalid match: %s", current_match->error); \
return; \
} \
} while (0)
/** When the command did not include match criteria (!), we use the currently /** When the command did not include match criteria (!), we use the currently
* focused container. Do not confuse this case with a command which included * focused container. Do not confuse this case with a command which included
* criteria but which did not match any windows. This macro has to be called in * criteria but which did not match any windows. This macro has to be called in
@ -49,7 +63,14 @@
*/ */
#define HANDLE_EMPTY_MATCH \ #define HANDLE_EMPTY_MATCH \
do { \ do { \
HANDLE_INVALID_MATCH; \
\
if (match_is_empty(current_match)) { \ if (match_is_empty(current_match)) { \
while (!TAILQ_EMPTY(&owindows)) { \
owindow *ow = TAILQ_FIRST(&owindows); \
TAILQ_REMOVE(&owindows, ow, owindows); \
free(ow); \
} \
owindow *ow = smalloc(sizeof(owindow)); \ owindow *ow = smalloc(sizeof(owindow)); \
ow->con = focused; \ ow->con = focused; \
TAILQ_INIT(&owindows); \ TAILQ_INIT(&owindows); \
@ -121,102 +142,6 @@ static Con *maybe_auto_back_and_forth_workspace(Con *workspace) {
return workspace; return workspace;
} }
// This code is commented out because we might recycle it for popping up error
// messages on parser errors.
#if 0
static pid_t migration_pid = -1;
/*
* Handler which will be called when we get a SIGCHLD for the nagbar, meaning
* it exited (or could not be started, depending on the exit code).
*
*/
static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
ev_child_stop(EV_A_ watcher);
if (!WIFEXITED(watcher->rstatus)) {
fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
return;
}
int exitcode = WEXITSTATUS(watcher->rstatus);
printf("i3-nagbar process exited with status %d\n", exitcode);
if (exitcode == 2) {
fprintf(stderr, "ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
}
migration_pid = -1;
}
/* We need ev >= 4 for the following code. Since it is not *that* important (it
* only makes sure that there are no i3-nagbar instances left behind) we still
* support old systems with libev 3. */
#if EV_VERSION_MAJOR >= 4
/*
* Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
* SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
*
*/
static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
if (migration_pid != -1) {
LOG("Sending SIGKILL (9) to i3-nagbar with PID %d\n", migration_pid);
kill(migration_pid, SIGKILL);
}
}
#endif
void cmd_MIGRATION_start_nagbar(void) {
if (migration_pid != -1) {
fprintf(stderr, "i3-nagbar already running.\n");
return;
}
fprintf(stderr, "Starting i3-nagbar, command parsing differs from expected output.\n");
ELOG("Please report this on IRC or in the bugtracker. Make sure to include the full debug level logfile:\n");
ELOG("i3-dump-log | gzip -9c > /tmp/i3.log.gz\n");
ELOG("FYI: Your i3 version is " I3_VERSION "\n");
migration_pid = fork();
if (migration_pid == -1) {
warn("Could not fork()");
return;
}
/* child */
if (migration_pid == 0) {
char *pageraction;
sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename);
char *argv[] = {
NULL, /* will be replaced by the executable path */
"-t",
"error",
"-m",
"You found a parsing error. Please, please, please, report it!",
"-b",
"show errors",
pageraction,
NULL
};
exec_i3_utility("i3-nagbar", argv);
}
/* parent */
/* install a child watcher */
ev_child *child = smalloc(sizeof(ev_child));
ev_child_init(child, &nagbar_exited, migration_pid, 0);
ev_child_start(main_loop, child);
/* We need ev >= 4 for the following code. Since it is not *that* important (it
* only makes sure that there are no i3-nagbar instances left behind) we still
* support old systems with libev 3. */
#if EV_VERSION_MAJOR >= 4
/* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
* still running) */
ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
ev_cleanup_init(cleanup, nagbar_cleanup);
ev_cleanup_start(main_loop, cleanup);
#endif
}
#endif
/******************************************************************************* /*******************************************************************************
* Criteria functions. * Criteria functions.
******************************************************************************/ ******************************************************************************/
@ -282,26 +207,60 @@ void cmd_criteria_match_windows(I3_CMD) {
next = TAILQ_NEXT(next, owindows); next = TAILQ_NEXT(next, owindows);
DLOG("checking if con %p / %s matches\n", current->con, current->con->name); DLOG("checking if con %p / %s matches\n", current->con, current->con->name);
/* We use this flag to prevent matching on window-less containers if
* only window-specific criteria were specified. */
bool accept_match = false;
if (current_match->con_id != NULL) { if (current_match->con_id != NULL) {
accept_match = true;
if (current_match->con_id == current->con) { if (current_match->con_id == current->con) {
DLOG("matches container!\n"); DLOG("con_id matched.\n");
TAILQ_INSERT_TAIL(&owindows, current, owindows);
} else { } else {
DLOG("doesnt match\n"); DLOG("con_id does not match.\n");
free(current); FREE(current);
continue;
} }
} else if (current_match->mark != NULL && current->con->mark != NULL && }
regex_matches(current_match->mark, current->con->mark)) {
if (current_match->mark != NULL && !TAILQ_EMPTY(&(current->con->marks_head))) {
accept_match = true;
bool matched_by_mark = false;
mark_t *mark;
TAILQ_FOREACH(mark, &(current->con->marks_head), marks) {
if (!regex_matches(current_match->mark, mark->name))
continue;
DLOG("match by mark\n"); DLOG("match by mark\n");
TAILQ_INSERT_TAIL(&owindows, current, owindows); matched_by_mark = true;
} else { break;
if (current->con->window && match_matches_window(current_match, current->con->window)) {
DLOG("matches window!\n");
TAILQ_INSERT_TAIL(&owindows, current, owindows);
} else {
DLOG("doesnt match\n");
free(current);
} }
if (!matched_by_mark) {
DLOG("mark does not match.\n");
FREE(current);
continue;
}
}
if (current->con->window != NULL) {
if (match_matches_window(current_match, current->con->window)) {
DLOG("matches window!\n");
accept_match = true;
} else {
DLOG("doesn't match\n");
FREE(current);
continue;
}
}
if (accept_match) {
TAILQ_INSERT_TAIL(&owindows, current, owindows);
} else {
FREE(current);
continue;
} }
} }
@ -397,16 +356,17 @@ void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
} }
/* /*
* Implementation of 'move [window|container] [to] workspace <name>'. * Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace <name>'.
* *
*/ */
void cmd_move_con_to_workspace_name(I3_CMD, const char *name) { void cmd_move_con_to_workspace_name(I3_CMD, const char *name, const char *_no_auto_back_and_forth) {
if (strncasecmp(name, "__", strlen("__")) == 0) { if (strncasecmp(name, "__", strlen("__")) == 0) {
LOG("You cannot move containers to i3-internal workspaces (\"%s\").\n", name); LOG("You cannot move containers to i3-internal workspaces (\"%s\").\n", name);
ysuccess(false); ysuccess(false);
return; return;
} }
const bool no_auto_back_and_forth = (_no_auto_back_and_forth != NULL);
owindow *current; owindow *current;
/* We have nothing to move: /* We have nothing to move:
@ -426,6 +386,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, const char *name) {
/* get the workspace */ /* get the workspace */
Con *ws = workspace_get(name, NULL); Con *ws = workspace_get(name, NULL);
if (!no_auto_back_and_forth)
ws = maybe_auto_back_and_forth_workspace(ws); ws = maybe_auto_back_and_forth_workspace(ws);
HANDLE_EMPTY_MATCH; HANDLE_EMPTY_MATCH;
@ -441,10 +402,11 @@ void cmd_move_con_to_workspace_name(I3_CMD, const char *name) {
} }
/* /*
* Implementation of 'move [window|container] [to] workspace number <name>'. * Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace number <name>'.
* *
*/ */
void cmd_move_con_to_workspace_number(I3_CMD, const char *which) { void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *_no_auto_back_and_forth) {
const bool no_auto_back_and_forth = (_no_auto_back_and_forth != NULL);
owindow *current; owindow *current;
/* We have nothing to move: /* We have nothing to move:
@ -477,6 +439,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, const char *which) {
workspace = workspace_get(which, NULL); workspace = workspace_get(which, NULL);
} }
if (!no_auto_back_and_forth)
workspace = maybe_auto_back_and_forth_workspace(workspace); workspace = maybe_auto_back_and_forth_workspace(workspace);
HANDLE_EMPTY_MATCH; HANDLE_EMPTY_MATCH;
@ -609,7 +572,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, const char *way
while (current->type != CT_WORKSPACE && while (current->type != CT_WORKSPACE &&
current->type != CT_FLOATING_CON && current->type != CT_FLOATING_CON &&
con_orientation(current->parent) != search_orientation) (con_orientation(current->parent) != search_orientation || con_num_children(current->parent) == 1))
current = current->parent; current = current->parent;
/* get the default percentage */ /* get the default percentage */
@ -753,8 +716,8 @@ void cmd_resize_set(I3_CMD, long cwidth, long cheight) {
* Implementation of 'border normal|pixel [<n>]', 'border none|1pixel|toggle'. * Implementation of 'border normal|pixel [<n>]', 'border none|1pixel|toggle'.
* *
*/ */
void cmd_border(I3_CMD, const char *border_style_str, const char *border_width) { void cmd_border(I3_CMD, const char *border_style_str, long border_width) {
DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width); DLOG("border style should be changed to %s with border width %ld\n", border_style_str, border_width);
owindow *current; owindow *current;
HANDLE_EMPTY_MATCH; HANDLE_EMPTY_MATCH;
@ -762,39 +725,35 @@ void cmd_border(I3_CMD, const char *border_style_str, const char *border_width)
TAILQ_FOREACH(current, &owindows, owindows) { TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name); DLOG("matching: %p / %s\n", current->con, current->con->name);
int border_style = current->con->border_style; int border_style = current->con->border_style;
char *end; int con_border_width = border_width;
int tmp_border_width = -1;
tmp_border_width = strtol(border_width, &end, 10);
if (end == border_width) {
/* no valid digits found */
tmp_border_width = -1;
}
if (strcmp(border_style_str, "toggle") == 0) { if (strcmp(border_style_str, "toggle") == 0) {
border_style++; border_style++;
border_style %= 3; border_style %= 3;
if (border_style == BS_NORMAL) if (border_style == BS_NORMAL)
tmp_border_width = 2; con_border_width = 2;
else if (border_style == BS_NONE) else if (border_style == BS_NONE)
tmp_border_width = 0; con_border_width = 0;
else if (border_style == BS_PIXEL) else if (border_style == BS_PIXEL)
tmp_border_width = 1; con_border_width = 1;
} else { } else {
if (strcmp(border_style_str, "normal") == 0) if (strcmp(border_style_str, "normal") == 0) {
border_style = BS_NORMAL; border_style = BS_NORMAL;
else if (strcmp(border_style_str, "pixel") == 0) } else if (strcmp(border_style_str, "pixel") == 0) {
border_style = BS_PIXEL; border_style = BS_PIXEL;
else if (strcmp(border_style_str, "1pixel") == 0) { } else if (strcmp(border_style_str, "1pixel") == 0) {
border_style = BS_PIXEL; border_style = BS_PIXEL;
tmp_border_width = 1; con_border_width = 1;
} else if (strcmp(border_style_str, "none") == 0) } else if (strcmp(border_style_str, "none") == 0) {
border_style = BS_NONE; border_style = BS_NONE;
else { } else {
ELOG("BUG: called with border_style=%s\n", border_style_str); ELOG("BUG: called with border_style=%s\n", border_style_str);
ysuccess(false); ysuccess(false);
return; return;
} }
} }
con_set_border_style(current->con, border_style, tmp_border_width);
con_set_border_style(current->con, border_style, logical_px(con_border_width));
} }
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
@ -911,10 +870,11 @@ void cmd_workspace(I3_CMD, const char *which) {
} }
/* /*
* Implementation of 'workspace number <name>' * Implementation of 'workspace [--no-auto-back-and-forth] number <name>'
* *
*/ */
void cmd_workspace_number(I3_CMD, const char *which) { void cmd_workspace_number(I3_CMD, const char *which, const char *_no_auto_back_and_forth) {
const bool no_auto_back_and_forth = (_no_auto_back_and_forth != NULL);
Con *output, *workspace = NULL; Con *output, *workspace = NULL;
if (con_get_fullscreen_con(croot, CF_GLOBAL)) { if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
@ -942,7 +902,7 @@ void cmd_workspace_number(I3_CMD, const char *which) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
return; return;
} }
if (maybe_back_and_forth(cmd_output, workspace->name)) if (!no_auto_back_and_forth && maybe_back_and_forth(cmd_output, workspace->name))
return; return;
workspace_show(workspace); workspace_show(workspace);
@ -970,10 +930,12 @@ void cmd_workspace_back_and_forth(I3_CMD) {
} }
/* /*
* Implementation of 'workspace <name>' * Implementation of 'workspace [--no-auto-back-and-forth] <name>'
* *
*/ */
void cmd_workspace_name(I3_CMD, const char *name) { void cmd_workspace_name(I3_CMD, const char *name, const char *_no_auto_back_and_forth) {
const bool no_auto_back_and_forth = (_no_auto_back_and_forth != NULL);
if (strncasecmp(name, "__", strlen("__")) == 0) { if (strncasecmp(name, "__", strlen("__")) == 0) {
LOG("You cannot switch to the i3-internal workspaces (\"%s\").\n", name); LOG("You cannot switch to the i3-internal workspaces (\"%s\").\n", name);
ysuccess(false); ysuccess(false);
@ -987,7 +949,7 @@ void cmd_workspace_name(I3_CMD, const char *name) {
} }
DLOG("should switch to workspace %s\n", name); DLOG("should switch to workspace %s\n", name);
if (maybe_back_and_forth(cmd_output, name)) if (!no_auto_back_and_forth && maybe_back_and_forth(cmd_output, name))
return; return;
workspace_show_by_name(name); workspace_show_by_name(name);
@ -997,10 +959,10 @@ void cmd_workspace_name(I3_CMD, const char *name) {
} }
/* /*
* Implementation of 'mark [--toggle] <mark>' * Implementation of 'mark [--add|--replace] [--toggle] <mark>'
* *
*/ */
void cmd_mark(I3_CMD, const char *mark, const char *toggle) { void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle) {
HANDLE_EMPTY_MATCH; HANDLE_EMPTY_MATCH;
owindow *current = TAILQ_FIRST(&owindows); owindow *current = TAILQ_FIRST(&owindows);
@ -1016,10 +978,12 @@ void cmd_mark(I3_CMD, const char *mark, const char *toggle) {
} }
DLOG("matching: %p / %s\n", current->con, current->con->name); DLOG("matching: %p / %s\n", current->con, current->con->name);
mark_mode_t mark_mode = (mode == NULL || strcmp(mode, "--replace") == 0) ? MM_REPLACE : MM_ADD;
if (toggle != NULL) { if (toggle != NULL) {
con_mark_toggle(current->con, mark); con_mark_toggle(current->con, mark, mark_mode);
} else { } else {
con_mark(current->con, mark); con_mark(current->con, mark, mark_mode);
} }
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
@ -1032,7 +996,14 @@ void cmd_mark(I3_CMD, const char *mark, const char *toggle) {
* *
*/ */
void cmd_unmark(I3_CMD, const char *mark) { void cmd_unmark(I3_CMD, const char *mark) {
con_unmark(mark); if (match_is_empty(current_match)) {
con_unmark(NULL, mark);
} else {
owindow *current;
TAILQ_FOREACH(current, &owindows, owindows) {
con_unmark(current->con, mark);
}
}
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
@ -1166,7 +1137,7 @@ void cmd_move_workspace_to_output(I3_CMD, const char *name) {
} }
/* /*
* Implementation of 'split v|h|vertical|horizontal'. * Implementation of 'split v|h|t|vertical|horizontal|toggle'.
* *
*/ */
void cmd_split(I3_CMD, const char *direction) { void cmd_split(I3_CMD, const char *direction) {
@ -1180,6 +1151,24 @@ void cmd_split(I3_CMD, const char *direction) {
continue; continue;
} }
DLOG("matching: %p / %s\n", current->con, current->con->name);
if (direction[0] == 't') {
layout_t current_layout;
if (current->con->type == CT_WORKSPACE) {
current_layout = current->con->layout;
} else {
current_layout = current->con->parent->layout;
}
/* toggling split orientation */
if (current_layout == L_SPLITH) {
tree_split(current->con, VERT);
} else {
tree_split(current->con, HORIZ);
}
} else {
tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
}
DLOG("matching: %p / %s\n", current->con, current->con->name); DLOG("matching: %p / %s\n", current->con, current->con->name);
tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ)); tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
} }
@ -1196,7 +1185,6 @@ void cmd_split(I3_CMD, const char *direction) {
void cmd_kill(I3_CMD, const char *kill_mode_str) { void cmd_kill(I3_CMD, const char *kill_mode_str) {
if (kill_mode_str == NULL) if (kill_mode_str == NULL)
kill_mode_str = "window"; kill_mode_str = "window";
owindow *current;
DLOG("kill_mode=%s\n", kill_mode_str); DLOG("kill_mode=%s\n", kill_mode_str);
@ -1211,14 +1199,11 @@ void cmd_kill(I3_CMD, const char *kill_mode_str) {
return; return;
} }
/* check if the match is empty, not if the result is empty */ HANDLE_EMPTY_MATCH;
if (match_is_empty(current_match))
tree_close_con(kill_mode); owindow *current;
else {
TAILQ_FOREACH(current, &owindows, owindows) { TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name); con_close(current->con, kill_mode);
tree_close(current->con, kill_mode, false, false);
}
} }
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
@ -1460,6 +1445,8 @@ void cmd_sticky(I3_CMD, const char *action) {
* sure it gets pushed to the front now. */ * sure it gets pushed to the front now. */
output_push_sticky_windows(focused); output_push_sticky_windows(focused);
ewmh_update_wm_desktop();
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
ysuccess(true); ysuccess(true);
} }
@ -1580,6 +1567,9 @@ void cmd_layout_toggle(I3_CMD, const char *toggle_mode) {
*/ */
void cmd_exit(I3_CMD) { void cmd_exit(I3_CMD) {
LOG("Exiting due to user command.\n"); LOG("Exiting due to user command.\n");
#ifdef I3_ASAN_ENABLED
__lsan_do_leak_check();
#endif
ipc_shutdown(); ipc_shutdown();
unlink(config.ipc_socket_path); unlink(config.ipc_socket_path);
xcb_disconnect(conn); xcb_disconnect(conn);
@ -1738,28 +1728,42 @@ void cmd_move_window_to_position(I3_CMD, const char *method, long x, long y) {
* *
*/ */
void cmd_move_window_to_center(I3_CMD, const char *method) { void cmd_move_window_to_center(I3_CMD, const char *method) {
if (!con_is_floating(focused)) { bool has_error = false;
ELOG("Cannot change position. The window/container is not floating\n"); HANDLE_EMPTY_MATCH;
yerror("Cannot change position. The window/container is not floating.");
return; owindow *current;
TAILQ_FOREACH(current, &owindows, owindows) {
Con *floating_con = con_inside_floating(current->con);
if (floating_con == NULL) {
ELOG("con %p / %s is not floating, cannot move it to the center.\n",
current->con, current->con->name);
if (!has_error) {
yerror("Cannot change position of a window/container because it is not floating.");
has_error = true;
}
continue;
} }
if (strcmp(method, "absolute") == 0) { if (strcmp(method, "absolute") == 0) {
DLOG("moving to absolute center\n"); DLOG("moving to absolute center\n");
floating_center(focused->parent, croot->rect); floating_center(floating_con, croot->rect);
floating_maybe_reassign_ws(focused->parent); floating_maybe_reassign_ws(floating_con);
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
} }
if (strcmp(method, "position") == 0) { if (strcmp(method, "position") == 0) {
DLOG("moving to center\n"); DLOG("moving to center\n");
floating_center(focused->parent, con_get_workspace(focused)->rect); floating_center(floating_con, con_get_workspace(floating_con)->rect);
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
} }
}
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
if (!has_error)
ysuccess(true); ysuccess(true);
} }
@ -1839,27 +1843,33 @@ void cmd_title_format(I3_CMD, const char *format) {
owindow *current; owindow *current;
TAILQ_FOREACH(current, &owindows, owindows) { TAILQ_FOREACH(current, &owindows, owindows) {
if (current->con->window == NULL)
continue;
DLOG("setting title_format for %p / %s\n", current->con, current->con->name); DLOG("setting title_format for %p / %s\n", current->con, current->con->name);
FREE(current->con->window->title_format); FREE(current->con->title_format);
/* If we only display the title without anything else, we can skip the parsing step, /* If we only display the title without anything else, we can skip the parsing step,
* so we remove the title format altogether. */ * so we remove the title format altogether. */
if (strcasecmp(format, "%title") != 0) { if (strcasecmp(format, "%title") != 0) {
current->con->window->title_format = sstrdup(format); current->con->title_format = sstrdup(format);
i3String *formatted_title = window_parse_title_format(current->con->window); if (current->con->window != NULL) {
i3String *formatted_title = con_parse_title_format(current->con);
ewmh_update_visible_name(current->con->window->id, i3string_as_utf8(formatted_title)); ewmh_update_visible_name(current->con->window->id, i3string_as_utf8(formatted_title));
I3STRING_FREE(formatted_title); I3STRING_FREE(formatted_title);
}
} else { } else {
if (current->con->window != NULL) {
/* We can remove _NET_WM_VISIBLE_NAME since we don't display a custom title. */ /* We can remove _NET_WM_VISIBLE_NAME since we don't display a custom title. */
ewmh_update_visible_name(current->con->window->id, NULL); ewmh_update_visible_name(current->con->window->id, NULL);
} }
}
if (current->con->window != NULL) {
/* Make sure the window title is redrawn immediately. */ /* Make sure the window title is redrawn immediately. */
current->con->window->name_x_changed = true; current->con->window->name_x_changed = true;
} else {
/* For windowless containers we also need to force the redrawing. */
FREE(current->con->deco_render_params);
}
} }
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
@ -1902,12 +1912,16 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) {
GREP_FIRST(check_dest, output_get_content(output), GREP_FIRST(check_dest, output_get_content(output),
!strcasecmp(child->name, new_name)); !strcasecmp(child->name, new_name));
if (check_dest != NULL) { /* If check_dest == workspace, the user might be changing the case of the
* workspace, or it might just be a no-op. */
if (check_dest != NULL && check_dest != workspace) {
yerror("New workspace \"%s\" already exists", new_name); yerror("New workspace \"%s\" already exists", new_name);
return; return;
} }
/* Change the name and try to parse it as a number. */ /* Change the name and try to parse it as a number. */
/* old_name might refer to workspace->name, so copy it before free()ing */
char *old_name_copy = sstrdup(old_name);
FREE(workspace->name); FREE(workspace->name);
workspace->name = sstrdup(new_name); workspace->name = sstrdup(new_name);
@ -1948,7 +1962,8 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) {
ewmh_update_desktop_viewport(); ewmh_update_desktop_viewport();
ewmh_update_current_desktop(); ewmh_update_current_desktop();
startup_sequence_rename_workspace(old_name, new_name); startup_sequence_rename_workspace(old_name_copy, new_name);
free(old_name_copy);
} }
/* /*

197
src/con.c
View File

@ -47,7 +47,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) {
new->depth = window->depth; new->depth = window->depth;
new->window->aspect_ratio = 0.0; new->window->aspect_ratio = 0.0;
} else { } else {
new->depth = XCB_COPY_FROM_PARENT; new->depth = root_depth;
} }
DLOG("opening window\n"); DLOG("opening window\n");
@ -55,6 +55,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) {
TAILQ_INIT(&(new->nodes_head)); TAILQ_INIT(&(new->nodes_head));
TAILQ_INIT(&(new->focus_head)); TAILQ_INIT(&(new->focus_head));
TAILQ_INIT(&(new->swallow_head)); TAILQ_INIT(&(new->swallow_head));
TAILQ_INIT(&(new->marks_head));
if (parent != NULL) if (parent != NULL)
con_attach(new, parent, false); con_attach(new, parent, false);
@ -219,6 +220,36 @@ void con_focus(Con *con) {
} }
} }
/*
* Closes the given container.
*
*/
void con_close(Con *con, kill_window_t kill_window) {
assert(con != NULL);
DLOG("Closing con = %p.\n", con);
/* We never close output or root containers. */
if (con->type == CT_OUTPUT || con->type == CT_ROOT) {
DLOG("con = %p is of type %d, not closing anything.\n", con, con->type);
return;
}
if (con->type == CT_WORKSPACE) {
DLOG("con = %p is a workspace, closing all children instead.\n", con);
Con *child, *nextchild;
for (child = TAILQ_FIRST(&(con->focus_head)); child;) {
nextchild = TAILQ_NEXT(child, focused);
DLOG("killing child = %p.\n", child);
tree_close_internal(child, kill_window, false, false);
child = nextchild;
}
return;
}
tree_close_internal(con, kill_window, false, false);
}
/* /*
* Returns true when this node is a leaf node (has no children) * Returns true when this node is a leaf node (has no children)
* *
@ -513,7 +544,7 @@ Con *con_by_window_id(xcb_window_t window) {
Con *con_by_frame_id(xcb_window_t frame) { Con *con_by_frame_id(xcb_window_t frame) {
Con *con; Con *con;
TAILQ_FOREACH(con, &all_cons, all_cons) TAILQ_FOREACH(con, &all_cons, all_cons)
if (con->frame == frame) if (con->frame.id == frame)
return con; return con;
return NULL; return NULL;
} }
@ -526,27 +557,41 @@ Con *con_by_frame_id(xcb_window_t frame) {
Con *con_by_mark(const char *mark) { Con *con_by_mark(const char *mark) {
Con *con; Con *con;
TAILQ_FOREACH(con, &all_cons, all_cons) { TAILQ_FOREACH(con, &all_cons, all_cons) {
if (con->mark != NULL && strcmp(con->mark, mark) == 0) if (con_has_mark(con, mark))
return con; return con;
} }
return NULL; return NULL;
} }
/*
* Returns true if and only if the given containers holds the mark.
*
*/
bool con_has_mark(Con *con, const char *mark) {
mark_t *current;
TAILQ_FOREACH(current, &(con->marks_head), marks) {
if (strcmp(current->name, mark) == 0)
return true;
}
return false;
}
/* /*
* Toggles the mark on a container. * Toggles the mark on a container.
* If the container already has this mark, the mark is removed. * If the container already has this mark, the mark is removed.
* Otherwise, the mark is assigned to the container. * Otherwise, the mark is assigned to the container.
* *
*/ */
void con_mark_toggle(Con *con, const char *mark) { void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode) {
assert(con != NULL); assert(con != NULL);
DLOG("Toggling mark \"%s\" on con = %p.\n", mark, con); DLOG("Toggling mark \"%s\" on con = %p.\n", mark, con);
if (con->mark != NULL && strcmp(con->mark, mark) == 0) { if (con_has_mark(con, mark)) {
con_unmark(mark); con_unmark(con, mark);
} else { } else {
con_mark(con, mark); con_mark(con, mark, mode);
} }
} }
@ -554,55 +599,77 @@ void con_mark_toggle(Con *con, const char *mark) {
* Assigns a mark to the container. * Assigns a mark to the container.
* *
*/ */
void con_mark(Con *con, const char *mark) { void con_mark(Con *con, const char *mark, mark_mode_t mode) {
assert(con != NULL); assert(con != NULL);
DLOG("Setting mark \"%s\" on con = %p.\n", mark, con); DLOG("Setting mark \"%s\" on con = %p.\n", mark, con);
FREE(con->mark); con_unmark(NULL, mark);
con->mark = sstrdup(mark); if (mode == MM_REPLACE) {
DLOG("Removing all existing marks on con = %p.\n", con);
mark_t *current;
while (!TAILQ_EMPTY(&(con->marks_head))) {
current = TAILQ_FIRST(&(con->marks_head));
con_unmark(con, current->name);
}
}
mark_t *new = scalloc(1, sizeof(mark_t));
new->name = sstrdup(mark);
TAILQ_INSERT_TAIL(&(con->marks_head), new, marks);
con->mark_changed = true; con->mark_changed = true;
DLOG("Clearing the mark from all other windows.\n");
Con *other;
TAILQ_FOREACH(other, &all_cons, all_cons) {
/* Skip the window we actually handled since we took care of it already. */
if (con == other)
continue;
if (other->mark != NULL && strcmp(other->mark, mark) == 0) {
FREE(other->mark);
other->mark_changed = true;
}
}
} }
/* /*
* If mark is NULL, this removes all existing marks. * Removes marks from containers.
* If con is NULL, all containers are considered.
* If name is NULL, this removes all existing marks.
* Otherwise, it will only remove the given mark (if it is present). * Otherwise, it will only remove the given mark (if it is present).
* *
*/ */
void con_unmark(const char *mark) { void con_unmark(Con *con, const char *name) {
Con *con; Con *current;
if (mark == NULL) { if (name == NULL) {
DLOG("Unmarking all containers.\n"); DLOG("Unmarking all containers.\n");
TAILQ_FOREACH(con, &all_cons, all_cons) { TAILQ_FOREACH(current, &all_cons, all_cons) {
if (con->mark == NULL) if (con != NULL && current != con)
continue; continue;
FREE(con->mark); if (TAILQ_EMPTY(&(current->marks_head)))
con->mark_changed = true; continue;
mark_t *mark;
while (!TAILQ_EMPTY(&(current->marks_head))) {
mark = TAILQ_FIRST(&(current->marks_head));
FREE(mark->name);
TAILQ_REMOVE(&(current->marks_head), mark, marks);
FREE(mark);
}
current->mark_changed = true;
} }
} else { } else {
DLOG("Removing mark \"%s\".\n", mark); DLOG("Removing mark \"%s\".\n", name);
con = con_by_mark(mark); current = (con == NULL) ? con_by_mark(name) : con;
if (con == NULL) { if (current == NULL) {
DLOG("No container found with this mark, so there is nothing to do.\n"); DLOG("No container found with this mark, so there is nothing to do.\n");
return; return;
} }
DLOG("Found mark on con = %p. Removing it now.\n", con); DLOG("Found mark on con = %p. Removing it now.\n", current);
FREE(con->mark); current->mark_changed = true;
con->mark_changed = true;
mark_t *mark;
TAILQ_FOREACH(mark, &(current->marks_head), marks) {
if (strcmp(mark->name, name) != 0)
continue;
FREE(mark->name);
TAILQ_REMOVE(&(current->marks_head), mark, marks);
FREE(mark);
break;
}
} }
} }
@ -1004,15 +1071,16 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
startup_sequence_delete(sequence); startup_sequence_delete(sequence);
} }
CALL(parent, on_remove_child);
/* 9. If the container was marked urgent, move the urgency hint. */ /* 9. If the container was marked urgent, move the urgency hint. */
if (urgent) { if (urgent) {
workspace_update_urgent_flag(source_ws); workspace_update_urgent_flag(source_ws);
con_set_urgency(con, true); con_set_urgency(con, true);
} }
CALL(parent, on_remove_child);
ipc_send_window_event("move", con); ipc_send_window_event("move", con);
ewmh_update_wm_desktop();
return true; return true;
} }
@ -1126,7 +1194,7 @@ orientation_t con_orientation(Con *con) {
/* /*
* Returns the container which will be focused next when the given container * Returns the container which will be focused next when the given container
* is not available anymore. Called in tree_close and con_move_to_workspace * is not available anymore. Called in tree_close_internal and con_move_to_workspace
* to properly restore focus. * to properly restore focus.
* *
*/ */
@ -1642,7 +1710,7 @@ static void con_on_remove_child(Con *con) {
if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) { if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name); LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL); yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL);
tree_close(con, DONT_KILL_WINDOW, false, false); tree_close_internal(con, DONT_KILL_WINDOW, false, false);
const unsigned char *payload; const unsigned char *payload;
ylength length; ylength length;
@ -1663,7 +1731,7 @@ static void con_on_remove_child(Con *con) {
int children = con_num_children(con); int children = con_num_children(con);
if (children == 0) { if (children == 0) {
DLOG("Container empty, closing\n"); DLOG("Container empty, closing\n");
tree_close(con, DONT_KILL_WINDOW, false, false); tree_close_internal(con, DONT_KILL_WINDOW, false, false);
return; return;
} }
} }
@ -1925,6 +1993,7 @@ char *con_get_tree_representation(Con *con) {
(TAILQ_FIRST(&(con->nodes_head)) == child ? "" : " "), child_txt); (TAILQ_FIRST(&(con->nodes_head)) == child ? "" : " "), child_txt);
free(buf); free(buf);
buf = tmp_buf; buf = tmp_buf;
free(child_txt);
} }
/* 3) close the brackets */ /* 3) close the brackets */
@ -1934,3 +2003,47 @@ char *con_get_tree_representation(Con *con) {
return complete_buf; return complete_buf;
} }
/*
* Returns the container's title considering the current title format.
*
*/
i3String *con_parse_title_format(Con *con) {
assert(con->title_format != NULL);
i3Window *win = con->window;
/* We need to ensure that we only escape the window title if pango
* is used by the current font. */
const bool pango_markup = font_is_pango();
char *title;
char *class;
char *instance;
if (win == NULL) {
title = pango_escape_markup(con_get_tree_representation(con));
class = sstrdup("i3-frame");
instance = sstrdup("i3-frame");
} else {
title = pango_escape_markup(sstrdup((win->name == NULL) ? "" : i3string_as_utf8(win->name)));
class = pango_escape_markup(sstrdup((win->class_class == NULL) ? "" : win->class_class));
instance = pango_escape_markup(sstrdup((win->class_instance == NULL) ? "" : win->class_instance));
}
placeholder_t placeholders[] = {
{.name = "%title", .value = title},
{.name = "%class", .value = class},
{.name = "%instance", .value = instance}};
const size_t num = sizeof(placeholders) / sizeof(placeholder_t);
char *formatted_str = format_placeholders(con->title_format, &placeholders[0], num);
i3String *formatted = i3string_from_utf8(formatted_str);
i3string_set_markup(formatted, pango_markup);
FREE(formatted_str);
for (size_t i = 0; i < num; i++) {
FREE(placeholders[i].value);
}
return formatted;
}

View File

@ -71,24 +71,29 @@ bool parse_configuration(const char *override_configpath, bool use_nagbar) {
*/ */
void load_configuration(xcb_connection_t *conn, const char *override_configpath, bool reload) { void load_configuration(xcb_connection_t *conn, const char *override_configpath, bool reload) {
if (reload) { if (reload) {
/* If we are currently in a binding mode, we first revert to the
* default since we have no guarantee that the current mode will even
* still exist after parsing the config again. See #2228. */
switch_mode("default");
/* First ungrab the keys */ /* First ungrab the keys */
ungrab_all_keys(conn); ungrab_all_keys(conn);
struct Mode *mode; struct Mode *mode;
Binding *bind;
while (!SLIST_EMPTY(&modes)) { while (!SLIST_EMPTY(&modes)) {
mode = SLIST_FIRST(&modes); mode = SLIST_FIRST(&modes);
FREE(mode->name); FREE(mode->name);
/* Clear the old binding list */ /* Clear the old binding list */
bindings = mode->bindings; while (!TAILQ_EMPTY(mode->bindings)) {
while (!TAILQ_EMPTY(bindings)) { Binding *bind = TAILQ_FIRST(mode->bindings);
bind = TAILQ_FIRST(bindings); TAILQ_REMOVE(mode->bindings, bind, bindings);
TAILQ_REMOVE(bindings, bind, bindings);
binding_free(bind); binding_free(bind);
} }
FREE(bindings); FREE(mode->bindings);
SLIST_REMOVE(&modes, mode, Mode, modes); SLIST_REMOVE(&modes, mode, Mode, modes);
FREE(mode);
} }
struct Assignment *assign; struct Assignment *assign;
@ -110,14 +115,32 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
FREE(barconfig->id); FREE(barconfig->id);
for (int c = 0; c < barconfig->num_outputs; c++) for (int c = 0; c < barconfig->num_outputs; c++)
free(barconfig->outputs[c]); free(barconfig->outputs[c]);
while (!TAILQ_EMPTY(&(barconfig->bar_bindings))) {
struct Barbinding *binding = TAILQ_FIRST(&(barconfig->bar_bindings));
FREE(binding->command);
TAILQ_REMOVE(&(barconfig->bar_bindings), binding, bindings);
FREE(binding);
}
while (!TAILQ_EMPTY(&(barconfig->tray_outputs))) {
struct tray_output_t *tray_output = TAILQ_FIRST(&(barconfig->tray_outputs));
FREE(tray_output->output);
TAILQ_REMOVE(&(barconfig->tray_outputs), tray_output, tray_outputs);
FREE(tray_output);
}
FREE(barconfig->outputs); FREE(barconfig->outputs);
FREE(barconfig->tray_output);
FREE(barconfig->socket_path); FREE(barconfig->socket_path);
FREE(barconfig->status_command); FREE(barconfig->status_command);
FREE(barconfig->i3bar_command); FREE(barconfig->i3bar_command);
FREE(barconfig->font); FREE(barconfig->font);
FREE(barconfig->colors.background); FREE(barconfig->colors.background);
FREE(barconfig->colors.statusline); FREE(barconfig->colors.statusline);
FREE(barconfig->colors.separator);
FREE(barconfig->colors.focused_background);
FREE(barconfig->colors.focused_statusline);
FREE(barconfig->colors.focused_separator);
FREE(barconfig->colors.focused_workspace_border); FREE(barconfig->colors.focused_workspace_border);
FREE(barconfig->colors.focused_workspace_bg); FREE(barconfig->colors.focused_workspace_bg);
FREE(barconfig->colors.focused_workspace_text); FREE(barconfig->colors.focused_workspace_text);
@ -151,6 +174,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
/* Get rid of the current font */ /* Get rid of the current font */
free_font(); free_font();
free(config.ipc_socket_path);
free(config.restart_state_path);
free(config.fake_outputs);
} }
SLIST_INIT(&modes); SLIST_INIT(&modes);
@ -173,13 +200,14 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
/* Initialize default colors */ /* Initialize default colors */
#define INIT_COLOR(x, cborder, cbackground, ctext, cindicator) \ #define INIT_COLOR(x, cborder, cbackground, ctext, cindicator) \
do { \ do { \
x.border = get_colorpixel(cborder); \ x.border = draw_util_hex_to_color(cborder); \
x.background = get_colorpixel(cbackground); \ x.background = draw_util_hex_to_color(cbackground); \
x.text = get_colorpixel(ctext); \ x.text = draw_util_hex_to_color(ctext); \
x.indicator = get_colorpixel(cindicator); \ x.indicator = draw_util_hex_to_color(cindicator); \
x.child_border = draw_util_hex_to_color(cbackground); \
} while (0) } while (0)
config.client.background = get_colorpixel("#000000"); config.client.background = draw_util_hex_to_color("#000000");
INIT_COLOR(config.client.focused, "#4c7899", "#285577", "#ffffff", "#2e9ef4"); INIT_COLOR(config.client.focused, "#4c7899", "#285577", "#ffffff", "#2e9ef4");
INIT_COLOR(config.client.focused_inactive, "#333333", "#5f676a", "#ffffff", "#484e50"); INIT_COLOR(config.client.focused_inactive, "#333333", "#5f676a", "#ffffff", "#484e50");
INIT_COLOR(config.client.unfocused, "#333333", "#222222", "#888888", "#292d2e"); INIT_COLOR(config.client.unfocused, "#333333", "#222222", "#888888", "#292d2e");
@ -211,6 +239,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
if (reload) { if (reload) {
translate_keysyms(); translate_keysyms();
grab_all_keys(conn); grab_all_keys(conn);
regrab_all_buttons(conn);
} }
if (config.font.type == FONT_TYPE_NONE) { if (config.font.type == FONT_TYPE_NONE) {

View File

@ -109,7 +109,7 @@ CFGFUN(font, const char *font) {
} }
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) { CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) {
configure_binding(bindtype, modifiers, key, release, border, whole_window, command, DEFAULT_BINDING_MODE); configure_binding(bindtype, modifiers, key, release, border, whole_window, command, DEFAULT_BINDING_MODE, false);
} }
/******************************************************************************* /*******************************************************************************
@ -117,12 +117,13 @@ CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, co
******************************************************************************/ ******************************************************************************/
static char *current_mode; static char *current_mode;
static bool current_mode_pango_markup;
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) { CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) {
configure_binding(bindtype, modifiers, key, release, border, whole_window, command, current_mode); configure_binding(bindtype, modifiers, key, release, border, whole_window, command, current_mode, current_mode_pango_markup);
} }
CFGFUN(enter_mode, const char *modename) { CFGFUN(enter_mode, const char *pango_markup, const char *modename) {
if (strcasecmp(modename, DEFAULT_BINDING_MODE) == 0) { if (strcasecmp(modename, DEFAULT_BINDING_MODE) == 0) {
ELOG("You cannot use the name %s for your mode\n", DEFAULT_BINDING_MODE); ELOG("You cannot use the name %s for your mode\n", DEFAULT_BINDING_MODE);
exit(1); exit(1);
@ -130,6 +131,7 @@ CFGFUN(enter_mode, const char *modename) {
DLOG("\t now in mode %s\n", modename); DLOG("\t now in mode %s\n", modename);
FREE(current_mode); FREE(current_mode);
current_mode = sstrdup(modename); current_mode = sstrdup(modename);
current_mode_pango_markup = (pango_markup != NULL);
} }
CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command) { CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command) {
@ -259,6 +261,7 @@ CFGFUN(workspace_back_and_forth, const char *value) {
} }
CFGFUN(fake_outputs, const char *outputs) { CFGFUN(fake_outputs, const char *outputs) {
free(config.fake_outputs);
config.fake_outputs = sstrdup(outputs); config.fake_outputs = sstrdup(outputs);
} }
@ -311,10 +314,12 @@ CFGFUN(workspace, const char *workspace, const char *output) {
} }
CFGFUN(ipc_socket, const char *path) { CFGFUN(ipc_socket, const char *path) {
free(config.ipc_socket_path);
config.ipc_socket_path = sstrdup(path); config.ipc_socket_path = sstrdup(path);
} }
CFGFUN(restart_state, const char *path) { CFGFUN(restart_state, const char *path) {
free(config.restart_state_path);
config.restart_state_path = sstrdup(path); config.restart_state_path = sstrdup(path);
} }
@ -330,18 +335,23 @@ CFGFUN(popup_during_fullscreen, const char *value) {
CFGFUN(color_single, const char *colorclass, const char *color) { CFGFUN(color_single, const char *colorclass, const char *color) {
/* used for client.background only currently */ /* used for client.background only currently */
config.client.background = get_colorpixel(color); config.client.background = draw_util_hex_to_color(color);
} }
CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator) { CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator, const char *child_border) {
#define APPLY_COLORS(classname) \ #define APPLY_COLORS(classname) \
do { \ do { \
if (strcmp(colorclass, "client." #classname) == 0) { \ if (strcmp(colorclass, "client." #classname) == 0) { \
config.client.classname.border = get_colorpixel(border); \ config.client.classname.border = draw_util_hex_to_color(border); \
config.client.classname.background = get_colorpixel(background); \ config.client.classname.background = draw_util_hex_to_color(background); \
config.client.classname.text = get_colorpixel(text); \ config.client.classname.text = draw_util_hex_to_color(text); \
if (indicator != NULL) { \ if (indicator != NULL) { \
config.client.classname.indicator = get_colorpixel(indicator); \ config.client.classname.indicator = draw_util_hex_to_color(indicator); \
} \
if (child_border != NULL) { \
config.client.classname.child_border = draw_util_hex_to_color(child_border); \
} else { \
config.client.classname.child_border = config.client.classname.background; \
} \ } \
} \ } \
} while (0) } while (0)
@ -385,57 +395,60 @@ CFGFUN(no_focus) {
* Bar configuration (i3bar) * Bar configuration (i3bar)
******************************************************************************/ ******************************************************************************/
static Barconfig current_bar; static Barconfig *current_bar;
CFGFUN(bar_font, const char *font) { CFGFUN(bar_font, const char *font) {
FREE(current_bar.font); FREE(current_bar->font);
current_bar.font = sstrdup(font); current_bar->font = sstrdup(font);
} }
CFGFUN(bar_separator_symbol, const char *separator) { CFGFUN(bar_separator_symbol, const char *separator) {
FREE(current_bar.separator_symbol); FREE(current_bar->separator_symbol);
current_bar.separator_symbol = sstrdup(separator); current_bar->separator_symbol = sstrdup(separator);
} }
CFGFUN(bar_mode, const char *mode) { CFGFUN(bar_mode, const char *mode) {
current_bar.mode = (strcmp(mode, "dock") == 0 ? M_DOCK : (strcmp(mode, "hide") == 0 ? M_HIDE : M_INVISIBLE)); current_bar->mode = (strcmp(mode, "dock") == 0 ? M_DOCK : (strcmp(mode, "hide") == 0 ? M_HIDE : M_INVISIBLE));
} }
CFGFUN(bar_hidden_state, const char *hidden_state) { CFGFUN(bar_hidden_state, const char *hidden_state) {
current_bar.hidden_state = (strcmp(hidden_state, "hide") == 0 ? S_HIDE : S_SHOW); current_bar->hidden_state = (strcmp(hidden_state, "hide") == 0 ? S_HIDE : S_SHOW);
} }
CFGFUN(bar_id, const char *bar_id) { CFGFUN(bar_id, const char *bar_id) {
current_bar.id = sstrdup(bar_id); current_bar->id = sstrdup(bar_id);
} }
CFGFUN(bar_output, const char *output) { CFGFUN(bar_output, const char *output) {
int new_outputs = current_bar.num_outputs + 1; int new_outputs = current_bar->num_outputs + 1;
current_bar.outputs = srealloc(current_bar.outputs, sizeof(char *) * new_outputs); current_bar->outputs = srealloc(current_bar->outputs, sizeof(char *) * new_outputs);
current_bar.outputs[current_bar.num_outputs] = sstrdup(output); current_bar->outputs[current_bar->num_outputs] = sstrdup(output);
current_bar.num_outputs = new_outputs; current_bar->num_outputs = new_outputs;
} }
CFGFUN(bar_verbose, const char *verbose) { CFGFUN(bar_verbose, const char *verbose) {
current_bar.verbose = eval_boolstr(verbose); current_bar->verbose = eval_boolstr(verbose);
} }
CFGFUN(bar_modifier, const char *modifier) { CFGFUN(bar_modifier, const char *modifier) {
if (strcmp(modifier, "Mod1") == 0) if (strcmp(modifier, "Mod1") == 0)
current_bar.modifier = M_MOD1; current_bar->modifier = M_MOD1;
else if (strcmp(modifier, "Mod2") == 0) else if (strcmp(modifier, "Mod2") == 0)
current_bar.modifier = M_MOD2; current_bar->modifier = M_MOD2;
else if (strcmp(modifier, "Mod3") == 0) else if (strcmp(modifier, "Mod3") == 0)
current_bar.modifier = M_MOD3; current_bar->modifier = M_MOD3;
else if (strcmp(modifier, "Mod4") == 0) else if (strcmp(modifier, "Mod4") == 0)
current_bar.modifier = M_MOD4; current_bar->modifier = M_MOD4;
else if (strcmp(modifier, "Mod5") == 0) else if (strcmp(modifier, "Mod5") == 0)
current_bar.modifier = M_MOD5; current_bar->modifier = M_MOD5;
else if (strcmp(modifier, "Control") == 0 || else if (strcmp(modifier, "Control") == 0 ||
strcmp(modifier, "Ctrl") == 0) strcmp(modifier, "Ctrl") == 0)
current_bar.modifier = M_CONTROL; current_bar->modifier = M_CONTROL;
else if (strcmp(modifier, "Shift") == 0) else if (strcmp(modifier, "Shift") == 0)
current_bar.modifier = M_SHIFT; current_bar->modifier = M_SHIFT;
else if (strcmp(modifier, "none") == 0 ||
strcmp(modifier, "off") == 0)
current_bar->modifier = M_NONE;
} }
static void bar_configure_binding(const char *button, const char *command) { static void bar_configure_binding(const char *button, const char *command) {
@ -451,7 +464,7 @@ static void bar_configure_binding(const char *button, const char *command) {
} }
struct Barbinding *current; struct Barbinding *current;
TAILQ_FOREACH(current, &(current_bar.bar_bindings), bindings) { TAILQ_FOREACH(current, &(current_bar->bar_bindings), bindings) {
if (current->input_code == input_code) { if (current->input_code == input_code) {
ELOG("command for button %s was already specified, ignoring.\n", button); ELOG("command for button %s was already specified, ignoring.\n", button);
return; return;
@ -461,7 +474,7 @@ static void bar_configure_binding(const char *button, const char *command) {
struct Barbinding *new_binding = scalloc(1, sizeof(struct Barbinding)); struct Barbinding *new_binding = scalloc(1, sizeof(struct Barbinding));
new_binding->input_code = input_code; new_binding->input_code = input_code;
new_binding->command = sstrdup(command); new_binding->command = sstrdup(command);
TAILQ_INSERT_TAIL(&(current_bar.bar_bindings), new_binding, bindings); TAILQ_INSERT_TAIL(&(current_bar->bar_bindings), new_binding, bindings);
} }
CFGFUN(bar_wheel_up_cmd, const char *command) { CFGFUN(bar_wheel_up_cmd, const char *command) {
@ -479,12 +492,12 @@ CFGFUN(bar_bindsym, const char *button, const char *command) {
} }
CFGFUN(bar_position, const char *position) { CFGFUN(bar_position, const char *position) {
current_bar.position = (strcmp(position, "top") == 0 ? P_TOP : P_BOTTOM); current_bar->position = (strcmp(position, "top") == 0 ? P_TOP : P_BOTTOM);
} }
CFGFUN(bar_i3bar_command, const char *i3bar_command) { CFGFUN(bar_i3bar_command, const char *i3bar_command) {
FREE(current_bar.i3bar_command); FREE(current_bar->i3bar_command);
current_bar.i3bar_command = sstrdup(i3bar_command); current_bar->i3bar_command = sstrdup(i3bar_command);
} }
CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text) { CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text) {
@ -493,13 +506,13 @@ CFGFUN(bar_color, const char *colorclass, const char *border, const char *backgr
if (strcmp(colorclass, #classname) == 0) { \ if (strcmp(colorclass, #classname) == 0) { \
if (text != NULL) { \ if (text != NULL) { \
/* New syntax: border, background, text */ \ /* New syntax: border, background, text */ \
current_bar.colors.classname##_border = sstrdup(border); \ current_bar->colors.classname##_border = sstrdup(border); \
current_bar.colors.classname##_bg = sstrdup(background); \ current_bar->colors.classname##_bg = sstrdup(background); \
current_bar.colors.classname##_text = sstrdup(text); \ current_bar->colors.classname##_text = sstrdup(text); \
} else { \ } else { \
/* Old syntax: text, background */ \ /* Old syntax: text, background */ \
current_bar.colors.classname##_bg = sstrdup(background); \ current_bar->colors.classname##_bg = sstrdup(background); \
current_bar.colors.classname##_text = sstrdup(border); \ current_bar->colors.classname##_text = sstrdup(border); \
} \ } \
} \ } \
} while (0) } while (0)
@ -514,67 +527,73 @@ CFGFUN(bar_color, const char *colorclass, const char *border, const char *backgr
} }
CFGFUN(bar_socket_path, const char *socket_path) { CFGFUN(bar_socket_path, const char *socket_path) {
FREE(current_bar.socket_path); FREE(current_bar->socket_path);
current_bar.socket_path = sstrdup(socket_path); current_bar->socket_path = sstrdup(socket_path);
} }
CFGFUN(bar_tray_output, const char *output) { CFGFUN(bar_tray_output, const char *output) {
FREE(current_bar.tray_output); struct tray_output_t *tray_output = scalloc(1, sizeof(struct tray_output_t));
current_bar.tray_output = sstrdup(output); tray_output->output = sstrdup(output);
TAILQ_INSERT_TAIL(&(current_bar->tray_outputs), tray_output, tray_outputs);
} }
CFGFUN(bar_tray_padding, const long padding_px) { CFGFUN(bar_tray_padding, const long padding_px) {
current_bar.tray_padding = padding_px; current_bar->tray_padding = padding_px;
} }
CFGFUN(bar_color_single, const char *colorclass, const char *color) { CFGFUN(bar_color_single, const char *colorclass, const char *color) {
if (strcmp(colorclass, "background") == 0) if (strcmp(colorclass, "background") == 0)
current_bar.colors.background = sstrdup(color); current_bar->colors.background = sstrdup(color);
else if (strcmp(colorclass, "separator") == 0) else if (strcmp(colorclass, "separator") == 0)
current_bar.colors.separator = sstrdup(color); current_bar->colors.separator = sstrdup(color);
else if (strcmp(colorclass, "statusline") == 0)
current_bar->colors.statusline = sstrdup(color);
else if (strcmp(colorclass, "focused_background") == 0)
current_bar->colors.focused_background = sstrdup(color);
else if (strcmp(colorclass, "focused_separator") == 0)
current_bar->colors.focused_separator = sstrdup(color);
else else
current_bar.colors.statusline = sstrdup(color); current_bar->colors.focused_statusline = sstrdup(color);
} }
CFGFUN(bar_status_command, const char *command) { CFGFUN(bar_status_command, const char *command) {
FREE(current_bar.status_command); FREE(current_bar->status_command);
current_bar.status_command = sstrdup(command); current_bar->status_command = sstrdup(command);
} }
CFGFUN(bar_binding_mode_indicator, const char *value) { CFGFUN(bar_binding_mode_indicator, const char *value) {
current_bar.hide_binding_mode_indicator = !eval_boolstr(value); current_bar->hide_binding_mode_indicator = !eval_boolstr(value);
} }
CFGFUN(bar_workspace_buttons, const char *value) { CFGFUN(bar_workspace_buttons, const char *value) {
current_bar.hide_workspace_buttons = !eval_boolstr(value); current_bar->hide_workspace_buttons = !eval_boolstr(value);
} }
CFGFUN(bar_strip_workspace_numbers, const char *value) { CFGFUN(bar_strip_workspace_numbers, const char *value) {
current_bar.strip_workspace_numbers = eval_boolstr(value); current_bar->strip_workspace_numbers = eval_boolstr(value);
} }
CFGFUN(bar_start) { CFGFUN(bar_start) {
TAILQ_INIT(&(current_bar.bar_bindings)); current_bar = scalloc(1, sizeof(struct Barconfig));
current_bar.tray_padding = 2; TAILQ_INIT(&(current_bar->bar_bindings));
TAILQ_INIT(&(current_bar->tray_outputs));
current_bar->tray_padding = 2;
current_bar->modifier = M_MOD4;
} }
CFGFUN(bar_finish) { CFGFUN(bar_finish) {
DLOG("\t new bar configuration finished, saving.\n"); DLOG("\t new bar configuration finished, saving.\n");
/* Generate a unique ID for this bar if not already configured */ /* Generate a unique ID for this bar if not already configured */
if (!current_bar.id) if (current_bar->id == NULL)
sasprintf(&current_bar.id, "bar-%d", config.number_barconfigs); sasprintf(&current_bar->id, "bar-%d", config.number_barconfigs);
config.number_barconfigs++; config.number_barconfigs++;
/* If no font was explicitly set, we use the i3 font as default */ /* If no font was explicitly set, we use the i3 font as default */
if (!current_bar.font && font_pattern) if (current_bar->font == NULL && font_pattern != NULL)
current_bar.font = sstrdup(font_pattern); current_bar->font = sstrdup(font_pattern);
/* Copy the current (static) structure into a dynamically allocated TAILQ_INSERT_TAIL(&barconfigs, current_bar, configs);
* one, then cleanup our static one. */ /* Simply reset the pointer, but don't free the resources. */
Barconfig *bar_config = scalloc(1, sizeof(Barconfig)); current_bar = NULL;
memcpy(bar_config, &current_bar, sizeof(Barconfig));
TAILQ_INSERT_TAIL(&barconfigs, bar_config, configs);
memset(&current_bar, '\0', sizeof(Barconfig));
} }

View File

@ -783,6 +783,34 @@ static char *migrate_config(char *input, off_t size) {
return converted; return converted;
} }
/**
* Launch nagbar to indicate errors in the configuration file.
*/
void start_config_error_nagbar(const char *configpath, bool has_errors) {
char *editaction, *pageraction;
sasprintf(&editaction, "i3-sensible-editor \"%s\" && i3-msg reload\n", configpath);
sasprintf(&pageraction, "i3-sensible-pager \"%s\"\n", errorfilename);
char *argv[] = {
NULL, /* will be replaced by the executable path */
"-f",
(config.font.pattern ? config.font.pattern : "fixed"),
"-t",
(has_errors ? "error" : "warning"),
"-m",
(has_errors ? "You have an error in your i3 config file!" : "Your config is outdated. Please fix the warnings to make sure everything works."),
"-b",
"edit config",
editaction,
(errorfilename ? "-b" : NULL),
(has_errors ? "show errors" : "show warnings"),
pageraction,
NULL};
start_nagbar(&config_error_nagbar_pid, argv);
free(editaction);
free(pageraction);
}
/* /*
* Parses the given file by first replacing the variables, then calling * Parses the given file by first replacing the variables, then calling
* parse_config and possibly launching i3-nagbar. * parse_config and possibly launching i3-nagbar.
@ -815,7 +843,7 @@ bool parse_file(const char *f, bool use_nagbar) {
break; break;
die("Could not read configuration file\n"); die("Could not read configuration file\n");
} }
if (buffer[strlen(buffer) - 1] != '\n') { if (buffer[strlen(buffer) - 1] != '\n' && !feof(fstr)) {
ELOG("Your line continuation is too long, it exceeds %zd bytes\n", sizeof(buffer)); ELOG("Your line continuation is too long, it exceeds %zd bytes\n", sizeof(buffer));
} }
continuation = strstr(buffer, "\\\n"); continuation = strstr(buffer, "\\\n");
@ -882,7 +910,7 @@ bool parse_file(const char *f, bool use_nagbar) {
FREE(bufcopy); FREE(bufcopy);
/* Then, allocate a new buffer and copy the file over to the new one, /* Then, allocate a new buffer and copy the file over to the new one,
* but replace occurences of our variables */ * but replace occurrences of our variables */
char *walk = buf, *destwalk; char *walk = buf, *destwalk;
char *new = smalloc(stbuf.st_size + extra_bytes + 1); char *new = smalloc(stbuf.st_size + extra_bytes + 1);
destwalk = new; destwalk = new;
@ -958,29 +986,7 @@ bool parse_file(const char *f, bool use_nagbar) {
if (version == 3) if (version == 3)
ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n"); ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n");
char *editaction, start_config_error_nagbar(f, context->has_errors);
*pageraction;
sasprintf(&editaction, "i3-sensible-editor \"%s\" && i3-msg reload\n", f);
sasprintf(&pageraction, "i3-sensible-pager \"%s\"\n", errorfilename);
char *argv[] = {
NULL, /* will be replaced by the executable path */
"-f",
(config.font.pattern ? config.font.pattern : "fixed"),
"-t",
(context->has_errors ? "error" : "warning"),
"-m",
(context->has_errors ? "You have an error in your i3 config file!" : "Your config is outdated. Please fix the warnings to make sure everything works."),
"-b",
"edit config",
editaction,
(errorfilename ? "-b" : NULL),
(context->has_errors ? "show errors" : "show warnings"),
pageraction,
NULL};
start_nagbar(&config_error_nagbar_pid, argv);
free(editaction);
free(pageraction);
} }
bool has_errors = context->has_errors; bool has_errors = context->has_errors;

View File

@ -21,24 +21,9 @@ xcb_window_t ewmh_window;
* *
*/ */
void ewmh_update_current_desktop(void) { void ewmh_update_current_desktop(void) {
Con *focused_ws = con_get_workspace(focused); const uint32_t idx = ewmh_get_workspace_index(focused);
Con *output; if (idx != NET_WM_DESKTOP_NONE) {
uint32_t idx = 0; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_CURRENT_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &idx);
/* We count to get the index of this workspace because named workspaces
* dont have the ->num property */
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
if (ws == focused_ws) {
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
A__NET_CURRENT_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &idx);
return;
}
++idx;
}
} }
} }
@ -138,6 +123,71 @@ void ewmh_update_desktop_viewport(void) {
A__NET_DESKTOP_VIEWPORT, XCB_ATOM_CARDINAL, 32, current_position, &viewports); A__NET_DESKTOP_VIEWPORT, XCB_ATOM_CARDINAL, 32, current_position, &viewports);
} }
static void ewmh_update_wm_desktop_recursively(Con *con, const uint32_t desktop) {
/* Recursively call this to descend through the entire subtree. */
Con *child;
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
ewmh_update_wm_desktop_recursively(child, desktop);
}
/* If con is a workspace, we also need to go through the floating windows on it. */
if (con->type == CT_WORKSPACE) {
TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
ewmh_update_wm_desktop_recursively(child, desktop);
}
}
if (!con_has_managed_window(con))
return;
const xcb_window_t window = con->window->id;
uint32_t wm_desktop = desktop;
/* Sticky windows are only actually sticky when they are floating or inside
* a floating container. This is technically still slightly wrong, since
* sticky windows will only be on all workspaces on this output, but we
* ignore multi-monitor situations for this since the spec isn't too
* precise on this anyway. */
if (con_is_sticky(con) && con_is_floating(con)) {
wm_desktop = NET_WM_DESKTOP_ALL;
}
/* If this is the cached value, we don't need to do anything. */
if (con->window->wm_desktop == wm_desktop)
return;
con->window->wm_desktop = wm_desktop;
if (wm_desktop != NET_WM_DESKTOP_NONE) {
DLOG("Setting _NET_WM_DESKTOP = %d for window 0x%08x.\n", wm_desktop, window);
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, A__NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &wm_desktop);
} else {
/* If we can't determine the workspace index, delete the property. We'd
* rather not set it than lie. */
ELOG("Failed to determine the proper EWMH desktop index for window 0x%08x, deleting _NET_WM_DESKTOP.\n", window);
xcb_delete_property(conn, window, A__NET_WM_DESKTOP);
}
}
/*
* Updates _NET_WM_DESKTOP for all windows.
* A request will only be made if the cached value differs from the calculated value.
*
*/
void ewmh_update_wm_desktop(void) {
uint32_t desktop = 0;
Con *output;
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *workspace;
TAILQ_FOREACH(workspace, &(output_get_content(output)->nodes_head), nodes) {
if (con_is_internal(workspace))
continue;
ewmh_update_wm_desktop_recursively(workspace, desktop);
++desktop;
}
}
}
/* /*
* Updates _NET_ACTIVE_WINDOW with the currently focused window. * Updates _NET_ACTIVE_WINDOW with the currently focused window.
* *
@ -234,7 +284,7 @@ void ewmh_update_sticky(xcb_window_t window, bool sticky) {
void ewmh_setup_hints(void) { void ewmh_setup_hints(void) {
xcb_atom_t supported_atoms[] = { xcb_atom_t supported_atoms[] = {
#define xmacro(atom) A_##atom, #define xmacro(atom) A_##atom,
#include "atoms.xmacro" #include "atoms_NET_SUPPORTED.xmacro"
#undef xmacro #undef xmacro
}; };
@ -263,10 +313,67 @@ void ewmh_setup_hints(void) {
/* Im not entirely sure if we need to keep _NET_WM_NAME on root. */ /* Im not entirely sure if we need to keep _NET_WM_NAME on root. */
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
/* only send the first 31 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, /* number of atoms */ sizeof(supported_atoms) / sizeof(xcb_atom_t), supported_atoms);
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, /* number of atoms */ 32, supported_atoms);
/* We need to map this window to be able to set the input focus to it if no other window is available to be focused. */ /* We need to map this window to be able to set the input focus to it if no other window is available to be focused. */
xcb_map_window(conn, ewmh_window); xcb_map_window(conn, ewmh_window);
xcb_configure_window(conn, ewmh_window, XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){XCB_STACK_MODE_BELOW}); xcb_configure_window(conn, ewmh_window, XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){XCB_STACK_MODE_BELOW});
} }
/*
* Returns the workspace container as enumerated by the EWMH desktop model.
* Returns NULL if no workspace could be found for the index.
*
* This is the reverse of ewmh_get_workspace_index.
*
*/
Con *ewmh_get_workspace_by_index(uint32_t idx) {
if (idx == NET_WM_DESKTOP_NONE)
return NULL;
uint32_t current_index = 0;
Con *output;
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *workspace;
TAILQ_FOREACH(workspace, &(output_get_content(output)->nodes_head), nodes) {
if (con_is_internal(workspace))
continue;
if (current_index == idx)
return workspace;
++current_index;
}
}
return NULL;
}
/*
* Returns the EWMH desktop index for the workspace the given container is on.
* Returns NET_WM_DESKTOP_NONE if the desktop index cannot be determined.
*
* This is the reverse of ewmh_get_workspace_by_index.
*
*/
uint32_t ewmh_get_workspace_index(Con *con) {
uint32_t index = 0;
Con *workspace = con_get_workspace(con);
Con *output;
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *current;
TAILQ_FOREACH(current, &(output_get_content(output)->nodes_head), nodes) {
if (con_is_internal(current))
continue;
if (current == workspace)
return index;
++index;
}
}
return NET_WM_DESKTOP_NONE;
}

View File

@ -11,8 +11,6 @@
*/ */
#include "all.h" #include "all.h"
extern xcb_connection_t *conn;
/* /*
* Calculates sum of heights and sum of widths of all currently active outputs * Calculates sum of heights and sum of widths of all currently active outputs
* *
@ -31,6 +29,34 @@ static Rect total_outputs_dimensions(void) {
return outputs_dimensions; return outputs_dimensions;
} }
/*
* Updates I3_FLOATING_WINDOW by either setting or removing it on the con and
* all its children.
*
*/
static void floating_set_hint_atom(Con *con, bool floating) {
if (!con_is_leaf(con)) {
Con *child;
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
floating_set_hint_atom(child, floating);
}
}
if (con->window == NULL) {
return;
}
if (floating) {
uint32_t val = 1;
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
A_I3_FLOATING_WINDOW, XCB_ATOM_CARDINAL, 32, 1, &val);
} else {
xcb_delete_property(conn, con->window->id, A_I3_FLOATING_WINDOW);
}
xcb_flush(conn);
}
/** /**
* Called when a floating window is created or resized. * Called when a floating window is created or resized.
* This function resizes the window if its size is higher or lower than the * This function resizes the window if its size is higher or lower than the
@ -118,51 +144,13 @@ void floating_enable(Con *con, bool automatic) {
return; return;
} }
/* 1: If the container is a workspace container, we need to create a new
* split-container with the same layout and make that one floating. We
* cannot touch the workspace container itself because floating containers
* are children of the workspace. */
if (con->type == CT_WORKSPACE) { if (con->type == CT_WORKSPACE) {
LOG("This is a workspace, creating new container around content\n"); LOG("Container is a workspace, not enabling floating mode.\n");
if (con_num_children(con) == 0) {
LOG("Workspace is empty, aborting\n");
return; return;
} }
/* TODO: refactor this with src/con.c:con_set_layout */
Con *new = con_new(NULL, NULL);
new->parent = con;
new->layout = con->layout;
/* since the new container will be set into floating mode directly
* afterwards, we need to copy the workspace rect. */
memcpy(&(new->rect), &(con->rect), sizeof(Rect));
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);
con = new;
set_focus = false;
}
/* 1: detach the container from its parent */ /* 1: detach the container from its parent */
/* TODO: refactor this with tree_close() */ /* TODO: refactor this with tree_close_internal() */
TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes); TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes);
TAILQ_REMOVE(&(con->parent->focus_head), con, focused); TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
@ -180,7 +168,7 @@ void floating_enable(Con *con, bool automatic) {
nc->layout = L_SPLITH; nc->layout = L_SPLITH;
/* We insert nc already, even though its rect is not yet calculated. This /* We insert nc already, even though its rect is not yet calculated. This
* is necessary because otherwise the workspace might be empty (and get * is necessary because otherwise the workspace might be empty (and get
* closed in tree_close()) even though its not. */ * closed in tree_close_internal()) even though its not. */
TAILQ_INSERT_TAIL(&(ws->floating_head), nc, floating_windows); TAILQ_INSERT_TAIL(&(ws->floating_head), nc, floating_windows);
TAILQ_INSERT_TAIL(&(ws->focus_head), nc, focused); TAILQ_INSERT_TAIL(&(ws->focus_head), nc, focused);
@ -188,7 +176,7 @@ void floating_enable(Con *con, bool automatic) {
if ((con->parent->type == CT_CON || con->parent->type == CT_FLOATING_CON) && if ((con->parent->type == CT_CON || con->parent->type == CT_FLOATING_CON) &&
con_num_children(con->parent) == 0) { con_num_children(con->parent) == 0) {
DLOG("Old container empty after setting this child to floating, closing\n"); DLOG("Old container empty after setting this child to floating, closing\n");
tree_close(con->parent, DONT_KILL_WINDOW, false, false); tree_close_internal(con->parent, DONT_KILL_WINDOW, false, false);
} }
char *name; char *name;
@ -300,19 +288,19 @@ void floating_enable(Con *con, bool automatic) {
/* Check if we need to re-assign it to a different workspace because of its /* Check if we need to re-assign it to a different workspace because of its
* coordinates and exit if that was done successfully. */ * coordinates and exit if that was done successfully. */
if (floating_maybe_reassign_ws(nc)) { if (floating_maybe_reassign_ws(nc)) {
ipc_send_window_event("floating", con); goto done;
return;
} }
/* Sanitize coordinates: Check if they are on any output */ /* Sanitize coordinates: Check if they are on any output */
if (get_output_containing(nc->rect.x, nc->rect.y) != NULL) { if (get_output_containing(nc->rect.x, nc->rect.y) != NULL) {
ipc_send_window_event("floating", con); goto done;
return;
} }
ELOG("No output found at destination coordinates, centering floating window on current ws\n"); ELOG("No output found at destination coordinates, centering floating window on current ws\n");
floating_center(nc, ws->rect); floating_center(nc, ws->rect);
done:
floating_set_hint_atom(nc, true);
ipc_send_window_event("floating", con); ipc_send_window_event("floating", con);
} }
@ -333,7 +321,7 @@ void floating_disable(Con *con, bool automatic) {
/* 2: kill parent container */ /* 2: kill parent container */
TAILQ_REMOVE(&(con->parent->parent->floating_head), con->parent, floating_windows); TAILQ_REMOVE(&(con->parent->parent->floating_head), con->parent, floating_windows);
TAILQ_REMOVE(&(con->parent->parent->focus_head), con->parent, focused); TAILQ_REMOVE(&(con->parent->parent->focus_head), con->parent, focused);
tree_close(con->parent, DONT_KILL_WINDOW, true, false); tree_close_internal(con->parent, DONT_KILL_WINDOW, true, false);
/* 3: re-attach to the parent of the currently focused con on the workspace /* 3: re-attach to the parent of the currently focused con on the workspace
* this floating con was on */ * this floating con was on */
@ -358,6 +346,7 @@ void floating_disable(Con *con, bool automatic) {
if (set_focus) if (set_focus)
con_focus(con); con_focus(con);
floating_set_hint_atom(con, false);
ipc_send_window_event("floating", con); ipc_send_window_event("floating", con);
} }

View File

@ -503,7 +503,12 @@ static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
goto ignore_end; goto ignore_end;
} }
tree_close(con, DONT_KILL_WINDOW, false, false); /* Since we close the container, we need to unset _NET_WM_DESKTOP and
* _NET_WM_STATE according to the spec. */
xcb_delete_property(conn, event->window, A__NET_WM_DESKTOP);
xcb_delete_property(conn, event->window, A__NET_WM_STATE);
tree_close_internal(con, DONT_KILL_WINDOW, false, false);
tree_render(); tree_render();
ignore_end: ignore_end:
@ -656,15 +661,14 @@ static void handle_expose_event(xcb_expose_event_t *event) {
return; return;
} }
/* Since we render to our pixmap on every change anyways, expose events /* Since we render to our surface on every change anyways, expose events
* only tell us that the X server lost (parts of) the window contents. We * only tell us that the X server lost (parts of) the window contents. We
* can handle that by copying the appropriate part from our pixmap to the * can handle that by copying the appropriate part from our surface to the
* window. */ * window. */
xcb_copy_area(conn, parent->pixmap, parent->frame, parent->pm_gc, draw_util_copy_surface(conn, &(parent->frame_buffer), &(parent->frame),
event->x, event->y, event->x, event->y, event->x, event->y, event->x, event->y,
event->width, event->height); event->width, event->height);
xcb_flush(conn); xcb_flush(conn);
return; return;
} }
@ -736,7 +740,9 @@ static void handle_client_message(xcb_client_message_event_t *event) {
con->sticky = !con->sticky; con->sticky = !con->sticky;
DLOG("New sticky status for con = %p is %i.\n", con, con->sticky); DLOG("New sticky status for con = %p is %i.\n", con, con->sticky);
ewmh_update_sticky(con->window->id, con->sticky);
output_push_sticky_windows(focused); output_push_sticky_windows(focused);
ewmh_update_wm_desktop();
} }
tree_render(); tree_render();
@ -840,32 +846,48 @@ static void handle_client_message(xcb_client_message_event_t *event) {
* a request to focus the given workspace. See * a request to focus the given workspace. See
* http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008 * http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008
* */ * */
Con *output;
uint32_t idx = 0;
DLOG("Request to change current desktop to index %d\n", event->data.data32[0]); DLOG("Request to change current desktop to index %d\n", event->data.data32[0]);
Con *ws = ewmh_get_workspace_by_index(event->data.data32[0]);
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { if (ws == NULL) {
Con *ws; ELOG("Could not determine workspace for this index, ignoring request.\n");
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
if (idx == event->data.data32[0]) {
/* data32[1] is a timestamp used to prevent focus race conditions */
if (event->data.data32[1])
last_timestamp = event->data.data32[1];
DLOG("Handling request to focus workspace %s\n", ws->name);
workspace_show(ws);
tree_render();
return; return;
} }
++idx; DLOG("Handling request to focus workspace %s\n", ws->name);
workspace_show(ws);
tree_render();
} else if (event->type == A__NET_WM_DESKTOP) {
uint32_t index = event->data.data32[0];
DLOG("Request to move window %d to EWMH desktop index %d\n", event->window, index);
Con *con = con_by_window_id(event->window);
if (con == NULL) {
DLOG("Couldn't find con for window %d, ignoring the request.\n", event->window);
return;
} }
if (index == NET_WM_DESKTOP_ALL) {
/* The window is requesting to be visible on all workspaces, so
* let's float it and make it sticky. */
DLOG("The window was requested to be visible on all workspaces, making it sticky and floating.\n");
floating_enable(con, false);
con->sticky = true;
ewmh_update_sticky(con->window->id, true);
output_push_sticky_windows(focused);
} else {
Con *ws = ewmh_get_workspace_by_index(index);
if (ws == NULL) {
ELOG("Could not determine workspace for this index, ignoring request.\n");
return;
} }
con_move_to_workspace(con, ws, true, false, false);
}
tree_render();
ewmh_update_wm_desktop();
} else if (event->type == A__NET_CLOSE_WINDOW) { } else if (event->type == A__NET_CLOSE_WINDOW) {
/* /*
* Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW * Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW
@ -879,7 +901,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
if (event->data.data32[0]) if (event->data.data32[0])
last_timestamp = event->data.data32[0]; last_timestamp = event->data.data32[0];
tree_close(con, KILL_WINDOW, false, false); tree_close_internal(con, KILL_WINDOW, false, false);
tree_render(); tree_render();
} else { } else {
DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window); DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window);
@ -916,8 +938,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
break; break;
} }
} else { } else {
DLOG("unhandled clientmessage\n"); DLOG("Skipping client message for unhandled type %d\n", event->type);
return;
} }
} }

View File

@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3
i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h))
i3_CFLAGS = $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS) $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) i3_CFLAGS = $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS) $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCB_CURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
i3_LIBS = $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread i3_LIBS = $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCB_CURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
# When using clang, we use pre-compiled headers to speed up the build. With # When using clang, we use pre-compiled headers to speed up the build. With
# gcc, this actually makes the build slower. # gcc, this actually makes the build slower.

View File

@ -71,6 +71,9 @@ void ipc_shutdown(void) {
current = TAILQ_FIRST(&all_clients); current = TAILQ_FIRST(&all_clients);
shutdown(current->fd, SHUT_RDWR); shutdown(current->fd, SHUT_RDWR);
close(current->fd); close(current->fd);
for (int i = 0; i < current->num_events; i++)
free(current->events[i]);
free(current->events);
TAILQ_REMOVE(&all_clients, current, clients); TAILQ_REMOVE(&all_clients, current, clients);
free(current); free(current);
} }
@ -275,9 +278,16 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
ystr("urgent"); ystr("urgent");
y(bool, con->urgent); y(bool, con->urgent);
if (con->mark != NULL) { if (!TAILQ_EMPTY(&(con->marks_head))) {
ystr("mark"); ystr("marks");
ystr(con->mark); y(array_open);
mark_t *mark;
TAILQ_FOREACH(mark, &(con->marks_head), marks) {
ystr(mark->name);
}
y(array_close);
} }
ystr("focused"); ystr("focused");
@ -365,6 +375,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
else else
y(null); y(null);
if (con->title_format != NULL) {
ystr("title_format");
ystr(con->title_format);
}
if (con->type == CT_WORKSPACE) { if (con->type == CT_WORKSPACE) {
ystr("num"); ystr("num");
y(integer, con->num); y(integer, con->num);
@ -544,6 +559,18 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
y(array_close); y(array_close);
} }
if (!TAILQ_EMPTY(&(config->tray_outputs))) {
ystr("tray_outputs");
y(array_open);
struct tray_output_t *tray_output;
TAILQ_FOREACH(tray_output, &(config->tray_outputs), tray_outputs) {
ystr(tray_output->output);
}
y(array_close);
}
#define YSTR_IF_SET(name) \ #define YSTR_IF_SET(name) \
do { \ do { \
if (config->name) { \ if (config->name) { \
@ -552,8 +579,6 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
} \ } \
} while (0) } while (0)
YSTR_IF_SET(tray_output);
ystr("tray_padding"); ystr("tray_padding");
y(integer, config->tray_padding); y(integer, config->tray_padding);
@ -586,6 +611,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
ystr("modifier"); ystr("modifier");
switch (config->modifier) { switch (config->modifier) {
case M_NONE:
ystr("none");
break;
case M_CONTROL: case M_CONTROL:
ystr("ctrl"); ystr("ctrl");
break; break;
@ -601,11 +629,6 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
case M_MOD3: case M_MOD3:
ystr("Mod3"); ystr("Mod3");
break; break;
/*
case M_MOD4:
ystr("Mod4");
break;
*/
case M_MOD5: case M_MOD5:
ystr("Mod5"); ystr("Mod5");
break; break;
@ -656,6 +679,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
YSTR_IF_SET(background); YSTR_IF_SET(background);
YSTR_IF_SET(statusline); YSTR_IF_SET(statusline);
YSTR_IF_SET(separator); YSTR_IF_SET(separator);
YSTR_IF_SET(focused_background);
YSTR_IF_SET(focused_statusline);
YSTR_IF_SET(focused_separator);
YSTR_IF_SET(focused_workspace_border); YSTR_IF_SET(focused_workspace_border);
YSTR_IF_SET(focused_workspace_bg); YSTR_IF_SET(focused_workspace_bg);
YSTR_IF_SET(focused_workspace_text); YSTR_IF_SET(focused_workspace_text);
@ -819,9 +845,12 @@ IPC_HANDLER(get_marks) {
y(array_open); y(array_open);
Con *con; Con *con;
TAILQ_FOREACH(con, &all_cons, all_cons) TAILQ_FOREACH(con, &all_cons, all_cons) {
if (con->mark != NULL) mark_t *mark;
ystr(con->mark); TAILQ_FOREACH(mark, &(con->marks_head), marks) {
ystr(mark->name);
}
}
y(array_close); y(array_close);
@ -1051,6 +1080,7 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
for (int i = 0; i < current->num_events; i++) for (int i = 0; i < current->num_events; i++)
free(current->events[i]); free(current->events[i]);
free(current->events);
/* We can call TAILQ_REMOVE because we break out of the /* We can call TAILQ_REMOVE because we break out of the
* TAILQ_FOREACH afterwards */ * TAILQ_FOREACH afterwards */
TAILQ_REMOVE(&all_clients, current, clients); TAILQ_REMOVE(&all_clients, current, clients);

View File

@ -31,9 +31,5 @@ void handle_key_press(xcb_key_press_event_t *event) {
return; return;
CommandResult *result = run_binding(bind, NULL); CommandResult *result = run_binding(bind, NULL);
if (result->needs_tree_render)
tree_render();
command_result_free(result); command_result_free(result);
} }

View File

@ -28,7 +28,9 @@ static bool parsing_deco_rect;
static bool parsing_window_rect; static bool parsing_window_rect;
static bool parsing_geometry; static bool parsing_geometry;
static bool parsing_focus; static bool parsing_focus;
static bool parsing_marks;
struct Match *current_swallow; struct Match *current_swallow;
static bool swallow_is_empty;
/* This list is used for reordering the focus stack after parsing the 'focus' /* This list is used for reordering the focus stack after parsing the 'focus'
* array. */ * array. */
@ -47,6 +49,7 @@ static int json_start_map(void *ctx) {
current_swallow = smalloc(sizeof(Match)); current_swallow = smalloc(sizeof(Match));
match_init(current_swallow); match_init(current_swallow);
TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches); TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
swallow_is_empty = true;
} else { } else {
if (!parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) { if (!parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
if (last_key && strcasecmp(last_key, "floating_nodes") == 0) { if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
@ -84,6 +87,7 @@ static int json_end_map(void *ctx) {
Match *match = TAILQ_FIRST(&(json_node->swallow_head)); Match *match = TAILQ_FIRST(&(json_node->swallow_head));
TAILQ_REMOVE(&(json_node->swallow_head), match, matches); TAILQ_REMOVE(&(json_node->swallow_head), match, matches);
match_free(match); match_free(match);
free(match);
} }
} }
@ -150,6 +154,13 @@ static int json_end_map(void *ctx) {
json_node = json_node->parent; json_node = json_node->parent;
} }
if (parsing_swallows && swallow_is_empty) {
/* We parsed an empty swallow definition. This is an invalid layout
* definition, hence we reject it. */
ELOG("Layout file is invalid: found an empty swallow definition.\n");
return 0;
}
parsing_rect = false; parsing_rect = false;
parsing_deco_rect = false; parsing_deco_rect = false;
parsing_window_rect = false; parsing_window_rect = false;
@ -159,12 +170,16 @@ static int json_end_map(void *ctx) {
static int json_end_array(void *ctx) { static int json_end_array(void *ctx) {
LOG("end of array\n"); LOG("end of array\n");
if (!parsing_swallows && !parsing_focus) { if (!parsing_swallows && !parsing_focus && !parsing_marks) {
con_fix_percent(json_node); con_fix_percent(json_node);
} }
if (parsing_swallows) { if (parsing_swallows) {
parsing_swallows = false; parsing_swallows = false;
} }
if (parsing_marks) {
parsing_marks = false;
}
if (parsing_focus) { if (parsing_focus) {
/* Clear the list of focus mappings */ /* Clear the list of focus mappings */
struct focus_mapping *mapping; struct focus_mapping *mapping;
@ -214,6 +229,9 @@ static int json_key(void *ctx, const unsigned char *val, size_t len) {
if (strcasecmp(last_key, "focus") == 0) if (strcasecmp(last_key, "focus") == 0)
parsing_focus = true; parsing_focus = true;
if (strcasecmp(last_key, "marks") == 0)
parsing_marks = true;
return 1; return 1;
} }
@ -224,20 +242,32 @@ static int json_string(void *ctx, const unsigned char *val, size_t len) {
sasprintf(&sval, "%.*s", len, val); sasprintf(&sval, "%.*s", len, val);
if (strcasecmp(last_key, "class") == 0) { if (strcasecmp(last_key, "class") == 0) {
current_swallow->class = regex_new(sval); current_swallow->class = regex_new(sval);
swallow_is_empty = false;
} else if (strcasecmp(last_key, "instance") == 0) { } else if (strcasecmp(last_key, "instance") == 0) {
current_swallow->instance = regex_new(sval); current_swallow->instance = regex_new(sval);
swallow_is_empty = false;
} else if (strcasecmp(last_key, "window_role") == 0) { } else if (strcasecmp(last_key, "window_role") == 0) {
current_swallow->window_role = regex_new(sval); current_swallow->window_role = regex_new(sval);
swallow_is_empty = false;
} else if (strcasecmp(last_key, "title") == 0) { } else if (strcasecmp(last_key, "title") == 0) {
current_swallow->title = regex_new(sval); current_swallow->title = regex_new(sval);
swallow_is_empty = false;
} else { } else {
ELOG("swallow key %s unknown\n", last_key); ELOG("swallow key %s unknown\n", last_key);
} }
free(sval); free(sval);
} else if (parsing_marks) {
char *mark;
sasprintf(&mark, "%.*s", (int)len, val);
con_mark(json_node, mark, MM_ADD);
} else { } else {
if (strcasecmp(last_key, "name") == 0) { if (strcasecmp(last_key, "name") == 0) {
json_node->name = scalloc(len + 1, 1); json_node->name = scalloc(len + 1, 1);
memcpy(json_node->name, val, len); memcpy(json_node->name, val, len);
} else if (strcasecmp(last_key, "title_format") == 0) {
json_node->title_format = scalloc(len + 1, 1);
memcpy(json_node->title_format, val, len);
} else if (strcasecmp(last_key, "sticky_group") == 0) { } else if (strcasecmp(last_key, "sticky_group") == 0) {
json_node->sticky_group = scalloc(len + 1, 1); json_node->sticky_group = scalloc(len + 1, 1);
memcpy(json_node->sticky_group, val, len); memcpy(json_node->sticky_group, val, len);
@ -336,13 +366,12 @@ static int json_string(void *ctx, const unsigned char *val, size_t len) {
LOG("Unhandled \"last_splitlayout\": %s\n", buf); LOG("Unhandled \"last_splitlayout\": %s\n", buf);
free(buf); free(buf);
} else if (strcasecmp(last_key, "mark") == 0) { } else if (strcasecmp(last_key, "mark") == 0) {
DLOG("Found deprecated key \"mark\".\n");
char *buf = NULL; char *buf = NULL;
sasprintf(&buf, "%.*s", (int)len, val); sasprintf(&buf, "%.*s", (int)len, val);
/* We unmark any containers using this mark to avoid duplicates. */ con_mark(json_node, buf, MM_REPLACE);
con_unmark(buf);
json_node->mark = buf;
} else if (strcasecmp(last_key, "floating") == 0) { } else if (strcasecmp(last_key, "floating") == 0) {
char *buf = NULL; char *buf = NULL;
sasprintf(&buf, "%.*s", (int)len, val); sasprintf(&buf, "%.*s", (int)len, val);
@ -421,12 +450,15 @@ static int json_int(void *ctx, long long val) {
if (parsing_swallows) { if (parsing_swallows) {
if (strcasecmp(last_key, "id") == 0) { if (strcasecmp(last_key, "id") == 0) {
current_swallow->id = val; current_swallow->id = val;
swallow_is_empty = false;
} }
if (strcasecmp(last_key, "dock") == 0) { if (strcasecmp(last_key, "dock") == 0) {
current_swallow->dock = val; current_swallow->dock = val;
swallow_is_empty = false;
} }
if (strcasecmp(last_key, "insert_where") == 0) { if (strcasecmp(last_key, "insert_where") == 0) {
current_swallow->insert_where = val; current_swallow->insert_where = val;
swallow_is_empty = false;
} }
} }
@ -443,8 +475,10 @@ static int json_bool(void *ctx, int val) {
json_node->sticky = val; json_node->sticky = val;
if (parsing_swallows) { if (parsing_swallows) {
if (strcasecmp(last_key, "restart_mode") == 0) if (strcasecmp(last_key, "restart_mode") == 0) {
current_swallow->restart_mode = val; current_swallow->restart_mode = val;
swallow_is_empty = false;
}
} }
return 1; return 1;
@ -589,6 +623,7 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
parsing_window_rect = false; parsing_window_rect = false;
parsing_geometry = false; parsing_geometry = false;
parsing_focus = false; parsing_focus = false;
parsing_marks = false;
setlocale(LC_NUMERIC, "C"); setlocale(LC_NUMERIC, "C");
stat = yajl_parse(hand, (const unsigned char *)buf, n); stat = yajl_parse(hand, (const unsigned char *)buf, n);
if (stat != yajl_status_ok) { if (stat != yajl_status_ok) {
@ -606,8 +641,11 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
setlocale(LC_NUMERIC, ""); setlocale(LC_NUMERIC, "");
yajl_complete_parse(hand); yajl_complete_parse(hand);
yajl_free(hand);
yajl_gen_free(g);
fclose(f); fclose(f);
free(buf);
if (to_focus) if (to_focus)
con_focus(to_focus); con_focus(to_focus);
} }

View File

@ -58,6 +58,8 @@ static char *loglastwrap;
static int logbuffer_size; static int logbuffer_size;
/* File descriptor for shm_open. */ /* File descriptor for shm_open. */
static int logbuffer_shm; static int logbuffer_shm;
/* Size (in bytes) of physical memory */
static long long physical_mem_bytes;
/* /*
* Writes the offsets for the next write and for the last wrap to the * Writes the offsets for the next write and for the last wrap to the
@ -89,6 +91,16 @@ void init_logging(void) {
} }
} }
} }
if (physical_mem_bytes == 0) {
#if defined(__APPLE__)
int mib[2] = {CTL_HW, HW_MEMSIZE};
size_t length = sizeof(long long);
sysctl(mib, 2, &physical_mem_bytes, &length, NULL, 0);
#else
physical_mem_bytes = (long long)sysconf(_SC_PHYS_PAGES) *
sysconf(_SC_PAGESIZE);
#endif
}
/* Start SHM logging if shmlog_size is > 0. shmlog_size is SHMLOG_SIZE by /* Start SHM logging if shmlog_size is > 0. shmlog_size is SHMLOG_SIZE by
* default on development versions, and 0 on release versions. If it is * default on development versions, and 0 on release versions. If it is
* not > 0, the user has turned it off, so let's close the logbuffer. */ * not > 0, the user has turned it off, so let's close the logbuffer. */
@ -108,15 +120,6 @@ void open_logbuffer(void) {
* For 512 MiB of RAM this will lead to a 5 MiB log buffer. * For 512 MiB of RAM this will lead to a 5 MiB log buffer.
* At the moment (2011-12-10), no testcase leads to an i3 log * At the moment (2011-12-10), no testcase leads to an i3 log
* of more than ~ 600 KiB. */ * of more than ~ 600 KiB. */
long long physical_mem_bytes;
#if defined(__APPLE__)
int mib[2] = {CTL_HW, HW_MEMSIZE};
size_t length = sizeof(long long);
sysctl(mib, 2, &physical_mem_bytes, &length, NULL, 0);
#else
physical_mem_bytes = (long long)sysconf(_SC_PHYS_PAGES) *
sysconf(_SC_PAGESIZE);
#endif
logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size); logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size);
#if defined(__FreeBSD__) #if defined(__FreeBSD__)
sasprintf(&shmlogname, "/tmp/i3-log-%d", getpid()); sasprintf(&shmlogname, "/tmp/i3-log-%d", getpid());
@ -172,6 +175,7 @@ void open_logbuffer(void) {
void close_logbuffer(void) { void close_logbuffer(void) {
close(logbuffer_shm); close(logbuffer_shm);
shm_unlink(shmlogname); shm_unlink(shmlogname);
free(shmlogname);
logbuffer = NULL; logbuffer = NULL;
shmlogname = ""; shmlogname = "";
} }

View File

@ -59,7 +59,7 @@ xcb_window_t root;
* pixmaps. Will use 32 bit depth and an appropriate visual, if available, * pixmaps. Will use 32 bit depth and an appropriate visual, if available,
* otherwise the root windows default (usually 24 bit TrueColor). */ * otherwise the root windows default (usually 24 bit TrueColor). */
uint8_t root_depth; uint8_t root_depth;
xcb_visualid_t visual_id; xcb_visualtype_t *visual_type;
xcb_colormap_t colormap; xcb_colormap_t colormap;
struct ev_loop *main_loop; struct ev_loop *main_loop;
@ -481,15 +481,29 @@ int main(int argc, char *argv[]) {
#include "atoms.xmacro" #include "atoms.xmacro"
#undef xmacro #undef xmacro
/* By default, we use the same depth and visual as the root window, which
* usually is TrueColor (24 bit depth) and the corresponding visual.
* However, we also check if a 32 bit depth and visual are available (for
* transparency) and use it if so. */
root_depth = root_screen->root_depth; root_depth = root_screen->root_depth;
visual_id = root_screen->root_visual;
colormap = root_screen->default_colormap; colormap = root_screen->default_colormap;
visual_type = xcb_aux_find_visual_by_attrs(root_screen, -1, 32);
if (visual_type != NULL) {
root_depth = xcb_aux_get_depth_of_visual(root_screen, visual_type->visual_id);
colormap = xcb_generate_id(conn);
DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_id); xcb_void_cookie_t cm_cookie = xcb_create_colormap_checked(conn,
XCB_COLORMAP_ALLOC_NONE,
colormap,
root,
visual_type->visual_id);
xcb_generic_error_t *error = xcb_request_check(conn, cm_cookie);
if (error != NULL) {
ELOG("Could not create colormap. Error code: %d\n", error->error_code);
exit(EXIT_FAILURE);
}
} else {
visual_type = get_visualtype(root_screen);
}
DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_type->visual_id);
DLOG("root_screen->height_in_pixels = %d, root_screen->height_in_millimeters = %d, dpi = %d\n", DLOG("root_screen->height_in_pixels = %d, root_screen->height_in_millimeters = %d, dpi = %d\n",
root_screen->height_in_pixels, root_screen->height_in_millimeters, root_screen->height_in_pixels, root_screen->height_in_millimeters,
(int)((double)root_screen->height_in_pixels * 25.4 / (double)root_screen->height_in_millimeters)); (int)((double)root_screen->height_in_pixels * 25.4 / (double)root_screen->height_in_millimeters));
@ -660,6 +674,7 @@ int main(int argc, char *argv[]) {
} }
con_focus(con_descend_focused(output_get_content(output->con))); con_focus(con_descend_focused(output_get_content(output->con)));
free(pointerreply);
} }
tree_render(); tree_render();
@ -787,6 +802,11 @@ int main(int argc, char *argv[]) {
xcb_free_pixmap(conn, pixmap); xcb_free_pixmap(conn, pixmap);
} }
#if defined(__OpenBSD__)
if (pledge("stdio rpath wpath cpath proc exec unix", NULL) == -1)
err(EXIT_FAILURE, "pledge");
#endif
struct sigaction action; struct sigaction action;
action.sa_sigaction = handle_signal; action.sa_sigaction = handle_signal;
@ -819,18 +839,28 @@ int main(int argc, char *argv[]) {
/* Autostarting exec-lines */ /* Autostarting exec-lines */
if (autostart) { if (autostart) {
struct Autostart *exec; while (!TAILQ_EMPTY(&autostarts)) {
TAILQ_FOREACH(exec, &autostarts, autostarts) { struct Autostart *exec = TAILQ_FIRST(&autostarts);
LOG("auto-starting %s\n", exec->command); LOG("auto-starting %s\n", exec->command);
start_application(exec->command, exec->no_startup_id); start_application(exec->command, exec->no_startup_id);
FREE(exec->command);
TAILQ_REMOVE(&autostarts, exec, autostarts);
FREE(exec);
} }
} }
/* Autostarting exec_always-lines */ /* Autostarting exec_always-lines */
struct Autostart *exec_always; while (!TAILQ_EMPTY(&autostarts_always)) {
TAILQ_FOREACH(exec_always, &autostarts_always, autostarts_always) { struct Autostart *exec_always = TAILQ_FIRST(&autostarts_always);
LOG("auto-starting (always!) %s\n", exec_always->command); LOG("auto-starting (always!) %s\n", exec_always->command);
start_application(exec_always->command, exec_always->no_startup_id); start_application(exec_always->command, exec_always->no_startup_id);
FREE(exec_always->command);
TAILQ_REMOVE(&autostarts_always, exec_always, autostarts_always);
FREE(exec_always);
} }
/* Start i3bar processes for all configured bars */ /* Start i3bar processes for all configured bars */

View File

@ -90,7 +90,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
utf8_title_cookie, title_cookie, utf8_title_cookie, title_cookie,
class_cookie, leader_cookie, transient_cookie, class_cookie, leader_cookie, transient_cookie,
role_cookie, startup_id_cookie, wm_hints_cookie, role_cookie, startup_id_cookie, wm_hints_cookie,
wm_normal_hints_cookie, motif_wm_hints_cookie; wm_normal_hints_cookie, motif_wm_hints_cookie, wm_user_time_cookie, wm_desktop_cookie;
geomc = xcb_get_geometry(conn, d); geomc = xcb_get_geometry(conn, d);
@ -161,6 +161,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
wm_hints_cookie = xcb_icccm_get_wm_hints(conn, window); wm_hints_cookie = xcb_icccm_get_wm_hints(conn, window);
wm_normal_hints_cookie = xcb_icccm_get_wm_normal_hints(conn, window); wm_normal_hints_cookie = xcb_icccm_get_wm_normal_hints(conn, window);
motif_wm_hints_cookie = GET_PROPERTY(A__MOTIF_WM_HINTS, 5 * sizeof(uint64_t)); motif_wm_hints_cookie = GET_PROPERTY(A__MOTIF_WM_HINTS, 5 * sizeof(uint64_t));
wm_user_time_cookie = GET_PROPERTY(A__NET_WM_USER_TIME, UINT32_MAX);
wm_desktop_cookie = GET_PROPERTY(A__NET_WM_DESKTOP, UINT32_MAX);
DLOG("Managing window 0x%08x\n", window); DLOG("Managing window 0x%08x\n", window);
@ -168,12 +170,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
cwindow->id = window; cwindow->id = window;
cwindow->depth = get_visual_depth(attr->visual); cwindow->depth = get_visual_depth(attr->visual);
/* We need to grab buttons 1-3 for click-to-focus and buttons 1-5 xcb_grab_buttons(conn, window, bindings_should_grab_scrollwheel_buttons());
* to allow for mouse bindings using --whole-window to work correctly. */
xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS,
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
XCB_BUTTON_INDEX_ANY,
XCB_BUTTON_MASK_ANY /* dont filter for any modifiers */);
/* update as much information as possible so far (some replies may be NULL) */ /* update as much information as possible so far (some replies may be NULL) */
window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true); window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true);
@ -198,6 +195,16 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
char *startup_ws = startup_workspace_for_window(cwindow, startup_id_reply); char *startup_ws = startup_workspace_for_window(cwindow, startup_id_reply);
DLOG("startup workspace = %s\n", startup_ws); DLOG("startup workspace = %s\n", startup_ws);
/* Get _NET_WM_DESKTOP if it was set. */
xcb_get_property_reply_t *wm_desktop_reply;
wm_desktop_reply = xcb_get_property_reply(conn, wm_desktop_cookie, NULL);
cwindow->wm_desktop = NET_WM_DESKTOP_NONE;
if (wm_desktop_reply != NULL && xcb_get_property_value_length(wm_desktop_reply) != 0) {
uint32_t *wm_desktops = xcb_get_property_value(wm_desktop_reply);
cwindow->wm_desktop = (int32_t)wm_desktops[0];
}
FREE(wm_desktop_reply);
/* check if the window needs WM_TAKE_FOCUS */ /* check if the window needs WM_TAKE_FOCUS */
cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS); cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS);
@ -246,7 +253,10 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
/* See if any container swallows this new window */ /* See if any container swallows this new window */
nc = con_for_window(search_at, cwindow, &match); nc = con_for_window(search_at, cwindow, &match);
const bool match_from_restart_mode = (match && match->restart_mode);
if (nc == NULL) { if (nc == NULL) {
Con *wm_desktop_ws = NULL;
/* If not, check if it is assigned to a specific workspace */ /* If not, check if it is assigned to a specific workspace */
if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE))) { if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE))) {
DLOG("Assignment matches (%p)\n", match); DLOG("Assignment matches (%p)\n", match);
@ -261,9 +271,23 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
/* set the urgency hint on the window if the workspace is not visible */ /* set the urgency hint on the window if the workspace is not visible */
if (!workspace_is_visible(assigned_ws)) if (!workspace_is_visible(assigned_ws))
urgency_hint = true; urgency_hint = true;
} else if (cwindow->wm_desktop != NET_WM_DESKTOP_NONE &&
cwindow->wm_desktop != NET_WM_DESKTOP_ALL &&
(wm_desktop_ws = ewmh_get_workspace_by_index(cwindow->wm_desktop)) != NULL) {
/* If _NET_WM_DESKTOP is set to a specific desktop, we open it
* there. Note that we ignore the special value 0xFFFFFFFF here
* since such a window will be made sticky anyway. */
DLOG("Using workspace %p / %s because _NET_WM_DESKTOP = %d.\n",
wm_desktop_ws, wm_desktop_ws->name, cwindow->wm_desktop);
nc = con_descend_tiling_focused(wm_desktop_ws);
if (nc->type == CT_WORKSPACE)
nc = tree_open_con(nc, cwindow);
else
nc = tree_open_con(nc->parent, cwindow);
} else if (startup_ws) { } else if (startup_ws) {
/* If its not assigned, but was started on a specific workspace, /* If it was started on a specific workspace, we want to open it there. */
* we want to open it there */
DLOG("Using workspace on which this application was started (%s)\n", startup_ws); DLOG("Using workspace on which this application was started (%s)\n", startup_ws);
nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL)); nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL));
DLOG("focused on ws %s: %p / %s\n", startup_ws, nc, nc->name); DLOG("focused on ws %s: %p / %s\n", startup_ws, nc, nc->name);
@ -295,6 +319,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
DLOG("Removing match %p from container %p\n", match, nc); DLOG("Removing match %p from container %p\n", match, nc);
TAILQ_REMOVE(&(nc->swallow_head), match, matches); TAILQ_REMOVE(&(nc->swallow_head), match, matches);
match_free(match); match_free(match);
FREE(match);
} }
} }
@ -308,9 +333,13 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
Match *first = TAILQ_FIRST(&(nc->swallow_head)); Match *first = TAILQ_FIRST(&(nc->swallow_head));
TAILQ_REMOVE(&(nc->swallow_head), first, matches); TAILQ_REMOVE(&(nc->swallow_head), first, matches);
match_free(first); match_free(first);
free(first);
} }
} }
} }
if (nc->window != cwindow && nc->window != NULL) {
window_free(nc->window);
}
nc->window = cwindow; nc->window = cwindow;
x_reinit(nc); x_reinit(nc);
@ -347,14 +376,17 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
Con *target_output = con_get_output(ws); Con *target_output = con_get_output(ws);
if (workspace_is_visible(ws) && current_output == target_output) { if (workspace_is_visible(ws) && current_output == target_output) {
if (!match || !match->restart_mode) { if (!match_from_restart_mode) {
set_focus = true; set_focus = true;
} else } else {
DLOG("not focusing, matched with restart_mode == true\n"); DLOG("not focusing, matched with restart_mode == true\n");
} else }
} else {
DLOG("workspace not visible, not focusing\n"); DLOG("workspace not visible, not focusing\n");
} else }
} else {
DLOG("dock, not focusing\n"); DLOG("dock, not focusing\n");
}
} else { } else {
DLOG("fs = %p, ws = %p, not focusing\n", fs, ws); DLOG("fs = %p, ws = %p, not focusing\n", fs, ws);
/* Insert the new container in focus stack *after* the currently /* Insert the new container in focus stack *after* the currently
@ -388,6 +420,12 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_STICKY)) if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_STICKY))
nc->sticky = true; nc->sticky = true;
if (cwindow->wm_desktop == NET_WM_DESKTOP_ALL) {
DLOG("This window has _NET_WM_DESKTOP = 0xFFFFFFFF. Will float it and make it sticky.\n");
nc->sticky = true;
want_floating = true;
}
FREE(state_reply); FREE(state_reply);
FREE(type_reply); FREE(type_reply);
@ -477,7 +515,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
values[0] = XCB_NONE; values[0] = XCB_NONE;
xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame, 0, 0); xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame.id, 0, 0);
if (xcb_request_check(conn, rcookie) != NULL) { if (xcb_request_check(conn, rcookie) != NULL) {
LOG("Could not reparent the window, aborting\n"); LOG("Could not reparent the window, aborting\n");
goto geom_out; goto geom_out;
@ -537,6 +575,23 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
} }
} }
if (set_focus) {
DLOG("Checking con = %p for _NET_WM_USER_TIME.\n", nc);
uint32_t *wm_user_time;
xcb_get_property_reply_t *wm_user_time_reply = xcb_get_property_reply(conn, wm_user_time_cookie, NULL);
if (wm_user_time_reply != NULL && xcb_get_property_value_length(wm_user_time_reply) != 0 &&
(wm_user_time = xcb_get_property_value(wm_user_time_reply)) &&
wm_user_time[0] == 0) {
DLOG("_NET_WM_USER_TIME set to 0, not focusing con = %p.\n", nc);
set_focus = false;
}
FREE(wm_user_time_reply);
} else {
xcb_discard_reply(conn, wm_user_time_cookie.sequence);
}
/* Defer setting focus after the 'new' event has been sent to ensure the /* Defer setting focus after the 'new' event has been sent to ensure the
* proper window event sequence. */ * proper window event sequence. */
if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) { if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) {
@ -552,6 +607,13 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
* needs to be on the final workspace first. */ * needs to be on the final workspace first. */
con_set_urgency(nc, urgency_hint); con_set_urgency(nc, urgency_hint);
/* Update _NET_WM_DESKTOP. We invalidate the cached value first to force an update. */
cwindow->wm_desktop = NET_WM_DESKTOP_NONE;
ewmh_update_wm_desktop();
/* If a sticky window was mapped onto another workspace, make sure to pop it to the front. */
output_push_sticky_windows(focused);
geom_out: geom_out:
free(geom); free(geom);
out: out:

Some files were not shown because too many files have changed in this diff Show More