Merge branch 'next' into master
This commit is contained in:
commit
988cc3ccaf
18
.github/ISSUE_TEMPLATE.md
vendored
Normal file
18
.github/ISSUE_TEMPLATE.md
vendored
Normal 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. "I’m 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"_
|
83
.travis.yml
83
.travis.yml
@ -1,72 +1,23 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
services:
|
||||
- docker
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- llvm-toolchain-precise-3.5
|
||||
# ubuntu-toolchain-r-test contains libstdc++6 >= 4.8 which libllvm3.5 needs.
|
||||
- ubuntu-toolchain-r-test
|
||||
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
|
||||
env:
|
||||
global:
|
||||
- BASENAME="i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh)"
|
||||
- secure: "B5IICA8MPx/FKaB50rTPqL8P1NU+Q0yuWl+lElL4+a9xSyLikfm3NzUPHoVwx8lNw2AVK6br7p0OmF7vMFjqAgrgc1cajTtEae5uFRKNUrWLpXM046YgNEYLLIHsQOjInxE+S4O6EFVzsUqsu8aeo2Xhq4sm4iUocG7e5isYgYo=" # DOCKER_PASS
|
||||
- secure: "EIvrq8PG7lRjidppG0RCv4F0X4GP3DT9F5+ixVuGPfhK/hZT3jYC2AVY9G+NnUcXVwQEpW92rlqpftQ/qZ13FoyWokC8ZyoyD06fr5FPCfoFF3OczZwAJzZYkObI/hE9+/hXcylx/Os6N4INd2My1ntGk3JPsWL9riopod5EjSg=" # DOCKER_EMAIL
|
||||
- secure: "hvhBunS4xXTgnIOsk/BPT7I7FrJhvVwCSt5PfxxvMqNaztOJI9BuK7ZrZ5Cy38KyHwlh3VHAH5AaCygJcPauoSQCV3bpnlbaWn3ruq2F0Q697Q5uNf73liXzyUqb9/Zvfvge4y4WWOhP5tVz1C6ZBe/NfhU7pqKLMA+6ads+99c=" # DOCKER_USER
|
||||
install:
|
||||
- sudo mk-build-deps --install --remove --tool 'apt-get --no-install-recommends' debian/control
|
||||
# Install as many dependencies as possible via apt because cpanm is not very reliable/easy to debug.
|
||||
- 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'
|
||||
- if [ -a .git/shallow ]; then git fetch --unshallow; fi
|
||||
- docker pull ${BASENAME} || ./travis/docker-build-and-push.sh
|
||||
script:
|
||||
- CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror" make -j
|
||||
- (cd testcases && xvfb-run ./complete-run.pl --parallel=1 || (cat latest/complete-run.log; false))
|
||||
- clang-format-3.5 -i $(find . -name "*.[ch]" | tr '\n' ' ') && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false)
|
||||
- |
|
||||
funcs='malloc|calloc|realloc|strdup|strndup|asprintf|write'
|
||||
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
|
||||
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-safe-wrappers.sh
|
||||
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-formatting.sh
|
||||
- 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
|
||||
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/run-tests.sh
|
||||
|
2
DEPENDS
2
DEPENDS
@ -24,7 +24,7 @@
|
||||
│ PCRE │ 8.12 │ 8.35 │ http://www.pcre.org/ │
|
||||
│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
|
||||
│ 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
|
||||
² Pod::Simple is a Perl module required for converting the testsuite
|
||||
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright © 2009-2011, Michael Stapelberg and contributors
|
||||
Copyright © 2009, Michael Stapelberg and contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -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
|
||||
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:
|
||||
(#i3 on irc.twice-irc.de).
|
||||
|
||||
|
@ -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 it’s 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 isn’t possible
|
||||
• docs/userguide: make syntax of syntax descriptions consistent
|
||||
• docs/userguide: recommend “exec exec” for correct signal handling
|
||||
• docs/userguide: explain i3-config-wizard’s 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
132
RELEASE-NOTES-4.12
Normal 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_TIME’s 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
|
||||
• don’t 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
|
17
common.mk
17
common.mk
@ -1,5 +1,6 @@
|
||||
UNAME=$(shell uname)
|
||||
DEBUG=1
|
||||
ASAN=0
|
||||
INSTALL=install
|
||||
LN=ln
|
||||
PKG_CONFIG=pkg-config
|
||||
@ -42,6 +43,11 @@ else
|
||||
CFLAGS ?= -pipe -O2 -freorder-blocks-and-partition
|
||||
endif
|
||||
|
||||
ifeq ($(ASAN),1)
|
||||
CFLAGS += -fsanitize=address -DI3_ASAN_ENABLED
|
||||
LDFLAGS += -fsanitize=address
|
||||
endif
|
||||
|
||||
# Default LDFLAGS that users should be able to override
|
||||
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-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_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon)
|
||||
XKB_COMMON_X11_CFLAGS := $(call cflags_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_CFLAGS := $(call cflags_for_lib, 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, pangocairo)
|
||||
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, pangocairo)
|
||||
|
||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -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
|
||||
|
||||
* New upstream release.
|
||||
|
2
debian/compat
vendored
2
debian/compat
vendored
@ -1 +1 @@
|
||||
7
|
||||
9
|
||||
|
4
debian/control
vendored
4
debian/control
vendored
@ -2,7 +2,7 @@ Source: i3-wm
|
||||
Section: x11
|
||||
Priority: extra
|
||||
Maintainer: Michael Stapelberg <stapelberg@debian.org>
|
||||
Build-Depends: debhelper (>= 7.0.50~),
|
||||
Build-Depends: debhelper (>= 9),
|
||||
libx11-dev,
|
||||
libxcb-util0-dev (>= 0.3.8),
|
||||
libxcb-keysyms1-dev,
|
||||
@ -24,7 +24,7 @@ Build-Depends: debhelper (>= 7.0.50~),
|
||||
libcairo2-dev,
|
||||
libpango1.0-dev,
|
||||
libpod-simple-perl
|
||||
Standards-Version: 3.9.5
|
||||
Standards-Version: 3.9.7
|
||||
Homepage: http://i3wm.org/
|
||||
|
||||
Package: i3
|
||||
|
65
debian/copyright
vendored
65
debian/copyright
vendored
@ -1,30 +1,41 @@
|
||||
This Debian package is based on a tarball downloaded from
|
||||
http://i3wm.org/
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
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>
|
||||
All rights reserved.
|
||||
Files: *
|
||||
Copyright: 2009 Michael Stapelberg
|
||||
License: BSD-3-clause
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
Files: debian/*
|
||||
Copyright: 2009 Michael Stapelberg
|
||||
License: BSD-3-clause
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
* 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
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
||||
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.
|
||||
.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
.
|
||||
* 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 specific prior written permission.
|
||||
.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
2
debian/i3-wm.install
vendored
Normal file
2
debian/i3-wm.install
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
etc
|
||||
usr
|
42
debian/rules
vendored
42
debian/rules
vendored
@ -1,47 +1,17 @@
|
||||
#!/usr/bin/make -f
|
||||
# vi: ts=8 sw=8 noet
|
||||
|
||||
DPKG_EXPORT_BUILDFLAGS = 1
|
||||
-include /usr/share/dpkg/buildflags.mk
|
||||
|
||||
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
|
||||
export V:=1
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
override_dh_auto_build:
|
||||
$(MAKE)
|
||||
$(MAKE) -C man
|
||||
$(MAKE) -C docs
|
||||
dh_auto_build -- all docs mans
|
||||
|
||||
override_dh_installchangelogs:
|
||||
dh_installchangelogs RELEASE-NOTES-*
|
||||
|
||||
override_dh_install:
|
||||
$(MAKE) DESTDIR=$(CURDIR)/debian/i3-wm/ install
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=i3-wm-dbg
|
||||
|
||||
%:
|
||||
dh $@ --parallel
|
||||
|
56
debian/upstream/signing-key.asc
vendored
Normal file
56
debian/upstream/signing-key.asc
vendored
Normal 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
3
debian/watch
vendored
@ -1,2 +1,3 @@
|
||||
version=3
|
||||
http://i3wm.org/downloads/ /downloads/i3-(.*)\.tar\.bz2
|
||||
opts=pgpsigurlmangle=s/$/.asc/ \
|
||||
https://i3wm.org/downloads/ /downloads/i3-(.*)\.tar\.bz2
|
||||
|
@ -117,7 +117,7 @@ containers, searching containers, getting specific properties from containers,
|
||||
|
||||
src/config.c::
|
||||
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::
|
||||
Contains debugging functions to print unhandled X events.
|
||||
|
@ -136,6 +136,10 @@ color::
|
||||
when it is associated.
|
||||
Colors are specified in hex (like in HTML), starting with a leading
|
||||
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::
|
||||
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
|
||||
@ -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)",
|
||||
"short_text": "10.0.0.1",
|
||||
"color": "#00ff00",
|
||||
"background": "#1c1c1c",
|
||||
"border": "#ee0000",
|
||||
"min_width": 300,
|
||||
"align": "right",
|
||||
"urgent": false,
|
||||
|
18
docs/ipc
18
docs/ipc
@ -520,6 +520,14 @@ statusline::
|
||||
Text color to be used for the statusline.
|
||||
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::
|
||||
Text/background/border color for a workspace button when the workspace
|
||||
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
|
||||
+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
|
||||
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:*
|
||||
---------------------------
|
||||
{ "change": "default" }
|
||||
{
|
||||
"change": "default",
|
||||
"pango_markup": true
|
||||
}
|
||||
---------------------------
|
||||
|
||||
=== window event
|
||||
@ -814,6 +826,8 @@ know):
|
||||
C::
|
||||
* i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
|
||||
* https://github.com/acrisci/i3ipc-glib
|
||||
C++::
|
||||
* https://github.com/drmgc/i3ipcpp
|
||||
Go::
|
||||
* https://github.com/proxypoke/i3ipc
|
||||
JavaScript::
|
||||
|
175
docs/userguide
175
docs/userguide
@ -4,8 +4,9 @@ Michael Stapelberg <michael@i3wm.org>
|
||||
March 2013
|
||||
|
||||
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
|
||||
contact us on IRC (preferred) or post your question(s) on the mailing list.
|
||||
window manager. If it does not, please check https://www.reddit.com/r/i3wm/
|
||||
first, then contact us on IRC (preferred) or post your question(s) on the
|
||||
mailing list.
|
||||
|
||||
== 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
|
||||
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
|
||||
|
||||
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
|
||||
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)
|
||||
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*:
|
||||
---------------------------------------
|
||||
@ -835,9 +837,9 @@ workspace "2: vim" output VGA1
|
||||
You can change all colors which i3 uses to draw the window decorations.
|
||||
|
||||
*Syntax*:
|
||||
------------------------------------------------------
|
||||
<colorclass> <border> <background> <text> <indicator>
|
||||
------------------------------------------------------
|
||||
--------------------------------------------------------------------
|
||||
<colorclass> <border> <background> <text> <indicator> <child_border>
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Where colorclass can be one of:
|
||||
|
||||
@ -862,20 +864,20 @@ client.background::
|
||||
Colors are in HTML hex format (#rrggbb), see the following example:
|
||||
|
||||
*Examples (default colors)*:
|
||||
---------------------------------------------------------
|
||||
# class border backgr. text indicator
|
||||
client.focused #4c7899 #285577 #ffffff #2e9ef4
|
||||
client.focused_inactive #333333 #5f676a #ffffff #484e50
|
||||
client.unfocused #333333 #222222 #888888 #292d2e
|
||||
client.urgent #2f343a #900000 #ffffff #900000
|
||||
client.placeholder #000000 #0c0c0c #ffffff #000000
|
||||
----------------------------------------------------------------------
|
||||
# class border backgr. text indicator child_border
|
||||
client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
|
||||
client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
|
||||
client.unfocused #333333 #222222 #888888 #292d2e #222222
|
||||
client.urgent #2f343a #900000 #ffffff #900000 #900000
|
||||
client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
|
||||
|
||||
client.background #ffffff
|
||||
---------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
|
||||
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
|
||||
the window.
|
||||
"child_border", and "border" color is only the two thin lines around the
|
||||
titlebar.
|
||||
|
||||
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
|
||||
@ -1236,7 +1238,7 @@ the windows key). The default value for the hidden_state is hide.
|
||||
-------------------------
|
||||
mode dock|hide|invisible
|
||||
hidden_state hide|show
|
||||
modifier <Modifier>
|
||||
modifier <Modifier>|none
|
||||
------------------------
|
||||
|
||||
*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
|
||||
|
||||
@ -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 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*:
|
||||
---------------------------------
|
||||
tray_output none|primary|<output>
|
||||
@ -1529,6 +1537,15 @@ statusline::
|
||||
Text color to be used for the statusline.
|
||||
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::
|
||||
Border, background and text color for a workspace button when the workspace
|
||||
has focus.
|
||||
@ -1643,7 +1660,7 @@ window_role::
|
||||
window_type::
|
||||
Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are
|
||||
+normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+,
|
||||
+popup_menu+ and +tooltip+.
|
||||
+popup_menu+, +tooltip+ and +notification+.
|
||||
id::
|
||||
Compares the X11 window ID, which you can get via +xwininfo+ for example.
|
||||
title::
|
||||
@ -1659,10 +1676,13 @@ workspace::
|
||||
the special value +\_\_focused__+ to match all windows in the currently
|
||||
focused workspace.
|
||||
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::
|
||||
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
|
||||
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)
|
||||
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*:
|
||||
--------------------------------
|
||||
@ -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
|
||||
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
|
||||
|
||||
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,
|
||||
nothing will happen. If you use a different orientation, the split container’s
|
||||
orientation will be changed (if it does not have more than one window). Use
|
||||
+layout toggle split+ to change the layout of any split container from splitv
|
||||
to splith or vice-versa.
|
||||
orientation will be changed (if it does not have more than one window).
|
||||
The +toggle+ option will toggle the orientation of the split container if it
|
||||
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*:
|
||||
-------------------------
|
||||
split vertical|horizontal
|
||||
-------------------------
|
||||
--------------------------------
|
||||
split vertical|horizontal|toggle
|
||||
--------------------------------
|
||||
|
||||
*Example*:
|
||||
------------------------------
|
||||
-------------------------------
|
||||
bindsym $mod+v split vertical
|
||||
bindsym $mod+h split horizontal
|
||||
------------------------------
|
||||
bindsym $mod+t split toggle
|
||||
-------------------------------
|
||||
|
||||
=== Manipulating layout
|
||||
|
||||
@ -1881,8 +1926,11 @@ for_window [instance=notepad] sticky enable
|
||||
=== Changing (named) workspaces/moving to workspaces
|
||||
|
||||
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
|
||||
+move container to workspace+.
|
||||
number or name of the workspace. Pass the optional flag
|
||||
+--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
|
||||
+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+
|
||||
(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
|
||||
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+.
|
||||
|
||||
*Syntax*:
|
||||
-----------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
workspace next|prev|next_on_output|prev_on_output
|
||||
workspace back_and_forth
|
||||
workspace <name>
|
||||
workspace number <name>
|
||||
workspace [--no-auto-back-and-forth] <name>
|
||||
workspace [--no-auto-back-and-forth] number <name>
|
||||
|
||||
move [window|container] [to] workspace <name>
|
||||
move [window|container] [to] workspace number <name>
|
||||
move [--no-auto-back-and-forth] [window|container] [to] workspace <name>
|
||||
move [--no-auto-back-and-forth] [window|container] [to] workspace number <name>
|
||||
move [window|container] [to] workspace prev|next|current
|
||||
-----------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
*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.
|
||||
|
||||
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
|
||||
another mark.
|
||||
this mark or add it otherwise. Note that you may need to use this in
|
||||
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.
|
||||
|
||||
*Syntax*:
|
||||
------------------------------
|
||||
mark [--toggle] <identifier>
|
||||
----------------------------------------------
|
||||
mark [--add|--replace] [--toggle] <identifier>
|
||||
[con_mark="identifier"] focus
|
||||
unmark <identifier>
|
||||
------------------------------
|
||||
----------------------------------------------
|
||||
|
||||
*Example (in a terminal)*:
|
||||
------------------------------
|
||||
$ i3-msg mark irssi
|
||||
$ i3-msg '[con_mark="irssi"] focus'
|
||||
$ i3-msg unmark irssi
|
||||
------------------------------
|
||||
---------------------------------------------------------
|
||||
# marks the focused container
|
||||
mark 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
|
||||
@ -2153,7 +2217,10 @@ https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup]
|
||||
and the following placeholders which will be replaced:
|
||||
|
||||
+%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+::
|
||||
The X11 window class (second part of WM_CLASS). This corresponds to the
|
||||
+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.
|
||||
|
||||
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*:
|
||||
-----------------------------------------------
|
||||
border normal|pixel [<n>]
|
||||
|
@ -109,7 +109,7 @@ for my $line (@lines) {
|
||||
# Second step: Generate the enum values for all states.
|
||||
|
||||
# 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 state’s name is included in another one’s (like FOR_WINDOW
|
||||
# is in FOR_WINDOW_COMMAND).
|
||||
my @keys = sort { (length($b) <=> length($a)) or ($a cmp $b) } keys %states;
|
||||
|
@ -479,7 +479,7 @@ static int handle_expose() {
|
||||
|
||||
if (current_step == STEP_WELCOME) {
|
||||
/* 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), 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");
|
||||
|
||||
/* 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>");
|
||||
|
||||
/* 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>");
|
||||
}
|
||||
|
||||
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(85), 4, "Win as default modifier");
|
||||
@ -519,7 +519,7 @@ static int handle_expose() {
|
||||
|
||||
/* the selected modifier */
|
||||
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)
|
||||
txt(logical_px(10), 4, "-> <Win>");
|
||||
else
|
||||
@ -527,11 +527,11 @@ static int handle_expose() {
|
||||
|
||||
/* green */
|
||||
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>");
|
||||
|
||||
/* 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>");
|
||||
}
|
||||
|
||||
|
@ -138,6 +138,7 @@ int main(int argc, char *argv[]) {
|
||||
if (verbose)
|
||||
printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n",
|
||||
header->offset_next_write, header->offset_last_wrap, header->size, shmname);
|
||||
free(shmname);
|
||||
walk = logbuffer + header->offset_next_write;
|
||||
|
||||
/* We first need to print old content in case there was at least one
|
||||
|
@ -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);
|
||||
|
||||
/* 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 … */
|
||||
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 */
|
||||
if (input_position > 0) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -176,14 +176,14 @@ static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_rel
|
||||
static void finish_input() {
|
||||
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 len = strlen(format);
|
||||
int cnt = 0;
|
||||
for (c = 0; c < (len - 1); c++)
|
||||
if (format[c] == '%' && format[c + 1] == 's')
|
||||
cnt++;
|
||||
printf("occurences = %d\n", cnt);
|
||||
printf("occurrences = %d\n", cnt);
|
||||
|
||||
/* allocate space for the output */
|
||||
int inputlen = strlen(command);
|
||||
|
@ -119,6 +119,10 @@ static yajl_callbacks reply_callbacks = {
|
||||
};
|
||||
|
||||
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");
|
||||
if (env_socket_path)
|
||||
socket_path = sstrdup(env_socket_path);
|
||||
|
@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-nagbar
|
||||
|
||||
i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c)
|
||||
i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h)
|
||||
i3_nagbar_CFLAGS = $(XCB_CFLAGS) $(XCB_WM_CFLAGS) $(PANGO_CFLAGS)
|
||||
i3_nagbar_LIBS = $(XCB_LIBS) $(XCB_WM_LIBS) $(PANGO_LIBS)
|
||||
i3_nagbar_CFLAGS = $(XCB_CFLAGS) $(XCB_CURSOR_CFLAGS) $(XCB_WM_CFLAGS) $(PANGO_CFLAGS)
|
||||
i3_nagbar_LIBS = $(XCB_LIBS) $(XCB_CURSOR_LIBS) $(XCB_WM_LIBS) $(PANGO_LIBS)
|
||||
|
||||
i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o)
|
||||
|
||||
|
@ -28,10 +28,15 @@
|
||||
#include <xcb/xcb_aux.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/xcb_cursor.h>
|
||||
|
||||
#include "libi3.h"
|
||||
#include "i3-nagbar.h"
|
||||
|
||||
/** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a
|
||||
* constant for that. */
|
||||
#define XCB_CURSOR_LEFT_PTR 68
|
||||
|
||||
static char *argv0 = NULL;
|
||||
|
||||
typedef struct {
|
||||
@ -51,11 +56,11 @@ static button_t *buttons;
|
||||
static int buttoncnt;
|
||||
|
||||
/* Result of get_colorpixel() for the various colors. */
|
||||
static uint32_t color_background; /* background of the bar */
|
||||
static uint32_t color_button_background; /* background for buttons */
|
||||
static uint32_t color_border; /* color of the button border */
|
||||
static uint32_t color_border_bottom; /* color of the bottom border */
|
||||
static uint32_t color_text; /* color of the text */
|
||||
static color_t color_background; /* background of the bar */
|
||||
static color_t color_button_background; /* background for buttons */
|
||||
static color_t color_border; /* color of the button border */
|
||||
static color_t color_border_bottom; /* color of the bottom border */
|
||||
static color_t color_text; /* color of the text */
|
||||
|
||||
xcb_window_t root;
|
||||
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) {
|
||||
/* 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);
|
||||
|
||||
/* restore font color */
|
||||
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),
|
||||
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);
|
||||
int y = rect.width;
|
||||
uint32_t values[3];
|
||||
values[0] = color_button_background;
|
||||
values[0] = color_button_background.colorpixel;
|
||||
values[1] = line_width;
|
||||
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_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[] = {
|
||||
{y - w - (2 * line_width), 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 */
|
||||
w += logical_px(12);
|
||||
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)};
|
||||
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].width = w;
|
||||
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)}};
|
||||
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2);
|
||||
|
||||
values[0] = color_text;
|
||||
values[1] = color_button_background;
|
||||
values[0] = color_text.colorpixel;
|
||||
values[1] = color_button_background.colorpixel;
|
||||
set_font_colors(pixmap_gc, color_text, color_button_background);
|
||||
/* 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),
|
||||
logical_px(4) + logical_px(3),
|
||||
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 */
|
||||
line_width = logical_px(2);
|
||||
values[0] = color_border_bottom;
|
||||
values[0] = color_border_bottom.colorpixel;
|
||||
values[1] = line_width;
|
||||
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
|
||||
xcb_point_t bottom[] = {
|
||||
@ -448,25 +453,49 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (bar_type == TYPE_ERROR) {
|
||||
/* Red theme for error messages */
|
||||
color_button_background = get_colorpixel("#680a0a");
|
||||
color_background = get_colorpixel("#900000");
|
||||
color_text = get_colorpixel("#ffffff");
|
||||
color_border = get_colorpixel("#d92424");
|
||||
color_border_bottom = get_colorpixel("#470909");
|
||||
color_button_background = draw_util_hex_to_color("#680a0a");
|
||||
color_background = draw_util_hex_to_color("#900000");
|
||||
color_text = draw_util_hex_to_color("#ffffff");
|
||||
color_border = draw_util_hex_to_color("#d92424");
|
||||
color_border_bottom = draw_util_hex_to_color("#470909");
|
||||
} else {
|
||||
/* Yellowish theme for warnings */
|
||||
color_button_background = get_colorpixel("#ffc100");
|
||||
color_background = get_colorpixel("#ffa8000");
|
||||
color_text = get_colorpixel("#000000");
|
||||
color_border = get_colorpixel("#ab7100");
|
||||
color_border_bottom = get_colorpixel("#ab7100");
|
||||
color_button_background = draw_util_hex_to_color("#ffc100");
|
||||
color_background = draw_util_hex_to_color("#ffa8000");
|
||||
color_text = draw_util_hex_to_color("#000000");
|
||||
color_border = draw_util_hex_to_color("#ab7100");
|
||||
color_border_bottom = draw_util_hex_to_color("#ab7100");
|
||||
}
|
||||
|
||||
font = load_font(pattern, true);
|
||||
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_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 */
|
||||
win = xcb_generate_id(conn);
|
||||
|
||||
@ -479,13 +508,14 @@ int main(int argc, char *argv[]) {
|
||||
0, /* x11 border = 0, we draw our own */
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
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[]){
|
||||
0, /* back pixel: black */
|
||||
XCB_EVENT_MASK_EXPOSURE |
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
|
||||
XCB_EVENT_MASK_BUTTON_PRESS |
|
||||
XCB_EVENT_MASK_BUTTON_RELEASE});
|
||||
XCB_EVENT_MASK_BUTTON_RELEASE,
|
||||
cursor});
|
||||
|
||||
/* Map the window (make it visible) */
|
||||
xcb_map_window(conn, win);
|
||||
|
@ -9,8 +9,8 @@
|
||||
# mechanism to find the preferred editor
|
||||
|
||||
# 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
|
||||
if command -v $editor > /dev/null 2>&1; then
|
||||
exec $editor "$@"
|
||||
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
|
||||
exec "$editor" "$@"
|
||||
fi
|
||||
done
|
||||
|
@ -11,8 +11,8 @@
|
||||
# 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.
|
||||
# Worst case scenario we'll open the file in your editor.
|
||||
for pager in $PAGER less most w3m pg i3-sensible-editor; do
|
||||
if command -v $pager > /dev/null 2>&1; then
|
||||
exec $pager "$@"
|
||||
for pager in "$PAGER" less most w3m pg i3-sensible-editor; do
|
||||
if command -v "$pager" > /dev/null 2>&1; then
|
||||
exec "$pager" "$@"
|
||||
fi
|
||||
done
|
||||
|
@ -8,9 +8,9 @@
|
||||
# We welcome patches that add distribution-specific mechanisms to find the
|
||||
# preferred terminal emulator. On Debian, there is the x-terminal-emulator
|
||||
# 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
|
||||
if command -v $terminal > /dev/null 2>&1; then
|
||||
exec $terminal "$@"
|
||||
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
|
||||
exec "$terminal" "$@"
|
||||
fi
|
||||
done
|
||||
|
||||
|
@ -165,7 +165,6 @@ bindsym Mod1+r mode "resize"
|
||||
# finds out, if available)
|
||||
bar {
|
||||
status_command i3status
|
||||
tray_output primary
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
@ -152,5 +152,4 @@ bindcode $mod+27 mode "resize"
|
||||
# finds out, if available)
|
||||
bar {
|
||||
status_command i3status
|
||||
tray_output primary
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar
|
||||
|
||||
i3bar_SOURCES := $(wildcard i3bar/src/*.c)
|
||||
i3bar_HEADERS := $(wildcard i3bar/include/*.h)
|
||||
i3bar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
|
||||
i3bar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS)
|
||||
i3bar_CFLAGS = $(XCB_CFLAGS) $(XCB_CURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
|
||||
i3bar_LIBS = $(XCB_LIBS) $(XCB_CURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS)
|
||||
|
||||
i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)
|
||||
|
||||
|
@ -31,6 +31,14 @@ typedef enum {
|
||||
ALIGN_RIGHT
|
||||
} 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
|
||||
* up one status line. */
|
||||
struct status_block {
|
||||
@ -38,6 +46,8 @@ struct status_block {
|
||||
i3String *short_text;
|
||||
|
||||
char *color;
|
||||
char *background;
|
||||
char *border;
|
||||
|
||||
/* 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
|
||||
@ -49,16 +59,14 @@ struct status_block {
|
||||
|
||||
bool urgent;
|
||||
bool no_separator;
|
||||
bool is_markup;
|
||||
bool pango_markup;
|
||||
|
||||
/* The amount of pixels necessary to render a separater after the block. */
|
||||
uint32_t sep_block_width;
|
||||
|
||||
/* The amount of pixels necessary to render this block. These variables are
|
||||
* only temporarily used in refresh_statusline(). */
|
||||
uint32_t width;
|
||||
uint32_t x_offset;
|
||||
uint32_t x_append;
|
||||
/* Continuously-updated information on how to render this status block. */
|
||||
struct status_block_render_desc full_render;
|
||||
struct status_block_render_desc short_render;
|
||||
|
||||
/* Optional */
|
||||
char *name;
|
||||
|
@ -29,6 +29,12 @@ typedef struct binding_t {
|
||||
TAILQ_ENTRY(binding_t) bindings;
|
||||
} binding_t;
|
||||
|
||||
typedef struct tray_output_t {
|
||||
char *output;
|
||||
|
||||
TAILQ_ENTRY(tray_output_t) tray_outputs;
|
||||
} tray_output_t;
|
||||
|
||||
typedef struct config_t {
|
||||
int modifier;
|
||||
TAILQ_HEAD(bindings_head, binding_t) bindings;
|
||||
@ -42,7 +48,7 @@ typedef struct config_t {
|
||||
char *command;
|
||||
char *fontname;
|
||||
i3String *separator_symbol;
|
||||
char *tray_output;
|
||||
TAILQ_HEAD(tray_outputs_head, tray_output_t) tray_outputs;
|
||||
int tray_padding;
|
||||
int num_outputs;
|
||||
char **outputs;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <cairo/cairo-xcb.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@ -36,6 +37,12 @@ void init_outputs(void);
|
||||
*/
|
||||
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 {
|
||||
char* name; /* Name of the output */
|
||||
bool active; /* If the output is active */
|
||||
@ -44,9 +51,16 @@ struct i3_output {
|
||||
int ws; /* The number of the currently visible ws */
|
||||
rect rect; /* The rect (relative to the root window) */
|
||||
|
||||
xcb_window_t bar; /* The id of the bar of the output */
|
||||
xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */
|
||||
xcb_gcontext_t bargc; /* The graphical context of the bar */
|
||||
/* Off-screen buffer for preliminary rendering of the bar. */
|
||||
surface_t buffer;
|
||||
/* 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 tc_head* trayclients; /* The tray clients on this output */
|
||||
|
@ -15,7 +15,7 @@
|
||||
#undef MIN
|
||||
#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 */
|
||||
#define FREE(p) \
|
||||
|
@ -24,10 +24,17 @@
|
||||
#define XEMBED_MAPPED (1 << 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 {
|
||||
char *bar_fg;
|
||||
char *bar_bg;
|
||||
char *sep_fg;
|
||||
char *focus_bar_fg;
|
||||
char *focus_bar_bg;
|
||||
char *focus_sep_fg;
|
||||
char *active_ws_fg;
|
||||
char *active_ws_bg;
|
||||
char *active_ws_border;
|
||||
|
@ -75,6 +75,8 @@ static void clear_statusline(struct statusline_head *head, bool free_resources)
|
||||
FREE(first->name);
|
||||
FREE(first->instance);
|
||||
FREE(first->min_width_str);
|
||||
FREE(first->background);
|
||||
FREE(first->border);
|
||||
}
|
||||
|
||||
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);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
|
||||
char *copy = (char *)smalloc(len + 1);
|
||||
strncpy(copy, (const char *)val, len);
|
||||
copy[len] = 0;
|
||||
ctx->block.min_width_str = copy;
|
||||
sasprintf(&(ctx->block.min_width_str), "%.*s", len, val);
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "name") == 0) {
|
||||
char *copy = (char *)smalloc(len + 1);
|
||||
strncpy(copy, (const char *)val, len);
|
||||
copy[len] = 0;
|
||||
ctx->block.name = copy;
|
||||
sasprintf(&(ctx->block.name), "%.*s", len, val);
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(ctx->last_map_key, "instance") == 0) {
|
||||
char *copy = (char *)smalloc(len + 1);
|
||||
strncpy(copy, (const char *)val, len);
|
||||
copy[len] = 0;
|
||||
ctx->block.instance = copy;
|
||||
sasprintf(&(ctx->block.instance), "%.*s", len, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -275,15 +276,15 @@ static int stdin_end_map(void *context) {
|
||||
|
||||
if (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);
|
||||
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)
|
||||
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);
|
||||
return 1;
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
static char *cur_key;
|
||||
static bool parsing_bindings;
|
||||
static bool parsing_tray_outputs;
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
FREE(cur_key);
|
||||
sasprintf(&(cur_key), "%.*s", keyLen, keyVal);
|
||||
|
||||
cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
|
||||
strncpy(cur_key, (const char *)keyVal, keyLen);
|
||||
cur_key[keyLen] = '\0';
|
||||
|
||||
if (strcmp(cur_key, "bindings") == 0)
|
||||
if (strcmp(cur_key, "bindings") == 0) {
|
||||
parsing_bindings = true;
|
||||
}
|
||||
|
||||
if (strcmp(cur_key, "tray_outputs") == 0) {
|
||||
parsing_tray_outputs = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int config_end_array_cb(void *params_) {
|
||||
parsing_bindings = false;
|
||||
parsing_tray_outputs = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -93,6 +97,14 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
|
||||
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")) {
|
||||
DLOG("mode = %.*s, len = %d\n", len, val, len);
|
||||
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")) {
|
||||
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"))) {
|
||||
config.modifier = ShiftMask;
|
||||
return 1;
|
||||
@ -128,16 +145,12 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
|
||||
case '3':
|
||||
config.modifier = Mod3Mask;
|
||||
return 1;
|
||||
/*
|
||||
case '4':
|
||||
config.modifier = Mod4Mask;
|
||||
return 1;
|
||||
*/
|
||||
case '5':
|
||||
config.modifier = Mod5Mask;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
config.modifier = Mod4Mask;
|
||||
return 1;
|
||||
}
|
||||
@ -198,10 +211,13 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* We keep the old single tray_output working for users who only restart i3bar
|
||||
* after updating. */
|
||||
if (!strcmp(cur_key, "tray_output")) {
|
||||
DLOG("tray_output %.*s\n", len, val);
|
||||
FREE(config.tray_output);
|
||||
sasprintf(&config.tray_output, "%.*s", len, val);
|
||||
DLOG("Found deprecated key tray_output %.*s.\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;
|
||||
}
|
||||
|
||||
@ -217,6 +233,9 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
|
||||
COLOR(statusline, bar_fg);
|
||||
COLOR(background, bar_bg);
|
||||
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_bg, focus_ws_bg);
|
||||
COLOR(focused_workspace_text, focus_ws_fg);
|
||||
@ -317,6 +336,7 @@ void parse_config_json(char *json) {
|
||||
handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
|
||||
|
||||
TAILQ_INIT(&(config.bindings));
|
||||
TAILQ_INIT(&(config.tray_outputs));
|
||||
|
||||
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_bg);
|
||||
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_bg);
|
||||
FREE_COLOR(active_ws_border);
|
||||
|
@ -17,6 +17,9 @@
|
||||
#include <sys/un.h>
|
||||
#include <i3/ipc.h>
|
||||
#include <ev.h>
|
||||
#ifdef I3_ASAN_ENABLED
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@ -63,7 +66,6 @@ void got_output_reply(char *reply) {
|
||||
DLOG("Parsing outputs JSON...\n");
|
||||
parse_outputs_json(reply);
|
||||
DLOG("Reconfiguring windows...\n");
|
||||
realloc_sl_buffer();
|
||||
reconfig_windows(false);
|
||||
|
||||
i3_output *o_walk;
|
||||
@ -175,7 +177,6 @@ void got_bar_config_update(char *event) {
|
||||
/* update fonts and colors */
|
||||
init_xcb_late(config.fontname);
|
||||
init_colors(&(config.colors));
|
||||
realloc_sl_buffer();
|
||||
|
||||
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,
|
||||
* we exit here. */
|
||||
DLOG("EOF received, exiting...\n");
|
||||
#ifdef I3_ASAN_ENABLED
|
||||
__lsan_do_leak_check();
|
||||
#endif
|
||||
clean_xcb();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -137,6 +137,8 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (socket_path == NULL) {
|
||||
socket_path = atom_sock_path;
|
||||
} else {
|
||||
free(atom_sock_path);
|
||||
}
|
||||
|
||||
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. */
|
||||
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 only need those watchers on the stack, so putting them on the stack saves us
|
||||
|
@ -20,6 +20,8 @@
|
||||
struct mode_json_params {
|
||||
char *json;
|
||||
char *cur_key;
|
||||
char *name;
|
||||
bool pango_markup;
|
||||
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_;
|
||||
|
||||
if (!strcmp(params->cur_key, "change")) {
|
||||
/* Save the name */
|
||||
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));
|
||||
sasprintf(&(params->name), "%.*s", len, val);
|
||||
FREE(params->cur_key);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
struct mode_json_params *params = (struct mode_json_params *)params_;
|
||||
FREE(params->cur_key);
|
||||
sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
|
||||
strncpy(params->cur_key, (const char *)keyVal, keyLen);
|
||||
params->cur_key[keyLen] = '\0';
|
||||
static int mode_end_map_cb(void *params_) {
|
||||
struct mode_json_params *params = (struct mode_json_params *)params_;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@ -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 */
|
||||
static yajl_callbacks mode_callbacks = {
|
||||
.yajl_string = mode_string_cb,
|
||||
.yajl_boolean = mode_boolean_cb,
|
||||
.yajl_map_key = mode_map_key_cb,
|
||||
.yajl_end_map = mode_end_map_cb,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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_;
|
||||
|
||||
if (!strcmp(params->cur_key, "current_workspace")) {
|
||||
char *copy = smalloc(sizeof(const unsigned char) * (len + 1));
|
||||
strncpy(copy, (const char *)val, len);
|
||||
copy[len] = '\0';
|
||||
char *copy = NULL;
|
||||
sasprintf(©, "%.*s", len, val);
|
||||
|
||||
char *end;
|
||||
errno = 0;
|
||||
@ -118,7 +117,8 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
|
||||
if (errno == 0 &&
|
||||
(end && *end == '\0'))
|
||||
params->outputs_walk->ws = parsed_num;
|
||||
free(copy);
|
||||
|
||||
FREE(copy);
|
||||
FREE(params->cur_key);
|
||||
return 1;
|
||||
}
|
||||
@ -127,14 +127,9 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *name = smalloc(sizeof(const unsigned char) * (len + 1));
|
||||
strncpy(name, (const char *)val, len);
|
||||
name[len] = '\0';
|
||||
|
||||
params->outputs_walk->name = name;
|
||||
sasprintf(&(params->outputs_walk->name), "%.*s", len, val);
|
||||
|
||||
FREE(params->cur_key);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -149,9 +144,16 @@ static int outputs_start_map_cb(void *params_) {
|
||||
if (params->cur_key == NULL) {
|
||||
new_output = smalloc(sizeof(i3_output));
|
||||
new_output->name = NULL;
|
||||
new_output->active = false;
|
||||
new_output->primary = false;
|
||||
new_output->visible = false;
|
||||
new_output->ws = 0,
|
||||
new_output->statusline_width = 0;
|
||||
new_output->statusline_short_text = false;
|
||||
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));
|
||||
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) {
|
||||
struct outputs_json_params *params = (struct outputs_json_params *)params_;
|
||||
FREE(params->cur_key);
|
||||
|
||||
params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
|
||||
strncpy(params->cur_key, (const char *)keyVal, keyLen);
|
||||
params->cur_key[keyLen] = '\0';
|
||||
|
||||
sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -304,3 +302,17 @@ i3_output *get_output_by_name(char *name) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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) {
|
||||
struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
|
||||
|
||||
char *output_name;
|
||||
|
||||
if (!strcmp(params->cur_key, "name")) {
|
||||
const char *ws_name = (const char *)val;
|
||||
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")) {
|
||||
/* We add the ws to the TAILQ of the output, it belongs to */
|
||||
output_name = smalloc(sizeof(const unsigned char) * (len + 1));
|
||||
strncpy(output_name, (const char *)val, len);
|
||||
output_name[len] = '\0';
|
||||
char *output_name = NULL;
|
||||
sasprintf(&output_name, "%.*s", len, val);
|
||||
|
||||
i3_output *target = get_output_by_name(output_name);
|
||||
if (target) {
|
||||
if (target != NULL) {
|
||||
params->workspaces_walk->output = target;
|
||||
|
||||
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) {
|
||||
struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
|
||||
FREE(params->cur_key);
|
||||
|
||||
params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
|
||||
strncpy(params->cur_key, (const char *)keyVal, keyLen);
|
||||
params->cur_key[keyLen] = '\0';
|
||||
|
||||
sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
844
i3bar/src/xcb.c
844
i3bar/src/xcb.c
File diff suppressed because it is too large
Load Diff
@ -1,49 +1,2 @@
|
||||
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_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)
|
||||
#include "atoms_NET_SUPPORTED.xmacro"
|
||||
#include "atoms_rest.xmacro"
|
||||
|
33
include/atoms_NET_SUPPORTED.xmacro
Normal file
33
include/atoms_NET_SUPPORTED.xmacro
Normal 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
19
include/atoms_rest.xmacro
Normal 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)
|
@ -15,7 +15,7 @@ extern pid_t command_error_nagbar_pid;
|
||||
* 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
|
||||
@ -25,7 +25,7 @@ const char *DEFAULT_BINDING_MODE;
|
||||
*/
|
||||
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 *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)
|
||||
@ -33,6 +33,13 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch
|
||||
*/
|
||||
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
|
||||
* no such binding exists.
|
||||
@ -95,3 +102,12 @@ CommandResult *run_binding(Binding *bind, Con *con);
|
||||
*
|
||||
*/
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
* 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]'.
|
||||
@ -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'.
|
||||
*
|
||||
*/
|
||||
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>'.
|
||||
@ -97,10 +97,10 @@ void cmd_append_layout(I3_CMD, const char *path);
|
||||
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'.
|
||||
@ -109,16 +109,16 @@ void cmd_workspace_number(I3_CMD, const char *which);
|
||||
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]'
|
||||
@ -157,7 +157,7 @@ void cmd_floating(I3_CMD, const char *floating_mode);
|
||||
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);
|
||||
|
@ -30,6 +30,12 @@ Con *con_new(Con *parent, i3Window *window);
|
||||
*/
|
||||
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)
|
||||
*
|
||||
@ -152,26 +158,34 @@ Con *con_by_frame_id(xcb_window_t frame);
|
||||
*/
|
||||
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.
|
||||
* If the container already has this mark, the mark is removed.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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).
|
||||
*
|
||||
*/
|
||||
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
|
||||
@ -270,7 +284,7 @@ orientation_t con_orientation(Con *con);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
@ -419,3 +433,9 @@ char *con_get_tree_representation(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);
|
||||
|
@ -50,10 +50,11 @@ struct context {
|
||||
*
|
||||
*/
|
||||
struct Colortriple {
|
||||
uint32_t border;
|
||||
uint32_t background;
|
||||
uint32_t text;
|
||||
uint32_t indicator;
|
||||
color_t border;
|
||||
color_t background;
|
||||
color_t text;
|
||||
color_t indicator;
|
||||
color_t child_border;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -77,6 +78,7 @@ struct Variable {
|
||||
*/
|
||||
struct Mode {
|
||||
char *name;
|
||||
bool pango_markup;
|
||||
struct bindings_head *bindings;
|
||||
|
||||
SLIST_ENTRY(Mode) modes;
|
||||
@ -92,7 +94,7 @@ struct Config {
|
||||
i3Font font;
|
||||
|
||||
char *ipc_socket_path;
|
||||
const char *restart_state_path;
|
||||
char *restart_state_path;
|
||||
|
||||
layout_t default_layout;
|
||||
int container_stack_limit;
|
||||
@ -201,7 +203,7 @@ struct Config {
|
||||
|
||||
/* Color codes are stored here */
|
||||
struct config_client {
|
||||
uint32_t background;
|
||||
color_t background;
|
||||
struct Colortriple focused;
|
||||
struct Colortriple focused_inactive;
|
||||
struct Colortriple unfocused;
|
||||
@ -247,9 +249,10 @@ struct Barconfig {
|
||||
* simplicity (since we store just strings). */
|
||||
char **outputs;
|
||||
|
||||
/** Output on which the tray should be shown. The special value of 'no'
|
||||
* disables the tray (it’s enabled by default). */
|
||||
char *tray_output;
|
||||
/* List of outputs on which the tray is allowed to be shown, in order.
|
||||
* The special value "none" disables it (per default, it will be shown) and
|
||||
* 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. */
|
||||
int tray_padding;
|
||||
@ -322,6 +325,10 @@ struct Barconfig {
|
||||
char *statusline;
|
||||
char *separator;
|
||||
|
||||
char *focused_background;
|
||||
char *focused_statusline;
|
||||
char *focused_separator;
|
||||
|
||||
char *focused_workspace_border;
|
||||
char *focused_workspace_bg;
|
||||
char *focused_workspace_text;
|
||||
@ -361,6 +368,12 @@ struct Barbinding {
|
||||
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
|
||||
* override_configpath), the user’s one or the system default) and calls
|
||||
|
@ -59,14 +59,14 @@ CFGFUN(no_focus);
|
||||
CFGFUN(ipc_socket, const char *path);
|
||||
CFGFUN(restart_state, const char *path);
|
||||
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(floating_modifier, const char *modifiers);
|
||||
CFGFUN(new_window, const char *windowtype, const char *border, const long width);
|
||||
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(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(bar_font, const char *font);
|
||||
|
@ -31,6 +31,11 @@ struct ConfigResultIR {
|
||||
|
||||
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
|
||||
* parse_config and launching i3-nagbar if use_nagbar is true.
|
||||
|
@ -46,6 +46,7 @@ typedef struct Con Con;
|
||||
typedef struct Match Match;
|
||||
typedef struct Assignment Assignment;
|
||||
typedef struct Window i3Window;
|
||||
typedef struct mark_t mark_t;
|
||||
|
||||
/******************************************************************************
|
||||
* Helper types
|
||||
@ -61,7 +62,7 @@ typedef enum { BS_NORMAL = 0,
|
||||
BS_NONE = 1,
|
||||
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 */
|
||||
typedef enum { DONT_KILL_WINDOW = 0,
|
||||
KILL_WINDOW = 1,
|
||||
@ -74,6 +75,9 @@ typedef enum { ADJ_NONE = 0,
|
||||
ADJ_UPPER_SCREEN_EDGE = (1 << 2),
|
||||
ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
|
||||
|
||||
typedef enum { MM_REPLACE,
|
||||
MM_ADD } mark_mode_t;
|
||||
|
||||
/**
|
||||
* Container layouts. See Con::layout.
|
||||
*/
|
||||
@ -175,7 +179,7 @@ struct deco_render_params {
|
||||
struct width_height con_rect;
|
||||
struct width_height con_window_rect;
|
||||
Rect con_deco_rect;
|
||||
uint32_t background;
|
||||
color_t background;
|
||||
layout_t parent_layout;
|
||||
bool con_is_leaf;
|
||||
};
|
||||
@ -372,8 +376,6 @@ struct Window {
|
||||
|
||||
/** The name of the window. */
|
||||
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
|
||||
* 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. */
|
||||
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 */
|
||||
enum { W_NODOCK = 0,
|
||||
W_DOCK_TOP = 1,
|
||||
@ -432,6 +437,9 @@ struct Window {
|
||||
*
|
||||
*/
|
||||
struct Match {
|
||||
/* Set if a criterion was specified incorrectly. */
|
||||
char *error;
|
||||
|
||||
struct regex *title;
|
||||
struct regex *application;
|
||||
struct regex *class;
|
||||
@ -523,6 +531,12 @@ typedef enum { CF_NONE = 0,
|
||||
CF_OUTPUT = 1,
|
||||
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.
|
||||
*
|
||||
@ -541,11 +555,10 @@ struct Con {
|
||||
* change. */
|
||||
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;
|
||||
xcb_window_t frame;
|
||||
xcb_pixmap_t pixmap;
|
||||
xcb_gcontext_t pm_gc;
|
||||
|
||||
enum {
|
||||
CT_ROOT = 0,
|
||||
@ -562,21 +575,30 @@ struct Con {
|
||||
|
||||
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;
|
||||
/* The position and size of the actual client window. These coordinates are
|
||||
* relative to the container's 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;
|
||||
/** the geometry this window requested when getting mapped */
|
||||
struct Rect geometry;
|
||||
|
||||
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
|
||||
* group. The contents are shared between all of them, that is they are
|
||||
* displayed on whichever of the containers is currently visible */
|
||||
char *sticky_group;
|
||||
|
||||
/* user-definable mark to jump to this container later */
|
||||
char *mark;
|
||||
/* user-definable marks to jump to this container later */
|
||||
TAILQ_HEAD(marks_head, mark_t) marks_head;
|
||||
/* cached to decide whether a redraw is needed */
|
||||
bool mark_changed;
|
||||
|
||||
|
@ -36,6 +36,13 @@ void ewmh_update_desktop_names(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.
|
||||
*
|
||||
@ -96,3 +103,21 @@ void ewmh_setup_hints(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);
|
||||
|
@ -35,7 +35,6 @@ extern struct rlimit original_rlimit_core;
|
||||
extern bool debug_build;
|
||||
/** The number of file descriptors passed via socket activation. */
|
||||
extern int listen_fds;
|
||||
extern xcb_connection_t *conn;
|
||||
extern int conn_screen;
|
||||
/**
|
||||
* 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(assignments_head, Assignment) assignments;
|
||||
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
|
||||
* pixmaps. Will use 32 bit depth and an appropriate visual, if available,
|
||||
|
134
include/libi3.h
134
include/libi3.h
@ -20,9 +20,19 @@
|
||||
#if PANGO_SUPPORT
|
||||
#include <pango/pango.h>
|
||||
#endif
|
||||
#ifdef CAIRO_SUPPORT
|
||||
#include <cairo/cairo-xcb.h>
|
||||
#endif
|
||||
|
||||
#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.
|
||||
*
|
||||
@ -243,7 +253,7 @@ bool i3string_is_markup(i3String *str);
|
||||
/**
|
||||
* 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.
|
||||
@ -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);
|
||||
|
||||
/* 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.
|
||||
*
|
||||
*/
|
||||
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.
|
||||
@ -402,8 +425,8 @@ bool font_is_pango(void);
|
||||
* Text must be specified as an i3String.
|
||||
*
|
||||
*/
|
||||
void draw_text(i3String *text, xcb_drawable_t drawable,
|
||||
xcb_gcontext_t gc, int x, int y, int max_width);
|
||||
void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc,
|
||||
xcb_visualtype_t *visual, int x, int y, int max_width);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
#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);
|
||||
|
@ -10,6 +10,23 @@
|
||||
*/
|
||||
#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
|
||||
* updated correctly. Note that this function does not call any xcb_*
|
||||
|
@ -56,12 +56,6 @@ bool level_down(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
|
||||
* (horizontal/vertical).
|
||||
@ -78,11 +72,11 @@ void tree_next(char way, orientation_t orientation);
|
||||
* recursively while deleting a containers children.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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).
|
||||
|
@ -20,7 +20,7 @@
|
||||
if (pointer == NULL) \
|
||||
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_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_PREV(elm, field) : NULL)
|
||||
#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
|
||||
|
||||
/**
|
||||
* 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
|
||||
* handling SIGCHLD and killing i3-nagbar when i3 exits.
|
||||
|
@ -9,6 +9,12 @@
|
||||
*/
|
||||
#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
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
@ -14,6 +14,14 @@
|
||||
#include "tree.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),
|
||||
* creating the workspace if necessary (by allocating the necessary amount of
|
||||
|
@ -140,6 +140,12 @@ void xcb_set_root_cursor(int cursor);
|
||||
*/
|
||||
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
|
||||
*
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
@ -8,8 +8,6 @@
|
||||
#include "libi3.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
|
||||
* screen) to a corresponding amount of physical pixels on a standard or retina
|
||||
|
252
libi3/draw_util.c
Normal file
252
libi3/draw_util.c
Normal 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
|
||||
}
|
46
libi3/font.c
46
libi3/font.c
@ -12,16 +12,13 @@
|
||||
#include <stdbool.h>
|
||||
#include <err.h>
|
||||
|
||||
#if PANGO_SUPPORT
|
||||
#include <cairo/cairo-xcb.h>
|
||||
#if PANGO_SUPPORT
|
||||
#include <pango/pangocairo.h>
|
||||
#endif
|
||||
|
||||
#include "libi3.h"
|
||||
|
||||
extern xcb_connection_t *conn;
|
||||
extern xcb_screen_t *root_screen;
|
||||
|
||||
static const i3Font *savedFont = NULL;
|
||||
|
||||
#if PANGO_SUPPORT
|
||||
@ -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,
|
||||
xcb_drawable_t drawable, int x, int y,
|
||||
int max_width, bool is_markup) {
|
||||
xcb_drawable_t drawable, xcb_visualtype_t *visual, int x, int y,
|
||||
int max_width, bool pango_markup) {
|
||||
/* Create the Pango layout */
|
||||
/* root_visual_type is cached in load_pango_font */
|
||||
cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable,
|
||||
root_visual_type, x + max_width, y + savedFont->height);
|
||||
visual, x + max_width, y + savedFont->height);
|
||||
cairo_t *cr = cairo_create(surface);
|
||||
PangoLayout *layout = create_layout_with_dpi(cr);
|
||||
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_ellipsize(layout, PANGO_ELLIPSIZE_END);
|
||||
|
||||
if (is_markup)
|
||||
if (pango_markup)
|
||||
pango_layout_set_markup(layout, text, text_len);
|
||||
else
|
||||
pango_layout_set_text(layout, text, text_len);
|
||||
|
||||
/* Do the drawing */
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue);
|
||||
pango_cairo_update_layout(cr, layout);
|
||||
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.
|
||||
*
|
||||
*/
|
||||
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 */
|
||||
/* 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);
|
||||
@ -153,7 +151,7 @@ static int predict_text_width_pango(const char *text, size_t text_len, bool is_m
|
||||
gint width;
|
||||
pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
|
||||
|
||||
if (is_markup)
|
||||
if (pango_markup)
|
||||
pango_layout_set_markup(layout, text, text_len);
|
||||
else
|
||||
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);
|
||||
|
||||
/* Check if we managed to open 'fixed' */
|
||||
free(error);
|
||||
error = xcb_request_check(conn, font_cookie);
|
||||
|
||||
/* Fall back to '-misc-*' if opening 'fixed' fails. */
|
||||
@ -236,12 +235,16 @@ i3Font load_font(const char *pattern, const bool fallback) {
|
||||
strlen(pattern), pattern);
|
||||
info_cookie = xcb_query_font(conn, font.specific.xcb.id);
|
||||
|
||||
free(error);
|
||||
if ((error = xcb_request_check(conn, font_cookie)) != NULL)
|
||||
errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks "
|
||||
"(fixed or -misc-*): X11 error %d",
|
||||
error->error_code);
|
||||
}
|
||||
}
|
||||
if (error != NULL) {
|
||||
free(error);
|
||||
}
|
||||
|
||||
font.pattern = sstrdup(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.
|
||||
*
|
||||
*/
|
||||
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);
|
||||
|
||||
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: {
|
||||
/* Change the font and colors in the GC */
|
||||
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
|
||||
uint32_t values[] = {foreground, background, savedFont->specific.xcb.id};
|
||||
uint32_t values[] = {foreground.colorpixel, background.colorpixel, savedFont->specific.xcb.id};
|
||||
xcb_change_gc(conn, gc, mask, values);
|
||||
break;
|
||||
}
|
||||
#if PANGO_SUPPORT
|
||||
case FONT_TYPE_PANGO:
|
||||
/* Save the foreground font */
|
||||
pango_font_red = ((foreground >> 16) & 0xff) / 255.0;
|
||||
pango_font_green = ((foreground >> 8) & 0xff) / 255.0;
|
||||
pango_font_blue = (foreground & 0xff) / 255.0;
|
||||
pango_font_red = foreground.red;
|
||||
pango_font_green = foreground.green;
|
||||
pango_font_blue = foreground.blue;
|
||||
break;
|
||||
#endif
|
||||
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.
|
||||
*
|
||||
*/
|
||||
void draw_text(i3String *text, xcb_drawable_t drawable,
|
||||
xcb_gcontext_t gc, int x, int y, int max_width) {
|
||||
void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc,
|
||||
xcb_visualtype_t *visual, int x, int y, int max_width) {
|
||||
assert(savedFont != NULL);
|
||||
if (visual == NULL) {
|
||||
visual = root_visual_type;
|
||||
}
|
||||
|
||||
switch (savedFont->type) {
|
||||
case FONT_TYPE_NONE:
|
||||
@ -407,7 +413,7 @@ void draw_text(i3String *text, xcb_drawable_t drawable,
|
||||
case FONT_TYPE_PANGO:
|
||||
/* Render the text using Pango */
|
||||
draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
|
||||
drawable, x, y, max_width, i3string_is_markup(text));
|
||||
drawable, visual, x, y, max_width, i3string_is_markup(text));
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
@ -432,7 +438,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable,
|
||||
if (text_len > 255) {
|
||||
/* The text is too long to draw it directly to X */
|
||||
i3String *str = i3string_from_utf8(text);
|
||||
draw_text(str, drawable, gc, x, y, max_width);
|
||||
draw_text(str, drawable, gc, NULL, x, y, max_width);
|
||||
i3string_free(str);
|
||||
} else {
|
||||
/* 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:
|
||||
/* Render the text using Pango */
|
||||
draw_text_pango(text, strlen(text),
|
||||
drawable, x, y, max_width, false);
|
||||
drawable, root_visual_type, x, y, max_width, false);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
|
67
libi3/format_placeholders.c
Normal file
67
libi3/format_placeholders.c
Normal 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);
|
||||
}
|
@ -7,32 +7,77 @@
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "queue.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
|
||||
* works for true-color (vast majority of cases) at the moment, avoiding a
|
||||
* roundtrip to X11.
|
||||
* Returns the colorpixel to use for the given hex color (think of HTML).
|
||||
*
|
||||
* The hex_color has to start with #, for example #FF00FF.
|
||||
*
|
||||
* NOTE that get_colorpixel() does _NOT_ check the given color code for validity.
|
||||
* 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) {
|
||||
char strgroups[3][3] = {{hex[1], hex[2], '\0'},
|
||||
{hex[3], hex[4], '\0'},
|
||||
{hex[5], hex[6], '\0'}};
|
||||
char strgroups[3][3] = {
|
||||
{hex[1], hex[2], '\0'},
|
||||
{hex[3], hex[4], '\0'},
|
||||
{hex[5], hex[6], '\0'}};
|
||||
uint8_t r = strtol(strgroups[0], NULL, 16);
|
||||
uint8_t g = strtol(strgroups[1], 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
|
||||
* color depth visual. */
|
||||
return (0xFF << 24) | (r << 16 | g << 8 | b);
|
||||
/* Shortcut: if our screen is true color, no need to do a roundtrip to X11 */
|
||||
if (root_screen == NULL || root_screen->root_depth == 24 || root_screen->root_depth == 32) {
|
||||
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;
|
||||
}
|
||||
|
@ -12,8 +12,6 @@
|
||||
|
||||
#include "libi3.h"
|
||||
|
||||
extern xcb_connection_t *conn;
|
||||
|
||||
/*
|
||||
* 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).
|
||||
|
@ -31,14 +31,15 @@
|
||||
char *root_atom_contents(const char *atomname, xcb_connection_t *provided_conn, int screen) {
|
||||
xcb_intern_atom_cookie_t atom_cookie;
|
||||
xcb_intern_atom_reply_t *atom_reply;
|
||||
char *content;
|
||||
char *content = NULL;
|
||||
size_t content_max_words = 256;
|
||||
xcb_connection_t *conn = provided_conn;
|
||||
|
||||
if (provided_conn == NULL &&
|
||||
((conn = xcb_connect(NULL, &screen)) == NULL ||
|
||||
xcb_connection_has_error(conn)))
|
||||
xcb_connection_has_error(conn))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL);
|
||||
if (atom_reply == NULL)
|
||||
return NULL;
|
||||
if (atom_reply == NULL) {
|
||||
goto out_conn;
|
||||
}
|
||||
|
||||
xcb_get_property_cookie_t prop_cookie;
|
||||
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);
|
||||
prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
|
||||
if (prop_reply == NULL) {
|
||||
free(atom_reply);
|
||||
return NULL;
|
||||
goto out_atom;
|
||||
}
|
||||
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
|
||||
@ -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);
|
||||
prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
|
||||
if (prop_reply == NULL) {
|
||||
free(atom_reply);
|
||||
return NULL;
|
||||
goto out_atom;
|
||||
}
|
||||
}
|
||||
if (xcb_get_property_value_length(prop_reply) == 0) {
|
||||
free(atom_reply);
|
||||
free(prop_reply);
|
||||
return NULL;
|
||||
goto out;
|
||||
}
|
||||
if (prop_reply->type == XCB_ATOM_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),
|
||||
(char *)xcb_get_property_value(prop_reply));
|
||||
}
|
||||
|
||||
out:
|
||||
free(prop_reply);
|
||||
out_atom:
|
||||
free(atom_reply);
|
||||
out_conn:
|
||||
if (provided_conn == NULL)
|
||||
xcb_disconnect(conn);
|
||||
free(atom_reply);
|
||||
free(prop_reply);
|
||||
return content;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ struct _i3String {
|
||||
xcb_char2b_t *ucs2;
|
||||
size_t num_glyphs;
|
||||
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);
|
||||
|
||||
/* Set the markup flag */
|
||||
str->is_markup = true;
|
||||
str->pango_markup = true;
|
||||
|
||||
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);
|
||||
|
||||
/* set the markup flag */
|
||||
str->is_markup = true;
|
||||
str->pango_markup = true;
|
||||
|
||||
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 *copy = i3string_from_utf8(i3string_as_utf8(str));
|
||||
copy->is_markup = str->is_markup;
|
||||
copy->pango_markup = str->pango_markup;
|
||||
return copy;
|
||||
}
|
||||
|
||||
@ -178,14 +178,14 @@ size_t i3string_get_num_bytes(i3String *str) {
|
||||
* Whether the given i3String is in Pango markup.
|
||||
*/
|
||||
bool i3string_is_markup(i3String *str) {
|
||||
return str->is_markup;
|
||||
return str->pango_markup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set whether the i3String should use Pango markup.
|
||||
*/
|
||||
void i3string_set_markup(i3String *str, bool is_markup) {
|
||||
str->is_markup = is_markup;
|
||||
void i3string_set_markup(i3String *str, bool pango_markup) {
|
||||
str->pango_markup = pango_markup;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -7,7 +7,7 @@ template::[header-declarations]
|
||||
<refentrytitle>{mantitle}</refentrytitle>
|
||||
<manvolnum>{manvolnum}</manvolnum>
|
||||
<refmiscinfo class="source">i3</refmiscinfo>
|
||||
<refmiscinfo class="version">4.11</refmiscinfo>
|
||||
<refmiscinfo class="version">4.12</refmiscinfo>
|
||||
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
||||
</refmeta>
|
||||
<refnamediv>
|
||||
|
@ -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).
|
||||
|
||||
-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".
|
||||
|
||||
-l <limit>::
|
||||
|
@ -89,7 +89,7 @@ i3-msg -t get_tree
|
||||
=== I3SOCK
|
||||
|
||||
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.
|
||||
|
||||
== SEE ALSO
|
||||
|
@ -44,7 +44,7 @@ after modifying the configuration 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
|
||||
|
@ -25,6 +25,7 @@ It tries to start one of the following (in that order):
|
||||
* x-terminal-emulator (only present on Debian and derivatives)
|
||||
* urxvt
|
||||
* rxvt
|
||||
* termit
|
||||
* terminator
|
||||
* Eterm
|
||||
* aterm
|
||||
@ -32,6 +33,11 @@ It tries to start one of the following (in that order):
|
||||
* gnome-terminal
|
||||
* roxterm
|
||||
* xfce4-terminal
|
||||
* termite
|
||||
* lxterminal
|
||||
* mate-terminal
|
||||
* terminology
|
||||
* st
|
||||
|
||||
Please don’t complain about the order: If the user has any preference, they will
|
||||
have $TERMINAL set or modified their i3 configuration file.
|
||||
|
@ -77,6 +77,12 @@ i3 keeps your layout in a tree data structure.
|
||||
Window::
|
||||
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::
|
||||
A split container contains multiple other split containers or windows.
|
||||
+
|
||||
|
@ -86,15 +86,15 @@ state BORDER:
|
||||
border_style = 'normal', 'pixel'
|
||||
-> BORDER_WIDTH
|
||||
border_style = 'none', 'toggle'
|
||||
-> call cmd_border($border_style, "0")
|
||||
-> call cmd_border($border_style, 0)
|
||||
border_style = '1pixel'
|
||||
-> call cmd_border($border_style, "1")
|
||||
-> call cmd_border($border_style, 1)
|
||||
|
||||
state BORDER_WIDTH:
|
||||
end
|
||||
-> call cmd_border($border_style, "2")
|
||||
border_width = word
|
||||
-> call cmd_border($border_style, $border_width)
|
||||
-> call cmd_border($border_style, 2)
|
||||
border_width = number
|
||||
-> call cmd_border($border_style, &border_width)
|
||||
|
||||
# layout default|stacked|stacking|tabbed|splitv|splith
|
||||
# layout toggle [split|all]
|
||||
@ -117,9 +117,11 @@ state APPEND_LAYOUT:
|
||||
|
||||
# workspace next|prev|next_on_output|prev_on_output
|
||||
# workspace back_and_forth
|
||||
# workspace <name>
|
||||
# workspace number <number>
|
||||
# workspace [--no-auto-back-and-forth] <name>
|
||||
# workspace [--no-auto-back-and-forth] number <number>
|
||||
state WORKSPACE:
|
||||
no_auto_back_and_forth = '--no-auto-back-and-forth'
|
||||
->
|
||||
direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
|
||||
-> call cmd_workspace($direction)
|
||||
'back_and_forth'
|
||||
@ -127,11 +129,11 @@ state WORKSPACE:
|
||||
'number'
|
||||
-> WORKSPACE_NUMBER
|
||||
workspace = string
|
||||
-> call cmd_workspace_name($workspace)
|
||||
-> call cmd_workspace_name($workspace, $no_auto_back_and_forth)
|
||||
|
||||
state WORKSPACE_NUMBER:
|
||||
workspace = string
|
||||
-> call cmd_workspace_number($workspace)
|
||||
-> call cmd_workspace_number($workspace, $no_auto_back_and_forth)
|
||||
|
||||
# focus left|right|up|down
|
||||
# focus output <output>
|
||||
@ -189,9 +191,9 @@ state STICKY:
|
||||
action = 'enable', 'disable', 'toggle'
|
||||
-> call cmd_sticky($action)
|
||||
|
||||
# split v|h|vertical|horizontal
|
||||
# split v|h|t|vertical|horizontal|toggle
|
||||
state SPLIT:
|
||||
direction = 'horizontal', 'vertical', 'v', 'h'
|
||||
direction = 'horizontal', 'vertical', 'toggle', 'v', 'h', 't'
|
||||
-> call cmd_split($direction)
|
||||
|
||||
# floating enable|disable|toggle
|
||||
@ -199,12 +201,14 @@ state FLOATING:
|
||||
floating = 'enable', 'disable', 'toggle'
|
||||
-> call cmd_floating($floating)
|
||||
|
||||
# mark [--toggle] <mark>
|
||||
# mark [--add|--replace] [--toggle] <mark>
|
||||
state MARK:
|
||||
mode = '--add', '--replace'
|
||||
->
|
||||
toggle = '--toggle'
|
||||
->
|
||||
mark = string
|
||||
-> call cmd_mark($mark, $toggle)
|
||||
-> call cmd_mark($mark, $mode, $toggle)
|
||||
|
||||
# unmark [mark]
|
||||
state UNMARK:
|
||||
@ -304,6 +308,8 @@ state MOVE:
|
||||
->
|
||||
'to'
|
||||
->
|
||||
no_auto_back_and_forth = '--no-auto-back-and-forth'
|
||||
->
|
||||
'workspace'
|
||||
-> MOVE_WORKSPACE
|
||||
'output'
|
||||
@ -341,11 +347,11 @@ state MOVE_WORKSPACE:
|
||||
'number'
|
||||
-> MOVE_WORKSPACE_NUMBER
|
||||
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:
|
||||
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:
|
||||
output = string
|
||||
|
@ -146,6 +146,8 @@ state ASSIGN:
|
||||
state ASSIGN_WORKSPACE:
|
||||
'→'
|
||||
->
|
||||
'workspace'
|
||||
->
|
||||
workspace = string
|
||||
-> call cfg_assign($workspace)
|
||||
|
||||
@ -280,9 +282,15 @@ state COLOR_TEXT:
|
||||
|
||||
state COLOR_INDICATOR:
|
||||
indicator = word
|
||||
-> call cfg_color($colorclass, $border, $background, $text, $indicator)
|
||||
-> COLOR_CHILD_BORDER
|
||||
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
|
||||
state EXEC:
|
||||
@ -326,8 +334,10 @@ state BINDCOMMAND:
|
||||
################################################################################
|
||||
|
||||
state MODENAME:
|
||||
pango_markup = '--pango_markup'
|
||||
->
|
||||
modename = word
|
||||
-> call cfg_enter_mode($modename); MODEBRACE
|
||||
-> call cfg_enter_mode($pango_markup, $modename); MODEBRACE
|
||||
|
||||
state MODEBRACE:
|
||||
end
|
||||
@ -443,7 +453,7 @@ state BAR_ID:
|
||||
-> call cfg_bar_id($bar_id); BAR
|
||||
|
||||
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
|
||||
|
||||
state BAR_WHEEL_UP_CMD:
|
||||
@ -518,7 +528,7 @@ state BAR_COLORS:
|
||||
end ->
|
||||
'#' -> 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
|
||||
colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace', 'binding_mode'
|
||||
-> BAR_COLORS_BORDER
|
||||
|
10
release.sh
10
release.sh
@ -1,14 +1,14 @@
|
||||
#!/bin/zsh
|
||||
# This script is used to prepare a new release of i3.
|
||||
|
||||
export RELEASE_VERSION="4.10.4"
|
||||
export PREVIOUS_VERSION="4.10.3"
|
||||
export RELEASE_BRANCH="master"
|
||||
export RELEASE_VERSION="4.11"
|
||||
export PREVIOUS_VERSION="4.10.4"
|
||||
export RELEASE_BRANCH="next"
|
||||
|
||||
if [ ! -e "../i3.github.io" ]
|
||||
then
|
||||
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
|
||||
fi
|
||||
|
||||
@ -41,7 +41,7 @@ STARTDIR=$PWD
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
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)."
|
||||
exit 1
|
||||
fi
|
||||
|
109
src/bindings.c
109
src/bindings.c
@ -27,7 +27,7 @@ const char *DEFAULT_BINDING_MODE = "default";
|
||||
* 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;
|
||||
|
||||
/* 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 */
|
||||
mode = scalloc(1, sizeof(struct Mode));
|
||||
mode->name = sstrdup(name);
|
||||
mode->pango_markup = pango_markup;
|
||||
mode->bindings = scalloc(1, sizeof(struct bindings_head));
|
||||
TAILQ_INIT(mode->bindings);
|
||||
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,
|
||||
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));
|
||||
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);
|
||||
@ -91,7 +92,7 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch
|
||||
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");
|
||||
|
||||
struct Mode *mode = mode_from_name(modename);
|
||||
struct Mode *mode = mode_from_name(modename, pango_markup);
|
||||
TAILQ_INSERT_TAIL(mode->bindings, new_binding, bindings);
|
||||
|
||||
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
|
||||
* 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) {
|
||||
bool state_matches;
|
||||
if (bind->event_state_mask == 0) {
|
||||
const uint32_t xkb_group_mask = (bind->event_state_mask & 0xFFFF0000);
|
||||
/* 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
|
||||
* false positives, see issue #2002. */
|
||||
state_matches = (state_filtered == 0);
|
||||
mods_match = (modifiers_state == 0);
|
||||
} 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",
|
||||
bind->event_state_mask, state_filtered, (state_matches ? "yes" : "no"));
|
||||
DLOG("binding groups_match = %s, mods_match = %s, state_matches = %s\n",
|
||||
(groups_match ? "yes" : "no"),
|
||||
(mods_match ? "yes" : "no"),
|
||||
(state_matches ? "yes" : "no"));
|
||||
/* First compare the state_filtered (unless this is a
|
||||
* B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
|
||||
* event) */
|
||||
@ -327,6 +359,7 @@ void translate_keysyms(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool has_errors = false;
|
||||
Binding *bind;
|
||||
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||
if (bind->input_type == B_MOUSE) {
|
||||
@ -397,6 +430,21 @@ void translate_keysyms(void) {
|
||||
sasprintf(&tmp, "%s %d", keycodes, bind->translated_to[n]);
|
||||
free(keycodes);
|
||||
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",
|
||||
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_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);
|
||||
|
||||
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);
|
||||
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.
|
||||
* 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) ||
|
||||
(bind->symbol != NULL && current->symbol == NULL))
|
||||
continue;
|
||||
@ -588,8 +639,8 @@ void binding_free(Binding *bind) {
|
||||
/*
|
||||
* Runs the given binding and handles parse errors. If con is passed, it will
|
||||
* execute the command binding with that container selected by criteria.
|
||||
* Returns a CommandResult for running the binding's command. Caller should
|
||||
* render tree if needs_tree_render is true. Free with command_result_free().
|
||||
* Returns a CommandResult for running the binding's command. Free with
|
||||
* command_result_free().
|
||||
*
|
||||
*/
|
||||
CommandResult *run_binding(Binding *bind, Con *con) {
|
||||
@ -758,3 +809,33 @@ bool load_keymap(void) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
17
src/click.c
17
src/click.c
@ -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_flush(conn);
|
||||
|
||||
if (result->needs_tree_render)
|
||||
tree_render();
|
||||
|
||||
command_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -356,13 +352,24 @@ int handle_button_press(xcb_button_press_event_t *event) {
|
||||
|
||||
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);
|
||||
DLOG("floating_mod = %d, detail = %d\n", mod_pressed, event->detail);
|
||||
if ((con = con_by_window_id(event->event)))
|
||||
return route_click(con, event, mod_pressed, CLICK_INSIDE);
|
||||
|
||||
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
|
||||
* click coordinates and focus the output's active workspace. */
|
||||
if (event->event == root && event->response_type == XCB_BUTTON_PRESS) {
|
||||
|
389
src/commands.c
389
src/commands.c
@ -12,6 +12,10 @@
|
||||
#include <float.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef I3_ASAN_ENABLED
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
#endif
|
||||
|
||||
#include "all.h"
|
||||
#include "shmlog.h"
|
||||
|
||||
@ -42,6 +46,16 @@
|
||||
} \
|
||||
} 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
|
||||
* 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
|
||||
@ -49,7 +63,14 @@
|
||||
*/
|
||||
#define HANDLE_EMPTY_MATCH \
|
||||
do { \
|
||||
HANDLE_INVALID_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)); \
|
||||
ow->con = focused; \
|
||||
TAILQ_INIT(&owindows); \
|
||||
@ -121,102 +142,6 @@ static Con *maybe_auto_back_and_forth_workspace(Con *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.
|
||||
******************************************************************************/
|
||||
@ -282,26 +207,60 @@ void cmd_criteria_match_windows(I3_CMD) {
|
||||
next = TAILQ_NEXT(next, owindows);
|
||||
|
||||
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) {
|
||||
accept_match = true;
|
||||
|
||||
if (current_match->con_id == current->con) {
|
||||
DLOG("matches container!\n");
|
||||
TAILQ_INSERT_TAIL(&owindows, current, owindows);
|
||||
DLOG("con_id matched.\n");
|
||||
} else {
|
||||
DLOG("doesnt match\n");
|
||||
free(current);
|
||||
DLOG("con_id does not match.\n");
|
||||
FREE(current);
|
||||
continue;
|
||||
}
|
||||
} else if (current_match->mark != NULL && current->con->mark != NULL &&
|
||||
regex_matches(current_match->mark, current->con->mark)) {
|
||||
DLOG("match by mark\n");
|
||||
}
|
||||
|
||||
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");
|
||||
matched_by_mark = true;
|
||||
break;
|
||||
}
|
||||
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
LOG("You cannot move containers to i3-internal workspaces (\"%s\").\n", name);
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool no_auto_back_and_forth = (_no_auto_back_and_forth != NULL);
|
||||
owindow *current;
|
||||
|
||||
/* We have nothing to move:
|
||||
@ -426,7 +386,8 @@ void cmd_move_con_to_workspace_name(I3_CMD, const char *name) {
|
||||
/* get the workspace */
|
||||
Con *ws = workspace_get(name, NULL);
|
||||
|
||||
ws = maybe_auto_back_and_forth_workspace(ws);
|
||||
if (!no_auto_back_and_forth)
|
||||
ws = maybe_auto_back_and_forth_workspace(ws);
|
||||
|
||||
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;
|
||||
|
||||
/* We have nothing to move:
|
||||
@ -477,7 +439,8 @@ void cmd_move_con_to_workspace_number(I3_CMD, const char *which) {
|
||||
workspace = workspace_get(which, NULL);
|
||||
}
|
||||
|
||||
workspace = maybe_auto_back_and_forth_workspace(workspace);
|
||||
if (!no_auto_back_and_forth)
|
||||
workspace = maybe_auto_back_and_forth_workspace(workspace);
|
||||
|
||||
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 &&
|
||||
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;
|
||||
|
||||
/* 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'.
|
||||
*
|
||||
*/
|
||||
void cmd_border(I3_CMD, const char *border_style_str, const char *border_width) {
|
||||
DLOG("border style should be changed to %s with border width %s\n", border_style_str, 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 %ld\n", border_style_str, border_width);
|
||||
owindow *current;
|
||||
|
||||
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) {
|
||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||
int border_style = current->con->border_style;
|
||||
char *end;
|
||||
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;
|
||||
}
|
||||
int con_border_width = border_width;
|
||||
|
||||
if (strcmp(border_style_str, "toggle") == 0) {
|
||||
border_style++;
|
||||
border_style %= 3;
|
||||
if (border_style == BS_NORMAL)
|
||||
tmp_border_width = 2;
|
||||
con_border_width = 2;
|
||||
else if (border_style == BS_NONE)
|
||||
tmp_border_width = 0;
|
||||
con_border_width = 0;
|
||||
else if (border_style == BS_PIXEL)
|
||||
tmp_border_width = 1;
|
||||
con_border_width = 1;
|
||||
} else {
|
||||
if (strcmp(border_style_str, "normal") == 0)
|
||||
if (strcmp(border_style_str, "normal") == 0) {
|
||||
border_style = BS_NORMAL;
|
||||
else if (strcmp(border_style_str, "pixel") == 0)
|
||||
} else if (strcmp(border_style_str, "pixel") == 0) {
|
||||
border_style = BS_PIXEL;
|
||||
else if (strcmp(border_style_str, "1pixel") == 0) {
|
||||
} else if (strcmp(border_style_str, "1pixel") == 0) {
|
||||
border_style = BS_PIXEL;
|
||||
tmp_border_width = 1;
|
||||
} else if (strcmp(border_style_str, "none") == 0)
|
||||
con_border_width = 1;
|
||||
} else if (strcmp(border_style_str, "none") == 0) {
|
||||
border_style = BS_NONE;
|
||||
else {
|
||||
} else {
|
||||
ELOG("BUG: called with border_style=%s\n", border_style_str);
|
||||
ysuccess(false);
|
||||
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;
|
||||
@ -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;
|
||||
|
||||
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;
|
||||
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;
|
||||
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) {
|
||||
LOG("You cannot switch to the i3-internal workspaces (\"%s\").\n", name);
|
||||
ysuccess(false);
|
||||
@ -987,7 +949,7 @@ void cmd_workspace_name(I3_CMD, const char *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;
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
mark_mode_t mark_mode = (mode == NULL || strcmp(mode, "--replace") == 0) ? MM_REPLACE : MM_ADD;
|
||||
if (toggle != NULL) {
|
||||
con_mark_toggle(current->con, mark);
|
||||
con_mark_toggle(current->con, mark, mark_mode);
|
||||
} else {
|
||||
con_mark(current->con, mark);
|
||||
con_mark(current->con, mark, mark_mode);
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
// 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) {
|
||||
@ -1180,6 +1151,24 @@ void cmd_split(I3_CMD, const char *direction) {
|
||||
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);
|
||||
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) {
|
||||
if (kill_mode_str == NULL)
|
||||
kill_mode_str = "window";
|
||||
owindow *current;
|
||||
|
||||
DLOG("kill_mode=%s\n", kill_mode_str);
|
||||
|
||||
@ -1211,14 +1199,11 @@ void cmd_kill(I3_CMD, const char *kill_mode_str) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* check if the match is empty, not if the result is empty */
|
||||
if (match_is_empty(current_match))
|
||||
tree_close_con(kill_mode);
|
||||
else {
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||
tree_close(current->con, kill_mode, false, false);
|
||||
}
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
owindow *current;
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
con_close(current->con, kill_mode);
|
||||
}
|
||||
|
||||
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. */
|
||||
output_push_sticky_windows(focused);
|
||||
|
||||
ewmh_update_wm_desktop();
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
ysuccess(true);
|
||||
}
|
||||
@ -1580,6 +1567,9 @@ void cmd_layout_toggle(I3_CMD, const char *toggle_mode) {
|
||||
*/
|
||||
void cmd_exit(I3_CMD) {
|
||||
LOG("Exiting due to user command.\n");
|
||||
#ifdef I3_ASAN_ENABLED
|
||||
__lsan_do_leak_check();
|
||||
#endif
|
||||
ipc_shutdown();
|
||||
unlink(config.ipc_socket_path);
|
||||
xcb_disconnect(conn);
|
||||
@ -1738,29 +1728,43 @@ 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) {
|
||||
if (!con_is_floating(focused)) {
|
||||
ELOG("Cannot change position. The window/container is not floating\n");
|
||||
yerror("Cannot change position. The window/container is not floating.");
|
||||
return;
|
||||
}
|
||||
bool has_error = false;
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
if (strcmp(method, "absolute") == 0) {
|
||||
DLOG("moving to absolute center\n");
|
||||
floating_center(focused->parent, croot->rect);
|
||||
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);
|
||||
|
||||
floating_maybe_reassign_ws(focused->parent);
|
||||
cmd_output->needs_tree_render = true;
|
||||
}
|
||||
if (!has_error) {
|
||||
yerror("Cannot change position of a window/container because it is not floating.");
|
||||
has_error = true;
|
||||
}
|
||||
|
||||
if (strcmp(method, "position") == 0) {
|
||||
DLOG("moving to center\n");
|
||||
floating_center(focused->parent, con_get_workspace(focused)->rect);
|
||||
continue;
|
||||
}
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
if (strcmp(method, "absolute") == 0) {
|
||||
DLOG("moving to absolute center\n");
|
||||
floating_center(floating_con, croot->rect);
|
||||
|
||||
floating_maybe_reassign_ws(floating_con);
|
||||
cmd_output->needs_tree_render = true;
|
||||
}
|
||||
|
||||
if (strcmp(method, "position") == 0) {
|
||||
DLOG("moving to center\n");
|
||||
floating_center(floating_con, con_get_workspace(floating_con)->rect);
|
||||
|
||||
cmd_output->needs_tree_render = true;
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: default reply for now, make this a better reply
|
||||
ysuccess(true);
|
||||
if (!has_error)
|
||||
ysuccess(true);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1839,27 +1843,33 @@ void cmd_title_format(I3_CMD, const char *format) {
|
||||
|
||||
owindow *current;
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
if (current->con->window == NULL)
|
||||
continue;
|
||||
|
||||
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,
|
||||
* so we remove the title format altogether. */
|
||||
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);
|
||||
ewmh_update_visible_name(current->con->window->id, i3string_as_utf8(formatted_title));
|
||||
I3STRING_FREE(formatted_title);
|
||||
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));
|
||||
I3STRING_FREE(formatted_title);
|
||||
}
|
||||
} else {
|
||||
/* We can remove _NET_WM_VISIBLE_NAME since we don't display a custom title. */
|
||||
ewmh_update_visible_name(current->con->window->id, NULL);
|
||||
if (current->con->window != NULL) {
|
||||
/* We can remove _NET_WM_VISIBLE_NAME since we don't display a custom title. */
|
||||
ewmh_update_visible_name(current->con->window->id, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the window title is redrawn immediately. */
|
||||
current->con->window->name_x_changed = true;
|
||||
if (current->con->window != NULL) {
|
||||
/* Make sure the window title is redrawn immediately. */
|
||||
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;
|
||||
@ -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),
|
||||
!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);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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_current_desktop();
|
||||
|
||||
startup_sequence_rename_workspace(old_name, new_name);
|
||||
startup_sequence_rename_workspace(old_name_copy, new_name);
|
||||
free(old_name_copy);
|
||||
}
|
||||
|
||||
/*
|
||||
|
193
src/con.c
193
src/con.c
@ -47,7 +47,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) {
|
||||
new->depth = window->depth;
|
||||
new->window->aspect_ratio = 0.0;
|
||||
} else {
|
||||
new->depth = XCB_COPY_FROM_PARENT;
|
||||
new->depth = root_depth;
|
||||
}
|
||||
DLOG("opening window\n");
|
||||
|
||||
@ -55,6 +55,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) {
|
||||
TAILQ_INIT(&(new->nodes_head));
|
||||
TAILQ_INIT(&(new->focus_head));
|
||||
TAILQ_INIT(&(new->swallow_head));
|
||||
TAILQ_INIT(&(new->marks_head));
|
||||
|
||||
if (parent != NULL)
|
||||
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)
|
||||
*
|
||||
@ -513,7 +544,7 @@ Con *con_by_window_id(xcb_window_t window) {
|
||||
Con *con_by_frame_id(xcb_window_t frame) {
|
||||
Con *con;
|
||||
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||
if (con->frame == frame)
|
||||
if (con->frame.id == frame)
|
||||
return con;
|
||||
return NULL;
|
||||
}
|
||||
@ -526,27 +557,41 @@ Con *con_by_frame_id(xcb_window_t frame) {
|
||||
Con *con_by_mark(const char *mark) {
|
||||
Con *con;
|
||||
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 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.
|
||||
* If the container already has this mark, the mark is removed.
|
||||
* 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);
|
||||
DLOG("Toggling mark \"%s\" on con = %p.\n", mark, con);
|
||||
|
||||
if (con->mark != NULL && strcmp(con->mark, mark) == 0) {
|
||||
con_unmark(mark);
|
||||
if (con_has_mark(con, mark)) {
|
||||
con_unmark(con, mark);
|
||||
} 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.
|
||||
*
|
||||
*/
|
||||
void con_mark(Con *con, const char *mark) {
|
||||
void con_mark(Con *con, const char *mark, mark_mode_t mode) {
|
||||
assert(con != NULL);
|
||||
DLOG("Setting mark \"%s\" on con = %p.\n", mark, con);
|
||||
|
||||
FREE(con->mark);
|
||||
con->mark = sstrdup(mark);
|
||||
con->mark_changed = true;
|
||||
con_unmark(NULL, mark);
|
||||
if (mode == MM_REPLACE) {
|
||||
DLOG("Removing all existing marks on con = %p.\n", con);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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).
|
||||
*
|
||||
*/
|
||||
void con_unmark(const char *mark) {
|
||||
Con *con;
|
||||
if (mark == NULL) {
|
||||
void con_unmark(Con *con, const char *name) {
|
||||
Con *current;
|
||||
if (name == NULL) {
|
||||
DLOG("Unmarking all containers.\n");
|
||||
TAILQ_FOREACH(con, &all_cons, all_cons) {
|
||||
if (con->mark == NULL)
|
||||
TAILQ_FOREACH(current, &all_cons, all_cons) {
|
||||
if (con != NULL && current != con)
|
||||
continue;
|
||||
|
||||
FREE(con->mark);
|
||||
con->mark_changed = true;
|
||||
if (TAILQ_EMPTY(&(current->marks_head)))
|
||||
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 {
|
||||
DLOG("Removing mark \"%s\".\n", mark);
|
||||
con = con_by_mark(mark);
|
||||
if (con == NULL) {
|
||||
DLOG("Removing mark \"%s\".\n", name);
|
||||
current = (con == NULL) ? con_by_mark(name) : con;
|
||||
if (current == NULL) {
|
||||
DLOG("No container found with this mark, so there is nothing to do.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DLOG("Found mark on con = %p. Removing it now.\n", con);
|
||||
FREE(con->mark);
|
||||
con->mark_changed = true;
|
||||
DLOG("Found mark on con = %p. Removing it now.\n", current);
|
||||
current->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);
|
||||
}
|
||||
|
||||
CALL(parent, on_remove_child);
|
||||
|
||||
/* 9. If the container was marked urgent, move the urgency hint. */
|
||||
if (urgent) {
|
||||
workspace_update_urgent_flag(source_ws);
|
||||
con_set_urgency(con, true);
|
||||
}
|
||||
|
||||
CALL(parent, on_remove_child);
|
||||
|
||||
ipc_send_window_event("move", con);
|
||||
ewmh_update_wm_desktop();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1126,7 +1194,7 @@ orientation_t con_orientation(Con *con) {
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
@ -1642,7 +1710,7 @@ static void con_on_remove_child(Con *con) {
|
||||
if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
|
||||
LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
|
||||
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;
|
||||
ylength length;
|
||||
@ -1663,7 +1731,7 @@ static void con_on_remove_child(Con *con) {
|
||||
int children = con_num_children(con);
|
||||
if (children == 0) {
|
||||
DLOG("Container empty, closing\n");
|
||||
tree_close(con, DONT_KILL_WINDOW, false, false);
|
||||
tree_close_internal(con, DONT_KILL_WINDOW, false, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1925,6 +1993,7 @@ char *con_get_tree_representation(Con *con) {
|
||||
(TAILQ_FIRST(&(con->nodes_head)) == child ? "" : " "), child_txt);
|
||||
free(buf);
|
||||
buf = tmp_buf;
|
||||
free(child_txt);
|
||||
}
|
||||
|
||||
/* 3) close the brackets */
|
||||
@ -1934,3 +2003,47 @@ char *con_get_tree_representation(Con *con) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
53
src/config.c
53
src/config.c
@ -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) {
|
||||
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 */
|
||||
ungrab_all_keys(conn);
|
||||
|
||||
struct Mode *mode;
|
||||
Binding *bind;
|
||||
while (!SLIST_EMPTY(&modes)) {
|
||||
mode = SLIST_FIRST(&modes);
|
||||
FREE(mode->name);
|
||||
|
||||
/* Clear the old binding list */
|
||||
bindings = mode->bindings;
|
||||
while (!TAILQ_EMPTY(bindings)) {
|
||||
bind = TAILQ_FIRST(bindings);
|
||||
TAILQ_REMOVE(bindings, bind, bindings);
|
||||
while (!TAILQ_EMPTY(mode->bindings)) {
|
||||
Binding *bind = TAILQ_FIRST(mode->bindings);
|
||||
TAILQ_REMOVE(mode->bindings, bind, bindings);
|
||||
binding_free(bind);
|
||||
}
|
||||
FREE(bindings);
|
||||
FREE(mode->bindings);
|
||||
|
||||
SLIST_REMOVE(&modes, mode, Mode, modes);
|
||||
FREE(mode);
|
||||
}
|
||||
|
||||
struct Assignment *assign;
|
||||
@ -110,14 +115,32 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
||||
FREE(barconfig->id);
|
||||
for (int c = 0; c < barconfig->num_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->tray_output);
|
||||
FREE(barconfig->socket_path);
|
||||
FREE(barconfig->status_command);
|
||||
FREE(barconfig->i3bar_command);
|
||||
FREE(barconfig->font);
|
||||
FREE(barconfig->colors.background);
|
||||
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_bg);
|
||||
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 */
|
||||
free_font();
|
||||
|
||||
free(config.ipc_socket_path);
|
||||
free(config.restart_state_path);
|
||||
free(config.fake_outputs);
|
||||
}
|
||||
|
||||
SLIST_INIT(&modes);
|
||||
@ -173,13 +200,14 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
||||
/* Initialize default colors */
|
||||
#define INIT_COLOR(x, cborder, cbackground, ctext, cindicator) \
|
||||
do { \
|
||||
x.border = get_colorpixel(cborder); \
|
||||
x.background = get_colorpixel(cbackground); \
|
||||
x.text = get_colorpixel(ctext); \
|
||||
x.indicator = get_colorpixel(cindicator); \
|
||||
x.border = draw_util_hex_to_color(cborder); \
|
||||
x.background = draw_util_hex_to_color(cbackground); \
|
||||
x.text = draw_util_hex_to_color(ctext); \
|
||||
x.indicator = draw_util_hex_to_color(cindicator); \
|
||||
x.child_border = draw_util_hex_to_color(cbackground); \
|
||||
} 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_inactive, "#333333", "#5f676a", "#ffffff", "#484e50");
|
||||
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) {
|
||||
translate_keysyms();
|
||||
grab_all_keys(conn);
|
||||
regrab_all_buttons(conn);
|
||||
}
|
||||
|
||||
if (config.font.type == FONT_TYPE_NONE) {
|
||||
|
@ -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) {
|
||||
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 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) {
|
||||
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) {
|
||||
ELOG("You cannot use the name %s for your mode\n", DEFAULT_BINDING_MODE);
|
||||
exit(1);
|
||||
@ -130,6 +131,7 @@ CFGFUN(enter_mode, const char *modename) {
|
||||
DLOG("\t now in mode %s\n", modename);
|
||||
FREE(current_mode);
|
||||
current_mode = sstrdup(modename);
|
||||
current_mode_pango_markup = (pango_markup != NULL);
|
||||
}
|
||||
|
||||
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) {
|
||||
free(config.fake_outputs);
|
||||
config.fake_outputs = sstrdup(outputs);
|
||||
}
|
||||
|
||||
@ -311,10 +314,12 @@ CFGFUN(workspace, const char *workspace, const char *output) {
|
||||
}
|
||||
|
||||
CFGFUN(ipc_socket, const char *path) {
|
||||
free(config.ipc_socket_path);
|
||||
config.ipc_socket_path = sstrdup(path);
|
||||
}
|
||||
|
||||
CFGFUN(restart_state, const char *path) {
|
||||
free(config.restart_state_path);
|
||||
config.restart_state_path = sstrdup(path);
|
||||
}
|
||||
|
||||
@ -330,20 +335,25 @@ CFGFUN(popup_during_fullscreen, const char *value) {
|
||||
|
||||
CFGFUN(color_single, const char *colorclass, const char *color) {
|
||||
/* 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) {
|
||||
#define APPLY_COLORS(classname) \
|
||||
do { \
|
||||
if (strcmp(colorclass, "client." #classname) == 0) { \
|
||||
config.client.classname.border = get_colorpixel(border); \
|
||||
config.client.classname.background = get_colorpixel(background); \
|
||||
config.client.classname.text = get_colorpixel(text); \
|
||||
if (indicator != NULL) { \
|
||||
config.client.classname.indicator = get_colorpixel(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) \
|
||||
do { \
|
||||
if (strcmp(colorclass, "client." #classname) == 0) { \
|
||||
config.client.classname.border = draw_util_hex_to_color(border); \
|
||||
config.client.classname.background = draw_util_hex_to_color(background); \
|
||||
config.client.classname.text = draw_util_hex_to_color(text); \
|
||||
if (indicator != NULL) { \
|
||||
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)
|
||||
|
||||
APPLY_COLORS(focused_inactive);
|
||||
@ -385,57 +395,60 @@ CFGFUN(no_focus) {
|
||||
* Bar configuration (i3bar)
|
||||
******************************************************************************/
|
||||
|
||||
static Barconfig current_bar;
|
||||
static Barconfig *current_bar;
|
||||
|
||||
CFGFUN(bar_font, const char *font) {
|
||||
FREE(current_bar.font);
|
||||
current_bar.font = sstrdup(font);
|
||||
FREE(current_bar->font);
|
||||
current_bar->font = sstrdup(font);
|
||||
}
|
||||
|
||||
CFGFUN(bar_separator_symbol, const char *separator) {
|
||||
FREE(current_bar.separator_symbol);
|
||||
current_bar.separator_symbol = sstrdup(separator);
|
||||
FREE(current_bar->separator_symbol);
|
||||
current_bar->separator_symbol = sstrdup(separator);
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
current_bar.id = sstrdup(bar_id);
|
||||
current_bar->id = sstrdup(bar_id);
|
||||
}
|
||||
|
||||
CFGFUN(bar_output, const char *output) {
|
||||
int new_outputs = current_bar.num_outputs + 1;
|
||||
current_bar.outputs = srealloc(current_bar.outputs, sizeof(char *) * new_outputs);
|
||||
current_bar.outputs[current_bar.num_outputs] = sstrdup(output);
|
||||
current_bar.num_outputs = new_outputs;
|
||||
int new_outputs = current_bar->num_outputs + 1;
|
||||
current_bar->outputs = srealloc(current_bar->outputs, sizeof(char *) * new_outputs);
|
||||
current_bar->outputs[current_bar->num_outputs] = sstrdup(output);
|
||||
current_bar->num_outputs = new_outputs;
|
||||
}
|
||||
|
||||
CFGFUN(bar_verbose, const char *verbose) {
|
||||
current_bar.verbose = eval_boolstr(verbose);
|
||||
current_bar->verbose = eval_boolstr(verbose);
|
||||
}
|
||||
|
||||
CFGFUN(bar_modifier, const char *modifier) {
|
||||
if (strcmp(modifier, "Mod1") == 0)
|
||||
current_bar.modifier = M_MOD1;
|
||||
current_bar->modifier = M_MOD1;
|
||||
else if (strcmp(modifier, "Mod2") == 0)
|
||||
current_bar.modifier = M_MOD2;
|
||||
current_bar->modifier = M_MOD2;
|
||||
else if (strcmp(modifier, "Mod3") == 0)
|
||||
current_bar.modifier = M_MOD3;
|
||||
current_bar->modifier = M_MOD3;
|
||||
else if (strcmp(modifier, "Mod4") == 0)
|
||||
current_bar.modifier = M_MOD4;
|
||||
current_bar->modifier = M_MOD4;
|
||||
else if (strcmp(modifier, "Mod5") == 0)
|
||||
current_bar.modifier = M_MOD5;
|
||||
current_bar->modifier = M_MOD5;
|
||||
else if (strcmp(modifier, "Control") == 0 ||
|
||||
strcmp(modifier, "Ctrl") == 0)
|
||||
current_bar.modifier = M_CONTROL;
|
||||
current_bar->modifier = M_CONTROL;
|
||||
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) {
|
||||
@ -451,7 +464,7 @@ static void bar_configure_binding(const char *button, const char *command) {
|
||||
}
|
||||
|
||||
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) {
|
||||
ELOG("command for button %s was already specified, ignoring.\n", button);
|
||||
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));
|
||||
new_binding->input_code = input_code;
|
||||
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) {
|
||||
@ -479,29 +492,29 @@ CFGFUN(bar_bindsym, const char *button, const char *command) {
|
||||
}
|
||||
|
||||
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) {
|
||||
FREE(current_bar.i3bar_command);
|
||||
current_bar.i3bar_command = sstrdup(i3bar_command);
|
||||
FREE(current_bar->i3bar_command);
|
||||
current_bar->i3bar_command = sstrdup(i3bar_command);
|
||||
}
|
||||
|
||||
CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text) {
|
||||
#define APPLY_COLORS(classname) \
|
||||
do { \
|
||||
if (strcmp(colorclass, #classname) == 0) { \
|
||||
if (text != NULL) { \
|
||||
/* New syntax: border, background, text */ \
|
||||
current_bar.colors.classname##_border = sstrdup(border); \
|
||||
current_bar.colors.classname##_bg = sstrdup(background); \
|
||||
current_bar.colors.classname##_text = sstrdup(text); \
|
||||
} else { \
|
||||
/* Old syntax: text, background */ \
|
||||
current_bar.colors.classname##_bg = sstrdup(background); \
|
||||
current_bar.colors.classname##_text = sstrdup(border); \
|
||||
} \
|
||||
} \
|
||||
#define APPLY_COLORS(classname) \
|
||||
do { \
|
||||
if (strcmp(colorclass, #classname) == 0) { \
|
||||
if (text != NULL) { \
|
||||
/* New syntax: border, background, text */ \
|
||||
current_bar->colors.classname##_border = sstrdup(border); \
|
||||
current_bar->colors.classname##_bg = sstrdup(background); \
|
||||
current_bar->colors.classname##_text = sstrdup(text); \
|
||||
} else { \
|
||||
/* Old syntax: text, background */ \
|
||||
current_bar->colors.classname##_bg = sstrdup(background); \
|
||||
current_bar->colors.classname##_text = sstrdup(border); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
APPLY_COLORS(focused_workspace);
|
||||
@ -514,67 +527,73 @@ CFGFUN(bar_color, const char *colorclass, const char *border, const char *backgr
|
||||
}
|
||||
|
||||
CFGFUN(bar_socket_path, const char *socket_path) {
|
||||
FREE(current_bar.socket_path);
|
||||
current_bar.socket_path = sstrdup(socket_path);
|
||||
FREE(current_bar->socket_path);
|
||||
current_bar->socket_path = sstrdup(socket_path);
|
||||
}
|
||||
|
||||
CFGFUN(bar_tray_output, const char *output) {
|
||||
FREE(current_bar.tray_output);
|
||||
current_bar.tray_output = sstrdup(output);
|
||||
struct tray_output_t *tray_output = scalloc(1, sizeof(struct tray_output_t));
|
||||
tray_output->output = sstrdup(output);
|
||||
TAILQ_INSERT_TAIL(&(current_bar->tray_outputs), tray_output, tray_outputs);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (strcmp(colorclass, "background") == 0)
|
||||
current_bar.colors.background = sstrdup(color);
|
||||
current_bar->colors.background = sstrdup(color);
|
||||
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
|
||||
current_bar.colors.statusline = sstrdup(color);
|
||||
current_bar->colors.focused_statusline = sstrdup(color);
|
||||
}
|
||||
|
||||
CFGFUN(bar_status_command, const char *command) {
|
||||
FREE(current_bar.status_command);
|
||||
current_bar.status_command = sstrdup(command);
|
||||
FREE(current_bar->status_command);
|
||||
current_bar->status_command = sstrdup(command);
|
||||
}
|
||||
|
||||
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) {
|
||||
current_bar.hide_workspace_buttons = !eval_boolstr(value);
|
||||
current_bar->hide_workspace_buttons = !eval_boolstr(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) {
|
||||
TAILQ_INIT(&(current_bar.bar_bindings));
|
||||
current_bar.tray_padding = 2;
|
||||
current_bar = scalloc(1, sizeof(struct Barconfig));
|
||||
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) {
|
||||
DLOG("\t new bar configuration finished, saving.\n");
|
||||
/* Generate a unique ID for this bar if not already configured */
|
||||
if (!current_bar.id)
|
||||
sasprintf(¤t_bar.id, "bar-%d", config.number_barconfigs);
|
||||
if (current_bar->id == NULL)
|
||||
sasprintf(¤t_bar->id, "bar-%d", config.number_barconfigs);
|
||||
|
||||
config.number_barconfigs++;
|
||||
|
||||
/* If no font was explicitly set, we use the i3 font as default */
|
||||
if (!current_bar.font && font_pattern)
|
||||
current_bar.font = sstrdup(font_pattern);
|
||||
if (current_bar->font == NULL && font_pattern != NULL)
|
||||
current_bar->font = sstrdup(font_pattern);
|
||||
|
||||
/* Copy the current (static) structure into a dynamically allocated
|
||||
* one, then cleanup our static one. */
|
||||
Barconfig *bar_config = scalloc(1, sizeof(Barconfig));
|
||||
memcpy(bar_config, ¤t_bar, sizeof(Barconfig));
|
||||
TAILQ_INSERT_TAIL(&barconfigs, bar_config, configs);
|
||||
|
||||
memset(¤t_bar, '\0', sizeof(Barconfig));
|
||||
TAILQ_INSERT_TAIL(&barconfigs, current_bar, configs);
|
||||
/* Simply reset the pointer, but don't free the resources. */
|
||||
current_bar = NULL;
|
||||
}
|
||||
|
@ -783,6 +783,34 @@ static char *migrate_config(char *input, off_t size) {
|
||||
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
|
||||
* parse_config and possibly launching i3-nagbar.
|
||||
@ -815,7 +843,7 @@ bool parse_file(const char *f, bool use_nagbar) {
|
||||
break;
|
||||
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));
|
||||
}
|
||||
continuation = strstr(buffer, "\\\n");
|
||||
@ -882,7 +910,7 @@ bool parse_file(const char *f, bool use_nagbar) {
|
||||
FREE(bufcopy);
|
||||
|
||||
/* 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 *new = smalloc(stbuf.st_size + extra_bytes + 1);
|
||||
destwalk = new;
|
||||
@ -958,29 +986,7 @@ bool parse_file(const char *f, bool use_nagbar) {
|
||||
if (version == 3)
|
||||
ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n");
|
||||
|
||||
char *editaction,
|
||||
*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);
|
||||
start_config_error_nagbar(f, context->has_errors);
|
||||
}
|
||||
|
||||
bool has_errors = context->has_errors;
|
||||
|
149
src/ewmh.c
149
src/ewmh.c
@ -21,24 +21,9 @@ xcb_window_t ewmh_window;
|
||||
*
|
||||
*/
|
||||
void ewmh_update_current_desktop(void) {
|
||||
Con *focused_ws = con_get_workspace(focused);
|
||||
Con *output;
|
||||
uint32_t idx = 0;
|
||||
/* We count to get the index of this workspace because named workspaces
|
||||
* don’t 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;
|
||||
}
|
||||
const uint32_t idx = ewmh_get_workspace_index(focused);
|
||||
if (idx != NET_WM_DESKTOP_NONE) {
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_CURRENT_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &idx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,6 +123,71 @@ void ewmh_update_desktop_viewport(void) {
|
||||
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.
|
||||
*
|
||||
@ -234,7 +284,7 @@ void ewmh_update_sticky(xcb_window_t window, bool sticky) {
|
||||
void ewmh_setup_hints(void) {
|
||||
xcb_atom_t supported_atoms[] = {
|
||||
#define xmacro(atom) A_##atom,
|
||||
#include "atoms.xmacro"
|
||||
#include "atoms_NET_SUPPORTED.xmacro"
|
||||
#undef xmacro
|
||||
};
|
||||
|
||||
@ -263,10 +313,67 @@ void ewmh_setup_hints(void) {
|
||||
/* I’m 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");
|
||||
|
||||
/* 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 */ 32, 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);
|
||||
|
||||
/* 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_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;
|
||||
}
|
||||
|
@ -11,8 +11,6 @@
|
||||
*/
|
||||
#include "all.h"
|
||||
|
||||
extern xcb_connection_t *conn;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
LOG("This is a workspace, creating new container around content\n");
|
||||
if (con_num_children(con) == 0) {
|
||||
LOG("Workspace is empty, aborting\n");
|
||||
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;
|
||||
LOG("Container is a workspace, not enabling floating mode.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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->focus_head), con, focused);
|
||||
|
||||
@ -180,7 +168,7 @@ void floating_enable(Con *con, bool automatic) {
|
||||
nc->layout = L_SPLITH;
|
||||
/* We insert nc already, even though its rect is not yet calculated. This
|
||||
* is necessary because otherwise the workspace might be empty (and get
|
||||
* closed in tree_close()) even though it’s not. */
|
||||
* closed in tree_close_internal()) even though it’s not. */
|
||||
TAILQ_INSERT_TAIL(&(ws->floating_head), nc, floating_windows);
|
||||
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) &&
|
||||
con_num_children(con->parent) == 0) {
|
||||
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;
|
||||
@ -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
|
||||
* coordinates and exit if that was done successfully. */
|
||||
if (floating_maybe_reassign_ws(nc)) {
|
||||
ipc_send_window_event("floating", con);
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Sanitize coordinates: Check if they are on any output */
|
||||
if (get_output_containing(nc->rect.x, nc->rect.y) != NULL) {
|
||||
ipc_send_window_event("floating", con);
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ELOG("No output found at destination coordinates, centering floating window on current ws\n");
|
||||
floating_center(nc, ws->rect);
|
||||
|
||||
done:
|
||||
floating_set_hint_atom(nc, true);
|
||||
ipc_send_window_event("floating", con);
|
||||
}
|
||||
|
||||
@ -333,7 +321,7 @@ void floating_disable(Con *con, bool automatic) {
|
||||
/* 2: kill parent container */
|
||||
TAILQ_REMOVE(&(con->parent->parent->floating_head), con->parent, floating_windows);
|
||||
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
|
||||
* this floating con was on */
|
||||
@ -358,6 +346,7 @@ void floating_disable(Con *con, bool automatic) {
|
||||
if (set_focus)
|
||||
con_focus(con);
|
||||
|
||||
floating_set_hint_atom(con, false);
|
||||
ipc_send_window_event("floating", con);
|
||||
}
|
||||
|
||||
|
@ -503,7 +503,12 @@ static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
|
||||
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();
|
||||
|
||||
ignore_end:
|
||||
@ -656,15 +661,14 @@ static void handle_expose_event(xcb_expose_event_t *event) {
|
||||
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
|
||||
* 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. */
|
||||
xcb_copy_area(conn, parent->pixmap, parent->frame, parent->pm_gc,
|
||||
event->x, event->y, event->x, event->y,
|
||||
event->width, event->height);
|
||||
draw_util_copy_surface(conn, &(parent->frame_buffer), &(parent->frame),
|
||||
event->x, event->y, event->x, event->y,
|
||||
event->width, event->height);
|
||||
xcb_flush(conn);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -736,7 +740,9 @@ static void handle_client_message(xcb_client_message_event_t *event) {
|
||||
con->sticky = !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);
|
||||
ewmh_update_wm_desktop();
|
||||
}
|
||||
|
||||
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
|
||||
* 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]);
|
||||
|
||||
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 (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;
|
||||
}
|
||||
|
||||
++idx;
|
||||
}
|
||||
Con *ws = ewmh_get_workspace_by_index(event->data.data32[0]);
|
||||
if (ws == NULL) {
|
||||
ELOG("Could not determine workspace for this index, ignoring request.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
/*
|
||||
* 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])
|
||||
last_timestamp = event->data.data32[0];
|
||||
|
||||
tree_close(con, KILL_WINDOW, false, false);
|
||||
tree_close_internal(con, KILL_WINDOW, false, false);
|
||||
tree_render();
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
DLOG("unhandled clientmessage\n");
|
||||
return;
|
||||
DLOG("Skipping client message for unhandled type %d\n", event->type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3
|
||||
i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
|
||||
i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
|
||||
i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h))
|
||||
i3_CFLAGS = $(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_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_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) $(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
|
||||
# gcc, this actually makes the build slower.
|
||||
|
56
src/ipc.c
56
src/ipc.c
@ -71,6 +71,9 @@ void ipc_shutdown(void) {
|
||||
current = TAILQ_FIRST(&all_clients);
|
||||
shutdown(current->fd, SHUT_RDWR);
|
||||
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);
|
||||
free(current);
|
||||
}
|
||||
@ -275,9 +278,16 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
||||
ystr("urgent");
|
||||
y(bool, con->urgent);
|
||||
|
||||
if (con->mark != NULL) {
|
||||
ystr("mark");
|
||||
ystr(con->mark);
|
||||
if (!TAILQ_EMPTY(&(con->marks_head))) {
|
||||
ystr("marks");
|
||||
y(array_open);
|
||||
|
||||
mark_t *mark;
|
||||
TAILQ_FOREACH(mark, &(con->marks_head), marks) {
|
||||
ystr(mark->name);
|
||||
}
|
||||
|
||||
y(array_close);
|
||||
}
|
||||
|
||||
ystr("focused");
|
||||
@ -365,6 +375,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
||||
else
|
||||
y(null);
|
||||
|
||||
if (con->title_format != NULL) {
|
||||
ystr("title_format");
|
||||
ystr(con->title_format);
|
||||
}
|
||||
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
ystr("num");
|
||||
y(integer, con->num);
|
||||
@ -544,6 +559,18 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
|
||||
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) \
|
||||
do { \
|
||||
if (config->name) { \
|
||||
@ -552,8 +579,6 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
YSTR_IF_SET(tray_output);
|
||||
|
||||
ystr("tray_padding");
|
||||
y(integer, config->tray_padding);
|
||||
|
||||
@ -586,6 +611,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
|
||||
|
||||
ystr("modifier");
|
||||
switch (config->modifier) {
|
||||
case M_NONE:
|
||||
ystr("none");
|
||||
break;
|
||||
case M_CONTROL:
|
||||
ystr("ctrl");
|
||||
break;
|
||||
@ -601,11 +629,6 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
|
||||
case M_MOD3:
|
||||
ystr("Mod3");
|
||||
break;
|
||||
/*
|
||||
case M_MOD4:
|
||||
ystr("Mod4");
|
||||
break;
|
||||
*/
|
||||
case M_MOD5:
|
||||
ystr("Mod5");
|
||||
break;
|
||||
@ -656,6 +679,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
|
||||
YSTR_IF_SET(background);
|
||||
YSTR_IF_SET(statusline);
|
||||
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_bg);
|
||||
YSTR_IF_SET(focused_workspace_text);
|
||||
@ -819,9 +845,12 @@ IPC_HANDLER(get_marks) {
|
||||
y(array_open);
|
||||
|
||||
Con *con;
|
||||
TAILQ_FOREACH(con, &all_cons, all_cons)
|
||||
if (con->mark != NULL)
|
||||
ystr(con->mark);
|
||||
TAILQ_FOREACH(con, &all_cons, all_cons) {
|
||||
mark_t *mark;
|
||||
TAILQ_FOREACH(mark, &(con->marks_head), marks) {
|
||||
ystr(mark->name);
|
||||
}
|
||||
}
|
||||
|
||||
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++)
|
||||
free(current->events[i]);
|
||||
free(current->events);
|
||||
/* We can call TAILQ_REMOVE because we break out of the
|
||||
* TAILQ_FOREACH afterwards */
|
||||
TAILQ_REMOVE(&all_clients, current, clients);
|
||||
|
@ -31,9 +31,5 @@ void handle_key_press(xcb_key_press_event_t *event) {
|
||||
return;
|
||||
|
||||
CommandResult *result = run_binding(bind, NULL);
|
||||
|
||||
if (result->needs_tree_render)
|
||||
tree_render();
|
||||
|
||||
command_result_free(result);
|
||||
}
|
||||
|
@ -28,7 +28,9 @@ static bool parsing_deco_rect;
|
||||
static bool parsing_window_rect;
|
||||
static bool parsing_geometry;
|
||||
static bool parsing_focus;
|
||||
static bool parsing_marks;
|
||||
struct Match *current_swallow;
|
||||
static bool swallow_is_empty;
|
||||
|
||||
/* This list is used for reordering the focus stack after parsing the 'focus'
|
||||
* array. */
|
||||
@ -47,6 +49,7 @@ static int json_start_map(void *ctx) {
|
||||
current_swallow = smalloc(sizeof(Match));
|
||||
match_init(current_swallow);
|
||||
TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
|
||||
swallow_is_empty = true;
|
||||
} else {
|
||||
if (!parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
|
||||
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));
|
||||
TAILQ_REMOVE(&(json_node->swallow_head), match, matches);
|
||||
match_free(match);
|
||||
free(match);
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,6 +154,13 @@ static int json_end_map(void *ctx) {
|
||||
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_deco_rect = false;
|
||||
parsing_window_rect = false;
|
||||
@ -159,12 +170,16 @@ static int json_end_map(void *ctx) {
|
||||
|
||||
static int json_end_array(void *ctx) {
|
||||
LOG("end of array\n");
|
||||
if (!parsing_swallows && !parsing_focus) {
|
||||
if (!parsing_swallows && !parsing_focus && !parsing_marks) {
|
||||
con_fix_percent(json_node);
|
||||
}
|
||||
if (parsing_swallows) {
|
||||
parsing_swallows = false;
|
||||
}
|
||||
if (parsing_marks) {
|
||||
parsing_marks = false;
|
||||
}
|
||||
|
||||
if (parsing_focus) {
|
||||
/* Clear the list of focus mappings */
|
||||
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)
|
||||
parsing_focus = true;
|
||||
|
||||
if (strcasecmp(last_key, "marks") == 0)
|
||||
parsing_marks = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -224,20 +242,32 @@ static int json_string(void *ctx, const unsigned char *val, size_t len) {
|
||||
sasprintf(&sval, "%.*s", len, val);
|
||||
if (strcasecmp(last_key, "class") == 0) {
|
||||
current_swallow->class = regex_new(sval);
|
||||
swallow_is_empty = false;
|
||||
} else if (strcasecmp(last_key, "instance") == 0) {
|
||||
current_swallow->instance = regex_new(sval);
|
||||
swallow_is_empty = false;
|
||||
} else if (strcasecmp(last_key, "window_role") == 0) {
|
||||
current_swallow->window_role = regex_new(sval);
|
||||
swallow_is_empty = false;
|
||||
} else if (strcasecmp(last_key, "title") == 0) {
|
||||
current_swallow->title = regex_new(sval);
|
||||
swallow_is_empty = false;
|
||||
} else {
|
||||
ELOG("swallow key %s unknown\n", last_key);
|
||||
}
|
||||
free(sval);
|
||||
} else if (parsing_marks) {
|
||||
char *mark;
|
||||
sasprintf(&mark, "%.*s", (int)len, val);
|
||||
|
||||
con_mark(json_node, mark, MM_ADD);
|
||||
} else {
|
||||
if (strcasecmp(last_key, "name") == 0) {
|
||||
json_node->name = scalloc(len + 1, 1);
|
||||
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) {
|
||||
json_node->sticky_group = scalloc(len + 1, 1);
|
||||
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);
|
||||
free(buf);
|
||||
} else if (strcasecmp(last_key, "mark") == 0) {
|
||||
DLOG("Found deprecated key \"mark\".\n");
|
||||
|
||||
char *buf = NULL;
|
||||
sasprintf(&buf, "%.*s", (int)len, val);
|
||||
|
||||
/* We unmark any containers using this mark to avoid duplicates. */
|
||||
con_unmark(buf);
|
||||
|
||||
json_node->mark = buf;
|
||||
con_mark(json_node, buf, MM_REPLACE);
|
||||
} else if (strcasecmp(last_key, "floating") == 0) {
|
||||
char *buf = NULL;
|
||||
sasprintf(&buf, "%.*s", (int)len, val);
|
||||
@ -421,12 +450,15 @@ static int json_int(void *ctx, long long val) {
|
||||
if (parsing_swallows) {
|
||||
if (strcasecmp(last_key, "id") == 0) {
|
||||
current_swallow->id = val;
|
||||
swallow_is_empty = false;
|
||||
}
|
||||
if (strcasecmp(last_key, "dock") == 0) {
|
||||
current_swallow->dock = val;
|
||||
swallow_is_empty = false;
|
||||
}
|
||||
if (strcasecmp(last_key, "insert_where") == 0) {
|
||||
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;
|
||||
|
||||
if (parsing_swallows) {
|
||||
if (strcasecmp(last_key, "restart_mode") == 0)
|
||||
if (strcasecmp(last_key, "restart_mode") == 0) {
|
||||
current_swallow->restart_mode = val;
|
||||
swallow_is_empty = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -589,6 +623,7 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
|
||||
parsing_window_rect = false;
|
||||
parsing_geometry = false;
|
||||
parsing_focus = false;
|
||||
parsing_marks = false;
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
stat = yajl_parse(hand, (const unsigned char *)buf, n);
|
||||
if (stat != yajl_status_ok) {
|
||||
@ -606,8 +641,11 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
|
||||
|
||||
setlocale(LC_NUMERIC, "");
|
||||
yajl_complete_parse(hand);
|
||||
yajl_free(hand);
|
||||
yajl_gen_free(g);
|
||||
|
||||
fclose(f);
|
||||
free(buf);
|
||||
if (to_focus)
|
||||
con_focus(to_focus);
|
||||
}
|
||||
|
22
src/log.c
22
src/log.c
@ -58,6 +58,8 @@ static char *loglastwrap;
|
||||
static int logbuffer_size;
|
||||
/* File descriptor for shm_open. */
|
||||
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
|
||||
@ -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
|
||||
* 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. */
|
||||
@ -108,15 +120,6 @@ void open_logbuffer(void) {
|
||||
* 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
|
||||
* 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);
|
||||
#if defined(__FreeBSD__)
|
||||
sasprintf(&shmlogname, "/tmp/i3-log-%d", getpid());
|
||||
@ -172,6 +175,7 @@ void open_logbuffer(void) {
|
||||
void close_logbuffer(void) {
|
||||
close(logbuffer_shm);
|
||||
shm_unlink(shmlogname);
|
||||
free(shmlogname);
|
||||
logbuffer = NULL;
|
||||
shmlogname = "";
|
||||
}
|
||||
|
52
src/main.c
52
src/main.c
@ -59,7 +59,7 @@ xcb_window_t root;
|
||||
* pixmaps. Will use 32 bit depth and an appropriate visual, if available,
|
||||
* otherwise the root window’s default (usually 24 bit TrueColor). */
|
||||
uint8_t root_depth;
|
||||
xcb_visualid_t visual_id;
|
||||
xcb_visualtype_t *visual_type;
|
||||
xcb_colormap_t colormap;
|
||||
|
||||
struct ev_loop *main_loop;
|
||||
@ -481,15 +481,29 @@ int main(int argc, char *argv[]) {
|
||||
#include "atoms.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;
|
||||
visual_id = root_screen->root_visual;
|
||||
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",
|
||||
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));
|
||||
@ -660,6 +674,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
con_focus(con_descend_focused(output_get_content(output->con)));
|
||||
free(pointerreply);
|
||||
}
|
||||
|
||||
tree_render();
|
||||
@ -787,6 +802,11 @@ int main(int argc, char *argv[]) {
|
||||
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;
|
||||
|
||||
action.sa_sigaction = handle_signal;
|
||||
@ -819,18 +839,28 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
/* Autostarting exec-lines */
|
||||
if (autostart) {
|
||||
struct Autostart *exec;
|
||||
TAILQ_FOREACH(exec, &autostarts, autostarts) {
|
||||
while (!TAILQ_EMPTY(&autostarts)) {
|
||||
struct Autostart *exec = TAILQ_FIRST(&autostarts);
|
||||
|
||||
LOG("auto-starting %s\n", exec->command);
|
||||
start_application(exec->command, exec->no_startup_id);
|
||||
|
||||
FREE(exec->command);
|
||||
TAILQ_REMOVE(&autostarts, exec, autostarts);
|
||||
FREE(exec);
|
||||
}
|
||||
}
|
||||
|
||||
/* Autostarting exec_always-lines */
|
||||
struct Autostart *exec_always;
|
||||
TAILQ_FOREACH(exec_always, &autostarts_always, autostarts_always) {
|
||||
while (!TAILQ_EMPTY(&autostarts_always)) {
|
||||
struct Autostart *exec_always = TAILQ_FIRST(&autostarts_always);
|
||||
|
||||
LOG("auto-starting (always!) %s\n", exec_always->command);
|
||||
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 */
|
||||
|
90
src/manage.c
90
src/manage.c
@ -90,7 +90,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
||||
utf8_title_cookie, title_cookie,
|
||||
class_cookie, leader_cookie, transient_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);
|
||||
|
||||
@ -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_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));
|
||||
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);
|
||||
|
||||
@ -168,12 +170,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
||||
cwindow->id = window;
|
||||
cwindow->depth = get_visual_depth(attr->visual);
|
||||
|
||||
/* We need to grab buttons 1-3 for click-to-focus and buttons 1-5
|
||||
* 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 /* don’t filter for any modifiers */);
|
||||
xcb_grab_buttons(conn, window, bindings_should_grab_scrollwheel_buttons());
|
||||
|
||||
/* 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);
|
||||
@ -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);
|
||||
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 */
|
||||
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 */
|
||||
nc = con_for_window(search_at, cwindow, &match);
|
||||
const bool match_from_restart_mode = (match && match->restart_mode);
|
||||
if (nc == NULL) {
|
||||
Con *wm_desktop_ws = NULL;
|
||||
|
||||
/* If not, check if it is assigned to a specific workspace */
|
||||
if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE))) {
|
||||
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 */
|
||||
if (!workspace_is_visible(assigned_ws))
|
||||
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) {
|
||||
/* If it’s not assigned, but was started on a specific workspace,
|
||||
* we want to open it there */
|
||||
/* If it was started on a specific workspace, we want to open it there. */
|
||||
DLOG("Using workspace on which this application was started (%s)\n", startup_ws);
|
||||
nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL));
|
||||
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);
|
||||
TAILQ_REMOVE(&(nc->swallow_head), match, matches);
|
||||
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));
|
||||
TAILQ_REMOVE(&(nc->swallow_head), first, matches);
|
||||
match_free(first);
|
||||
free(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nc->window != cwindow && nc->window != NULL) {
|
||||
window_free(nc->window);
|
||||
}
|
||||
nc->window = cwindow;
|
||||
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);
|
||||
|
||||
if (workspace_is_visible(ws) && current_output == target_output) {
|
||||
if (!match || !match->restart_mode) {
|
||||
if (!match_from_restart_mode) {
|
||||
set_focus = true;
|
||||
} else
|
||||
} else {
|
||||
DLOG("not focusing, matched with restart_mode == true\n");
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
DLOG("workspace not visible, not focusing\n");
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
DLOG("dock, not focusing\n");
|
||||
}
|
||||
} else {
|
||||
DLOG("fs = %p, ws = %p, not focusing\n", fs, ws);
|
||||
/* 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))
|
||||
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(type_reply);
|
||||
|
||||
@ -477,7 +515,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
|
||||
values[0] = XCB_NONE;
|
||||
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) {
|
||||
LOG("Could not reparent the window, aborting\n");
|
||||
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
|
||||
* proper window event sequence. */
|
||||
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. */
|
||||
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:
|
||||
free(geom);
|
||||
out:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user