diff --git a/.clang-format b/.clang-format
index 4a15e88e..97f3e47a 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,13 +1,14 @@
-BasedOnStyle: google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: None
-AllowShortBlocksOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: false
-IndentWidth: 4
-PointerBindsToType: false
+BasedOnStyle: google
ColumnLimit: 0
-SpaceBeforeParens: ControlStatements
-SortIncludes: false
ForEachMacros: [ TAILQ_FOREACH, TAILQ_FOREACH_REVERSE, SLIST_FOREACH, CIRCLEQ_FOREACH, CIRCLEQ_FOREACH_REVERSE, NODES_FOREACH, NODES_FOREACH_REVERSE, FOREACH_NONINTERNAL]
+IndentWidth: 4
+InsertBraces: true
+PointerBindsToType: false
+SortIncludes: false
+SpaceBeforeParens: ControlStatements
TypenameMacros: [ SLIST_HEAD, SLIST_ENTRY, LIST_HEAD, LIST_ENTRY, SIMPLEQ_HEAD, SIMPLEQ_ENTRY, TAILQ_HEAD, TAILQ_ENTRY, CIRCLEQ_HEAD, CIRCLEQ_ENTRY ]
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index a6df0a95..00000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,72 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
----
-
-
-
-## I'm submitting a…
-
-
-[x] Bug
-[ ] Feature Request
-[ ] Documentation Request
-[ ] Other (Please describe in detail)
-
-
-## Current Behavior
-
-
-## Expected Behavior
-
-
-## Reproduction Instructions
-
-
-## Environment
-
-Output of `i3 --moreversion 2>&-`:
-
-i3 version:
-
-
-
-Config file
-
-
-
-
-
-Logfile URL:
-
-
-
-
-- Linux Distribution & Version:
-- Are you using a compositor (e.g., xcompmgr or compton):
-
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100755
index 00000000..734ab55b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,107 @@
+name: Bug Report
+description: Create a report to help us improve.
+labels: [bug]
+body:
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Welcome
+ options:
+ - label: Yes, I'm using the latest major release or the current development version. These are the only supported versions.
+ required: true
+ - label: Yes, I've searched similar issues and discussions on GitHub and didn't find any.
+ required: true
+
+ - type: textarea
+ id: current
+ attributes:
+ label: Current Behavior
+ placeholder: |-
+ Describe the current behavior,
+ e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
+ validations:
+ required: true
+
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected Behavior
+ placeholder: |-
+ Describe the desired behavior you expect after mitigation of the issue,
+ e.g., »The window left next to the current window should be focused.«
+ validations:
+ required: true
+
+ - type: textarea
+ id: reproduction
+ attributes:
+ label: Reproduction Instructions
+ placeholder: |-
+ Please provide detailed instructions on how the bug can be reproduced.
+ E.g., »Open three windows in a V[A H[B C]] layout on a new workspace«
+ validations:
+ required: true
+
+ - type: textarea
+ id: version
+ attributes:
+ label: i3 version
+ description: |-
+ Paste the output of
+ ```
+ i3 --moreversion 2>&-
+ ```
+ render: text
+ validations:
+ required: true
+
+ - type: textarea
+ id: config
+ attributes:
+ label: Config file
+ description: |-
+ Please include your (complete) i3 config with which the issue occurs.
+
+ If you would like to help debugging the issue, please try to reduce the config such that it is as close to the default config as possible while still reproducing the issue. This can help us bisect the root cause.
+ render: text
+ validations:
+ required: true
+
+ - type: input
+ id: distro
+ attributes:
+ label: Linux distribution & Version
+ validations:
+ required: true
+
+ - type: dropdown
+ id: compositor
+ attributes:
+ label: Are you using a compositor?
+ description: |-
+ Try running
+ ```shell
+ pidof picom
+ pidof compton
+ ```
+ If any IDs show up, you are running a compositor
+ options:
+ - I don't know
+ - I am sure I don't run any compositor
+ - picom
+ - compton
+ - Other
+ validations:
+ required: true
+
+ - type: input
+ id: verbose-output
+ attributes:
+ label: Logfile
+ description: |-
+ Providing the URL to a logfile can help us trace the root cause of an issue much quicker. You can learn how to generate the logfile here:
+ https://i3wm.org/docs/debugging.html
+
+ Providing the logfile is optional.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 02251489..d35cc581 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,4 +1,7 @@
contact_links:
+ - name: Userguide
+ url: https://i3wm.org/docs/userguide.html
+ about: i3 User’s Guide
- name: Ask a question or request support for using i3
url: https://github.com/i3/i3/discussions/new
about: Ask a question or request support for using i3
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index c41d1cac..00000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,56 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
----
-
-
-
-## I'm submitting a…
-
-
-[ ] Bug
-[x] Feature Request
-[ ] Documentation Request
-[ ] Other (Please describe in detail)
-
-
-## Current Behavior
-
-
-## Desired Behavior
-
-
-## Impact
-
-
-[ ] This feature requires new configuration and/or commands
-
-
-## Environment
-
-Output of `i3 --moreversion 2>&-`:
-
-i3 version:
-
-
-
-
-- Linux Distribution & Version:
-- Are you using a compositor (e.g., xcompmgr or compton):
-
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100755
index 00000000..78ec54c1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,57 @@
+name: Feature request
+description: Suggest an idea for this project
+labels: [enhancement]
+body:
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Welcome
+ options:
+ - label: Yes, I've searched similar issues and discussions on GitHub and didn't find any.
+ required: true
+
+ - type: checkboxes
+ id: impact
+ attributes:
+ label: Impact
+ description: |-
+ Please note that at this point we focus on maintaining i3 and fixing bugs, and will rarely consider features which require further configuration or significant complexity.
+ In such cases you should consider and present specific benefits derived from adding this feature such that it can be weighed against the cost of additional complexity and maintenance.
+ Keep in mind that i3 provides a powerful way to interact with it through its IPC interface: https://i3wm.org/docs/ipc.html.
+ options:
+ - label: This feature requires new configuration and/or commands
+ required: false
+
+
+ - type: textarea
+ id: current
+ attributes:
+ label: Current Behavior
+ placeholder: |-
+ Describe the current behavior,
+ e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
+ validations:
+ required: true
+
+ - type: textarea
+ id: desired
+ attributes:
+ label: Desired Behavior
+ placeholder: |-
+ Describe the desired behavior you expect after mitigation of the issue,
+ e.g., »The window left next to the current window should be focused.«
+ validations:
+ required: true
+
+ - type: textarea
+ id: version
+ attributes:
+ label: i3 version
+ description: |-
+ Paste the output of
+ ```
+ i3 --moreversion 2>&-
+ ```
+ render: text
+ validations:
+ required: true
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 5e460a63..9e651eca 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -20,34 +20,36 @@ jobs:
DOCKER_EMAIL: ${{ secrets.DOCKER_EMAIL }}
DOCKER_USER: ${{ secrets.DOCKER_USER }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
- BALTO_TOKEN: ${{ secrets.BALTO_TOKEN }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: git fetch --prune --unshallow
- name: construct container name
run: |
echo "BASENAME=i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base.Dockerfile)" >> $GITHUB_ENV
- echo "BASENAME_386=i3wm/travis-base-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-386.Dockerfile)" >> $GITHUB_ENV
echo "BASENAME_UBUNTU=i3wm/travis-base-ubuntu:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu.Dockerfile)" >> $GITHUB_ENV
- echo "BASENAME_UBUNTU_386=i3wm/travis-base-ubuntu-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu-386.Dockerfile)" >> $GITHUB_ENV
- name: fetch or build Docker container
run: |
docker pull ${{ env.BASENAME }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME }} travis/travis-base.Dockerfile
- name: fetch or build extra Docker containers
+ if: github.ref == 'refs/heads/next' && matrix.compiler == 'gcc'
run: |
- echo "::group::Ubuntu amd64"
- ./travis/skip-pkg.sh || docker pull ${{ env.BASENAME_UBUNTU }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME_UBUNTU }} travis/travis-base-ubuntu.Dockerfile
- echo "::endgroup::"
- echo "::group::Debian i386"
- ./travis/skip-pkg.sh || docker pull ${{ env.BASENAME_386 }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME_386 }} travis/travis-base-386.Dockerfile
- echo "::endgroup::"
- echo "::group::Ubuntu i386"
- ./travis/skip-pkg.sh || docker pull ${{ env.BASENAME_UBUNTU_386 }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME_UBUNTU_386 }} travis/travis-base-ubuntu-386.Dockerfile
- echo "::endgroup::"
+ docker pull ${{ env.BASENAME_UBUNTU }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME_UBUNTU }} travis/travis-base-ubuntu.Dockerfile
- name: build i3
run: |
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} /bin/sh -c 'rm -rf build; mkdir -p build && cd build && CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Werror -fno-common" meson .. -Ddocs=true -Dmans=true -Db_sanitize=address && ninja -v'
+ CFLAGS="-Wformat -Wformat-security -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Werror -fno-common -D_FORTIFY_SOURCE=3"
+ if [ "${{ matrix.compiler }}" = "gcc" ]; then
+ CFLAGS="$CFLAGS -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=format"
+ fi
+ export CFLAGS
+ docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC -e CFLAGS ${{ env.BASENAME }} /bin/sh -c 'rm -rf build; mkdir -p build && cd build && meson setup .. -Ddocs=true -Dmans=true -Db_sanitize="address,undefined" --buildtype=debugoptimized && ninja -v'
+ - name: Upload docs html for manual inspection
+ uses: actions/upload-artifact@v4
+ with:
+ name: i3-docs
+ path: |
+ build/*.html
+ if: matrix.compiler == 'gcc'
- name: check spelling
run: |
docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/check-spelling.pl
@@ -55,48 +57,42 @@ jobs:
run: |
docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} ./travis/run-tests.sh
- name: Archive test logs
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: test-logs
path: build/testsuite-*
if: ${{ failure() }}
- name: build dist tarball
run: |
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} /bin/sh -c 'rm -rf distbuild; mkdir distbuild && cd distbuild && meson .. -Ddocs=true -Dmans=true && meson dist --no-tests'
+ docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} /bin/sh -c 'rm -rf distbuild; mkdir distbuild && cd distbuild && meson setup .. -Ddocs=true -Dmans=true && meson dist --no-tests'
- name: build Debian packages
+ if: github.ref == 'refs/heads/next' && matrix.compiler == 'gcc'
run: |
echo "::group::Debian amd64"
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/debian-build.sh deb/debian-amd64/DIST
+ docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/debian-build.sh deb/debian-amd64/DIST
echo "::endgroup::"
echo "::group::Ubuntu amd64"
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME_UBUNTU }} ./travis/debian-build.sh deb/ubuntu-amd64/DIST
+ docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME_UBUNTU }} ./travis/debian-build.sh deb/ubuntu-amd64/DIST
echo "::endgroup::"
- echo "::group::Debian i386"
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME_386 }} linux32 ./travis/debian-build.sh deb/debian-i386/DIST
- echo "::endgroup::"
- echo "::group::Ubuntu i386"
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME_UBUNTU_386 }} linux32 ./travis/debian-build.sh deb/ubuntu-i386/DIST
- echo "::endgroup::"
- - name: push Debian packages to balto
- run: |
- ./travis/skip-pkg.sh || travis/push-balto.sh
- name: build docs
+ if: github.ref == 'refs/heads/next' && matrix.compiler == 'gcc'
run: |
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/docs.sh
+ docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/docs.sh
- name: push docs to GitHub pages
+ if: github.ref == 'refs/heads/next' && matrix.compiler == 'gcc'
run: |
- ./travis/skip-pkg.sh || travis/deploy-github-pages.sh
+ travis/deploy-github-pages.sh
formatting:
name: Check formatting
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: check & print release notes
run: ./release-notes/generator.pl
- name: Install dependencies
run: |
- sudo apt-get install -y clang-format-12
+ sudo apt-get install -y clang-format-20
- name: Check formatting
- run: clang-format-12 --dry-run --Werror $(git ls-files '*.c' '*.h')
+ run: clang-format-20 --dry-run --Werror $(git ls-files '*.c' '*.h')
- name: Verify safe wrapper functions are used
run: ./travis/check-safe-wrappers.sh
diff --git a/.gitignore b/.gitignore
index 6ee317db..65debf08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,4 +50,3 @@ LAST_VERSION
# it is up to you to arrange for it to be ignored by git,
# e.g. by listing your directory in .git/info/exclude.
/build
-
diff --git a/AnyEvent-I3/Changes b/AnyEvent-I3/Changes
index d763437f..d9fe55a2 100644
--- a/AnyEvent-I3/Changes
+++ b/AnyEvent-I3/Changes
@@ -1,5 +1,14 @@
Revision history for AnyEvent-I3
+0.19 2024-04-09
+
+ * use Carp for errors (includes stacktraces)
+ * introduce (preferred) RUN_COMMAND spelling
+ * migrate tooling to ExtUtils::MakeMaker
+ * implement the tick event
+ * introduce the sync IPC command
+ * introduce the GET_BINDING_STATE IPC command
+
0.18 2017-08-19
* support the GET_CONFIG command
diff --git a/AnyEvent-I3/lib/AnyEvent/I3.pm b/AnyEvent-I3/lib/AnyEvent/I3.pm
index 1f4e5bd3..2d749d9e 100644
--- a/AnyEvent-I3/lib/AnyEvent/I3.pm
+++ b/AnyEvent-I3/lib/AnyEvent/I3.pm
@@ -8,7 +8,6 @@ use AnyEvent::Handle;
use AnyEvent::Socket;
use AnyEvent;
use Encode;
-use Scalar::Util qw(tainted);
use Carp;
=head1 NAME
@@ -17,11 +16,11 @@ AnyEvent::I3 - communicate with the i3 window manager
=cut
-our $VERSION = '0.18';
+our $VERSION = '0.19';
=head1 VERSION
-Version 0.18
+Version 0.19
=head1 SYNOPSIS
@@ -132,35 +131,10 @@ sub i3 {
AnyEvent::I3->new(@_)
}
-# Calls i3, even when running in taint mode.
sub _call_i3 {
my ($args) = @_;
- my $path_tainted = tainted($ENV{PATH});
- # This effectively circumvents taint mode checking for $ENV{PATH}. We
- # do this because users might specify PATH explicitly to call i3 in a
- # custom location (think ~/.bin/).
- (local $ENV{PATH}) = ($ENV{PATH} =~ /(.*)/);
-
- # In taint mode, we also need to remove all relative directories from
- # PATH (like . or ../bin). We only do this in taint mode and warn the
- # user, since this might break a real-world use case for some people.
- if ($path_tainted) {
- my @dirs = split /:/, $ENV{PATH};
- my @filtered = grep !/^\./, @dirs;
- if (scalar @dirs != scalar @filtered) {
- $ENV{PATH} = join ':', @filtered;
- warn qq|Removed relative directories from PATH because you | .
- qq|are running Perl with taint mode enabled. Remove -T | .
- qq|to be able to use relative directories in PATH. | .
- qq|New PATH is "$ENV{PATH}"|;
- }
- }
- # Otherwise the qx() operator wont work:
- delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
chomp(my $result = qx(i3 $args));
- # Circumventing taint mode again: the socket can be anywhere on the
- # system and that’s okay.
if ($result =~ /^([^\0]+)$/) {
return $1;
}
@@ -182,21 +156,21 @@ instance on the current DISPLAY which is almost always what you want.
sub new {
my ($class, $path) = @_;
- $path = _call_i3('--get-socketpath') unless $path;
-
- # This is the old default path (v3.*). This fallback line can be removed in
- # a year from now. -- Michael, 2012-07-09
- $path ||= '~/.i3/ipc.sock';
+ # We have I3SOCK now
+ $path ||= $ENV{I3SOCK};
+ $path ||= _call_i3('--get-socketpath');
# Check if we need to resolve ~
if ($path =~ /~/) {
- # We use getpwuid() instead of $ENV{HOME} because the latter is tainted
- # and thus produces warnings when running tests with perl -T
- my $home = (getpwuid($<))[7];
+ my $home = $ENV{HOME};
confess "Could not get home directory" unless $home and -d $home;
$path =~ s/~/$home/g;
}
+ if(!-S $path) {
+ die "$path is not a socket", $/;
+ }
+
bless { path => $path } => $class;
}
@@ -315,6 +289,11 @@ sub subscribe {
# Register callbacks for each message type
for my $key (keys %{$callbacks}) {
+ if (!exists $events{$key}) {
+ warn "Could not subscribe to event type '$key'." .
+ " Supported events are " . join(" ", sort keys %events), $/;
+ next;
+ }
my $type = $events{$key};
$self->{callbacks}->{$type} = $callbacks->{$key};
}
diff --git a/AnyEvent-I3/t/00-load.t b/AnyEvent-I3/t/00-load.t
index 4bf6151e..210ab153 100644
--- a/AnyEvent-I3/t/00-load.t
+++ b/AnyEvent-I3/t/00-load.t
@@ -1,4 +1,4 @@
-#!perl -T
+#!perl
use Test::More tests => 1;
diff --git a/AnyEvent-I3/t/01-workspaces.t b/AnyEvent-I3/t/01-workspaces.t
index f3206d89..8b12f111 100644
--- a/AnyEvent-I3/t/01-workspaces.t
+++ b/AnyEvent-I3/t/01-workspaces.t
@@ -1,4 +1,4 @@
-#!perl -T
+#!perl
# vim:ts=4:sw=4:expandtab
use Test::More tests => 3;
diff --git a/AnyEvent-I3/t/02-sugar.t b/AnyEvent-I3/t/02-sugar.t
index a3e2cc79..4811be44 100644
--- a/AnyEvent-I3/t/02-sugar.t
+++ b/AnyEvent-I3/t/02-sugar.t
@@ -1,4 +1,4 @@
-#!perl -T
+#!perl
# vim:ts=4:sw=4:expandtab
use Test::More tests => 3;
diff --git a/AnyEvent-I3/t/boilerplate.t b/AnyEvent-I3/t/boilerplate.t
index effb65b6..f4024158 100644
--- a/AnyEvent-I3/t/boilerplate.t
+++ b/AnyEvent-I3/t/boilerplate.t
@@ -1,4 +1,4 @@
-#!perl -T
+#!perl
use strict;
use warnings;
diff --git a/AnyEvent-I3/t/manifest.t b/AnyEvent-I3/t/manifest.t
index 45eb83fd..2dcbd43c 100644
--- a/AnyEvent-I3/t/manifest.t
+++ b/AnyEvent-I3/t/manifest.t
@@ -1,4 +1,4 @@
-#!perl -T
+#!perl
use strict;
use warnings;
diff --git a/AnyEvent-I3/t/pod.t b/AnyEvent-I3/t/pod.t
index ee8b18ad..b4791d4e 100644
--- a/AnyEvent-I3/t/pod.t
+++ b/AnyEvent-I3/t/pod.t
@@ -1,4 +1,4 @@
-#!perl -T
+#!perl
use strict;
use warnings;
diff --git a/PACKAGE-MAINTAINER b/PACKAGE-MAINTAINER
index c5a7bea6..3995c59c 100644
--- a/PACKAGE-MAINTAINER
+++ b/PACKAGE-MAINTAINER
@@ -27,7 +27,7 @@ https://mesonbuild.com/Quick-guide.html#compiling-a-meson-project
In case you’re unfamiliar:
$ mkdir -p build && cd build
- $ meson ..
+ $ meson setup
$ ninja
Please make sure that i3-migrate-config-to-v4 and i3-config-wizard are
diff --git a/RELEASE-NOTES-4.22 b/RELEASE-NOTES-4.22
deleted file mode 100644
index 36db5456..00000000
--- a/RELEASE-NOTES-4.22
+++ /dev/null
@@ -1,55 +0,0 @@
-
- ┌──────────────────────────────┐
- │ Release notes for i3 v4.22 │
- └──────────────────────────────┘
-
-This is i3 v4.22. This version is considered stable. All users of i3 are
-strongly encouraged to upgrade.
-
-The biggest change in this release is the merge of the i3-gaps fork.
-The i3-gaps fork was the most popular fork of i3, adding the option to
-show gaps between tiled windows and/or the screen edges.
-
-See https://i3wm.org/docs/userguide.html#gaps for more details.
-
-Instead of maintaining two versions of i3 (both upstream and downstream,
-meaning in Linux distributions and other package collections),
-we concluded it would be better for everyone to merge this feature.
-
-For users of i3: gaps are off by default, so there is no change in behavior.
-For users of i3-gaps: the configuration is compatible, so you can switch
-to i3 v4.22 or newer, without any changes in behavior.
-
-Thanks to Ingo Bürk for maintaining i3-gaps for many years,
-for becoming a core i3 maintainer and for helping make this merge possible!
-
- ┌────────────────────────────┐
- │ Changes in i3 v4.22 │
- └────────────────────────────┘
-
- • i3bar: bar { padding } config directive now implemented (supports bar { height } from i3-gaps)
- • i3-dmenu-desktop: allow more than one --entry-type with the --show-duplicates flag
- • You can now enable gaps using the gaps config directive and/or command
- • colors now support an optional alpha value at the end (#rrggbbaa)
- • the hide_edge_borders option now supports the smart_no_gaps keyword
- • Support nonprimary keyword for outputs
- • add "mode" field in binding event
-
- ┌────────────────────────────┐
- │ Bugfixes │
- └────────────────────────────┘
-
- • gaps: workspace gaps assignments are no longer order-dependent
- • Fix compliance to _MOTIF_WM_HINTS spec when all decorations are set
- • The floating_from and tiling_from criteria now also work in commands
-
- ┌────────────────────────────┐
- │ Thanks! │
- └────────────────────────────┘
-
-Thanks for testing, bugfixes, discussions and everything I forgot go out to:
-
- bodea, Demian, Erich Heine, Ingo Bürk, Matias Goldfeld, Orestis Floros,
- Tudor Brindus
-
--- Michael Stapelberg, 2023-01-02
diff --git a/RELEASE-NOTES-4.25 b/RELEASE-NOTES-4.25
new file mode 100644
index 00000000..c65b9254
--- /dev/null
+++ b/RELEASE-NOTES-4.25
@@ -0,0 +1,49 @@
+
+ ┌──────────────────────────────┐
+ │ Release notes for i3 v4.25 │
+ └──────────────────────────────┘
+
+This is i3 v4.25. This version is considered stable. All users of i3 are
+strongly encouraged to upgrade.
+
+Most changes in this release cycle have been “behind the scenes”,
+without an observable effect for users. For example, we switched
+to clang-format-20, fixed a number of (benign) memory issues,
+fixed a few flaky tests and maintained our GitHub Actions setup,
+e.g. for the shutdown of baltocdn (our old host for nightly builds).
+
+We also investigated shipping an i3.service systemd user unit,
+but could not make that work without breaking existing setups.
+If distribution maintainers (or anyone) has advice, please share
+(after reading up!) over in https://github.com/i3/i3/issues/5186
+
+ ┌────────────────────────────┐
+ │ Changes in i3 v4.25 │
+ └────────────────────────────┘
+
+ • Set _NET_FRAME_EXTENTS according to the actual decoration size.
+ This improves compatibility with picom and other software.
+ • The command parser is now reentrant, which fixes a few
+ advanced for_window usages (e.g. multiple criteria).
+
+ ┌────────────────────────────┐
+ │ Bugfixes │
+ └────────────────────────────┘
+
+ • fix paragraph separators cutting off window titles
+ • fix crash when a container parent is focused
+ and a tiling drag causes it to be killed
+ • fix crash when using for_window [...] reload
+ • fix append_layout when containers use a mark
+ • randr: fix memleak and use-after-free
+
+ ┌────────────────────────────┐
+ │ Thanks! │
+ └────────────────────────────┘
+
+Thanks for testing, bugfixes, discussions and everything I forgot go out to:
+
+ Dmitry, Emeric Planet, FedGuy699, Garrett Marcinak, Michele Piazzai,
+ Orestis Floros, Sergey Vlasov, Vladimir Panteleev, algonell
+
+-- Michael Stapelberg, 2025-12-19
diff --git a/contrib/banner.svg b/contrib/banner.svg
index af82021b..74881803 100644
--- a/contrib/banner.svg
+++ b/contrib/banner.svg
@@ -319,7 +319,7 @@
steckdenis
- Logo for I3, an improved dynamic tiling window manager: http://i3.zekjur.net/
+ Logo for i3 - an improved tiling window manager: http://i3.zekjur.net/
diff --git a/contrib/sticker_stickma_black.svg b/contrib/sticker_stickma_black.svg
index fced8dd9..18c3299a 100644
--- a/contrib/sticker_stickma_black.svg
+++ b/contrib/sticker_stickma_black.svg
@@ -329,7 +329,7 @@
steckdenis
- Logo for I3, an improved dynamic tiling window manager: http://i3.zekjur.net/
+ Logo for i3 - an improved tiling window manager: http://i3.zekjur.net/
diff --git a/debian/changelog b/debian/changelog
index 980224d0..12ecb36f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,20 @@
+i3-wm (4.24-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Michael Stapelberg Wed, 06 Nov 2024 18:34:06 +0100
+
+i3-wm (4.23-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Michael Stapelberg Sun, 29 Oct 2023 15:42:11 +0100
+
i3-wm (4.22-1) unstable; urgency=medium
* New upstream release.
- -- Michael Stapelberg Mon, 02 Jan 2023 09:34:02 +0100
+ -- Michael Stapelberg Mon, 02 Jan 2023 09:46:22 +0100
i3-wm (4.21.2-1) unstable; urgency=medium
diff --git a/debian/control b/debian/control
index 6ddc64e6..67e38ff9 100644
--- a/debian/control
+++ b/debian/control
@@ -44,7 +44,7 @@ Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
Provides: x-window-manager
Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl, rxvt-unicode | x-terminal-emulator
-Description: improved dynamic tiling window manager
+Description: improved tiling window manager
Key features of i3 are good documentation, reasonable defaults (changeable in
a simple configuration file) and good multi-monitor support. The user
interface is designed for power users and emphasizes keyboard usage. i3 uses
diff --git a/docs/NoName-2009-03-12/i3.tex b/docs/NoName-2009-03-12/i3.tex
index 4ca05f24..5cc92311 100644
--- a/docs/NoName-2009-03-12/i3.tex
+++ b/docs/NoName-2009-03-12/i3.tex
@@ -29,7 +29,7 @@
add_ignore_event, xcb_intern_atom, xcb_intern_atom_reply, fprintf, printf, free, load_configuration,%
XInternAtom, exit, strlen}}
}{}
-\title{i3 - an improved dynamic tiling window manager}
+\title{i3 - an improved tiling window manager}
\author{sECuRE beim NoName e.V.\\
~\\
powered by \LaTeX, of course}
diff --git a/docs/debugging b/docs/debugging
index 562a11f2..cfc260cc 100644
--- a/docs/debugging
+++ b/docs/debugging
@@ -147,16 +147,17 @@ After pressing "b" in the crash dialog, you will get a file called
id (PID) and the second one is incremented each time you generate a backtrace,
starting at 0.
-== Sending bug reports/debugging on IRC
+In Linux, if the backtrace just says +No stack.+, that's because gdb does not
+have necessary permissions to attach to a running process. You can fix that by
+running from a terminal (you can open a new tty, e.g. with ctrl-alt-F2):
-When sending bug reports, please attach the *whole* log file. Even if you think
-you found the section which clearly highlights the problem, additional
-information might be necessary to completely diagnose the problem.
+---------------------------------------------------------------------
+echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
+---------------------------------------------------------------------
-When debugging with us in IRC, be prepared to use a so-called nopaste service
-such as https://pastebin.com because pasting large amounts of text in IRC
-sometimes leads to incomplete lines (servers have line length limitations) or
-flood kicks.
+Afterwards, try re-generating the stack trace. Note that this setting re-sets
+after reboot, see more info at
+https://www.kernel.org/doc/Documentation/security/Yama.txt.
== Debugging i3bar
diff --git a/docs/hacking-howto b/docs/hacking-howto
index c6dd6fa4..d8c95ca4 100644
--- a/docs/hacking-howto
+++ b/docs/hacking-howto
@@ -14,7 +14,7 @@ you find necessary, please do not hesitate to contact me.
++++
This document is not 100% up to date. Specifically, everything up to and
-including <> has been updated recently. The rest might contain
+including <> has been updated recently. The rest might contain
outdated information.
++++
@@ -28,36 +28,24 @@ https://mesonbuild.com/[The Meson Build system]; see
https://mesonbuild.com/Quick-guide.html#compiling-a-meson-project[Quickstart
Guide → Compiling a Meson project]. In case you’re unfamiliar:
- $ mkdir -p build && cd build
- $ meson ..
- $ ninja
+ mkdir -p build
+ meson setup build
+ meson compile -C build
=== Build system features
-* We use the +AX_ENABLE_BUILDDIR+ macro to enforce builds happening in a separate
- directory. This is a prerequisite for the +AX_EXTEND_SRCDIR+ macro and building
- in a separate directory is common practice anyway. In case this causes any
- trouble when packaging i3 for your distribution, please open an issue.
+* +ninja test+ runs the i3 testsuite. See docs/testsuite for details.
-* +make check+ runs the i3 testsuite. See docs/testsuite for details.
+* +meson dist+ builds a release tarball and runs tests on the result.
-* +make distcheck+ (runs testsuite on +make dist+ result, tiny bit quicker
- feedback cycle than waiting for the travis build to catch the issue).
+* +meson -Ddocs=true -Dmans=true+ will enable the options to build docs and
+ manpages. These options require additional dependencies that are normally not
+ required for users who just want to build i3.
-* +make uninstall+ (occasionally requested by users who compile from source)
-
-* +make+ will build manpages/docs by default if the tools are installed.
- Conversely, manpages/docs are not tried to be built for users who don’t want
- to install all these dependencies to get started hacking on i3. Manpages and
- docs can be disabled with the +--disable-mans++ and ++--disable-docs++
- configure options respectively.
-
-* non-release builds will enable address sanitizer by default. Use the
- +--disable-sanitizers+ configure option to turn off all sanitizers, and see
- +--help+ for available sanitizers.
-
-* Coverage reports are now generated using +make check-code-coverage+, which
- requires specifying +--enable-code-coverage+ when calling configure.
+* +meson -Db_sanitize=address+ will enable the address sanitizer which is
+ disabled by default. A summary of memory leaks will be printed on program
+ exit. This can include false-positives. For other options of the +b_sanitize+
+ flag see https://mesonbuild.com/Builtin-options.html.
== Pull requests
@@ -341,30 +329,26 @@ ensure that the operating system on which i3 is compiled has all the expected
features, i3 comes with +include/queue.h+. On BSD systems, you can use +man
queue(3)+. On Linux, you have to use google (or read the source).
-The lists used are +SLIST+ (single linked lists), +CIRCLEQ+ (circular
-queues) and +TAILQ+ (tail queues). Usually, only forward traversal is necessary,
-so an +SLIST+ works fine. If inserting elements at arbitrary positions or at
-the end of a list is necessary, a +TAILQ+ is used instead. However, for the
-windows inside a container, a +CIRCLEQ+ is necessary to go from the currently
-selected window to the window above/below.
+The lists used are +SLIST+ (single linked lists), +CIRCLEQ+ (circular queues)
+and +TAILQ+ (tail queues). Usually, +TAILQ+ is used which allows inserting
+elements at arbitrary positions or at the end of the list. If only forward
+traversal is necessary, an +SLIST+ can be used. +CIRCLEQ+ is used just to
+manage the X11 state of each window.
-== Naming conventions
+[[startup]]
+== Startup (src/main.c, main())
-There is a row of standard variables used in many events. The following names
-should be chosen for those:
-
- * +conn+ is the xcb_connection_t
- * +event+ is the event of the particular type
- * +con+ names a container
- * +current+ is a loop variable when using +TAILQ_FOREACH+ etc.
-
-== Startup (src/mainx.c, main())
+Among other things, the main() function does the following:
* Establish the xcb connection
+ * Load the i3 config
* Check for XKB extension on the separate X connection, load Xcursor
- * Check for RandR screens (with a fall-back to Xinerama)
+ * Set up EWMH hints
* Grab the keycodes for which bindings exist
+ * Check for XRandR screens
* Manage all existing windows
+ * Exec configured startup processes
+ * Start i3bar if configured
* Enter the event loop
== Keybindings
diff --git a/docs/i3bar-workspace-protocol b/docs/i3bar-workspace-protocol
new file mode 100644
index 00000000..009b2a1d
--- /dev/null
+++ b/docs/i3bar-workspace-protocol
@@ -0,0 +1,184 @@
+i3bar workspace buttons protocol
+================================
+
+This document explains the protocol in which i3bar expects input for
+configuring workspace buttons. This feature is available since i3 version 4.23.
+
+The program defined by the +workspace_command+ configuration option for i3bar can
+modify the workspace buttons displayed by i3bar. The command should constantly
+print in its standard output a stream of messages following the protocol
+defined in this page.
+
+If you are looking for the status line protocol instead, see https://i3wm.org/docs/i3bar-protocol.html.
+
+== The protocol
+
+Each message should be a newline-delimited JSON array. The array is in the same
+format as the +GET_WORKSPACES+ ipc event, see
+https://i3wm.org/docs/ipc.html#_workspaces_reply.
+
+As an example, this is the output of the +i3-msg -t get_workspaces+ command:
+------------------------------
+[
+ {
+ "id": 94131549984064,
+ "num": 1,
+ "name": "1",
+ "visible": false,
+ "focused": false,
+ "output": "HDMI-A-0",
+ "urgent": false
+ },
+ {
+ "id": 94131550477584,
+ "num": 2,
+ "name": "2",
+ "visible": true,
+ "focused": true,
+ "output": "HDMI-A-0",
+ "urgent": false
+ },
+ {
+ "id": 94131550452704,
+ "num": 3,
+ "name": "3:some workspace",
+ "visible": false,
+ "focused": false,
+ "output": "HDMI-A-0",
+ "urgent": false
+ }
+]
+------------------------------
+
+Please note that this example was pretty printed for human consumption, with
+the +"rect"+ field removed. Workspace button commands should output each array
+in one line.
+
+Each element in the array represents a workspace. i3bar creates one workspace
+button for each element in the array. The order of these buttons is the same as
+the order of the elements in the array.
+
+In general, we recommend subscribing to the +workspace+ and +output+
+https://i3wm.org/docs/ipc.html#_workspace_event[events],
+fetching the current workspace information with +GET_WORKSPACES+, modifying the
+JSON array in the response according to your needs and then printing it to the
+standard output. However, you are free to build a new message from the ground
+up.
+
+=== Workspace objects in detail
+
+The documentation of +GET_WORKSPACES+ should be sufficient to understand the
+meaning of each property but here we provide extra notes for each property and
+its meaning with respect to i3bar.
+
+All properties but +name+ are optional.
+
+id (integer)::
+ If it is included it will be used to switch to that workspace when you
+ click the corresponding button. If it's not provided, the +name+ will be
+ used. You can use the +id+ field to present workspaces under a modified
+ name.
+num (integer)::
+ The only use of a workspace's number is if the +strip_workspace_numbers+
+ setting is enabled.
+name (string)::
+ The only required property. If an +id+ is provided you can freely change
+ the +name+ as you wish, effectively renaming the buttons of i3bar.
+visible (boolean)::
+ Defaults to +false+ if not included. +focused+ takes precedence over it,
+ however +visible+ is important for more than one monitor.
+focused (boolean)::
+ Defaults to +false+ if not included. Generally, exactly one of the
+ workspaces should be +focused+. If not, no button will have the
+ +focused_workspace+ color.
+urgent (boolean)::
+ Defaults to +false+ if not included.
+rect (map)::
+ Not used by i3bar but will be ignored.
+output (string)::
+ Defaults to the primary output if not included.
+
+== Examples
+
+These example scripts require the https://stedolan.github.io/jq/[jq] utility to
+be installed but otherwise just use the standard +i3-msg+ utility included with
+i3. However, you can write your own scripts in your preferred language, with
+the help of one of the
+https://i3wm.org/docs/ipc.html#_see_also_existing_libraries[pre-existing i3
+libraries]
+
+=== Base configuration
+
+------------------------------
+bar {
+ …
+ workspace_command /path/to/your/script.sh
+ …
+}
+------------------------------
+
+=== Re-create the default behaviour of i3bar
+
+Not very useful by itself but this will be the basic building block of all the
+following scripts. This one does not require +jq+.
+
+------------------------------
+#!/bin/sh
+i3-msg -t subscribe -m '["workspace", "output"]' | {
+ # Initially print the current workspaces before we receive any events. This
+ # avoids having an empty bar when starting up.
+ i3-msg -t get_workspaces;
+ # Then, while we receive events, update the workspace information.
+ while read EVENT; do i3-msg -t get_workspaces; done;
+}
+------------------------------
+
+=== Hide workspace named +foo+ unless if it is focused.
+
+------------------------------
+#!/bin/sh
+i3-msg -t subscribe -m '["workspace", "output"]' | {
+ i3-msg -t get_workspaces;
+ while read EVENT; do i3-msg -t get_workspaces; done;
+} | jq --unbuffered -c '[ .[] | select(.name != "foo" or .focused) ]'
+------------------------------
+
+Important! Make sure you use the +--unbuffered+ flag with +jq+, otherwise you
+might not get the changes in real-time but whenever they are flushed, which
+might mean that you are getting an empty bar until enough events are written.
+
+=== Show empty workspaces +foo+ and +bar+ on LVDS1 even if they do not exist at the moment.
+
+------------------------------
+#!/bin/sh
+i3-msg -t subscribe -m '["workspace", "output"]' | {
+ i3-msg -t get_workspaces;
+ while read EVENT; do i3-msg -t get_workspaces; done;
+} | jq --unbuffered -c '
+ def fake_ws(name): {
+ name: name,
+ output: "LVDS1",
+ };
+ . + [ fake_ws("foo"), fake_ws("bar") ] | unique_by(.name)
+'
+------------------------------
+
+=== Sort workspaces in reverse alphanumeric order
+
+------------------------------
+#!/bin/sh
+i3-msg -t subscribe -m '["workspace", "output"]' | {
+ i3-msg -t get_workspaces;
+ while read EVENT; do i3-msg -t get_workspaces; done;
+} | jq --unbuffered -c 'sort_by(.name) | reverse'
+------------------------------
+
+=== Append "foo" to the name of each workspace
+
+------------------------------
+#!/bin/sh
+i3-msg -t subscribe -m '["workspace", "output"]' | {
+ i3-msg -t get_workspaces;
+ while read EVENT; do i3-msg -t get_workspaces; done;
+} | jq --unbuffered -c '[ .[] | .name |= . + " foo" ]'
+------------------------------
diff --git a/docs/ipc b/docs/ipc
index 8d8bdfa5..de9eecd3 100644
--- a/docs/ipc
+++ b/docs/ipc
@@ -10,15 +10,22 @@ workspace bar.
The method of choice for IPC in our case is a unix socket because it has very
little overhead on both sides and is usually available without headaches in
-most languages. In the default configuration file, the ipc-socket gets created
-in +/tmp/i3-%u.XXXXXX/ipc-socket.%p+ where +%u+ is your UNIX username, +%p+ is
-the PID of i3 and XXXXXX is a string of random characters from the portable
-filename character set (see mkdtemp(3)). You can get the socketpath from i3 by
-executing +i3 --get-socketpath+, which will print the path to the standard
-output (plus a newline).
+most languages.
+By default i3 will set the path of the IPC socket based on:
-All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
-X11 property, stored on the X11 root window.
+1. The +ipc-socket+ configuration directive if it is used
+2. The +I3SOCK+ environmental variable if it is set
+3. +$XDG_RUNTIME_DIR/i3/ipc-socket.%p+ if the directory is available where +%p+
+ is the PID of i3 and XXXXXX is a string of random characters
+4. +/tmp/i3-%u.XXXXXX/ipc-socket.%p+ where +%u+ is your UNIX username
+
+You can get the socketpath from i3 by executing +i3 --get-socketpath+, which
+will print the path to the standard output (plus a newline) or by reading the
++I3SOCK+ environmental variable.
+
+All i3 utilities, like +i3-msg+ and +i3-input+ will determine the path of the
+IPC socket from the +I3SOCK+ environmental variable if it is set or the
++I3_SOCKET_PATH+ X11 property, stored on the X11 root window.
[WARNING]
.Use an existing library!
@@ -997,9 +1004,18 @@ if ($is_event) {
=== workspace event
This event consists of a single serialized map containing a property
-+change (string)+ which indicates the type of the change ("focus", "init",
-"empty", "urgent", "reload", "rename", "restored", "move"). A
-+current (object)+ property will be present with the affected workspace
++change (string)+ which indicates the type of the change.
+
+* +empty+ – the workspace has become empty
+* +focus+ – the workspace has received input focus
+* +init+ – the workspace has been created
+* +move+ – the workspace has been moved to a different output
+* +reload+ – i3 config has been reloaded
+* +rename+ – the workspace's name has changed
+* +restored+ – the workspace's layout has changed to a previously saved layout
+* +urgent+ – the workspace has become urgent or lost its urgent status
+
+A +current (object)+ property will be present with the affected workspace
whenever the type of event affects a workspace (otherwise, it will be +null+).
When the change is "focus", an +old (object)+ property will be present with the
diff --git a/docs/testsuite b/docs/testsuite
index ec87429c..22e96ab1 100644
--- a/docs/testsuite
+++ b/docs/testsuite
@@ -36,8 +36,8 @@ that, but it will also be useful for every future change.
Apart from this document, you should also have a look at:
-1. The "Modern Perl" book, which can be found at
- http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+1. The "Modern Perl" book:
+ https://i3wm.org/downloads/modern_perl_a4.pdf
2. The latest Perl documentation of the "i3test" (general testcase setup) and
"i3test::Test" (additional test instructions) modules:
https://build.i3wm.org/docs/lib-i3test.html respectively
@@ -76,30 +76,20 @@ used to install the testsuite. Many users prefer to use the more modern
The tests additionally require +Xephyr(1)+ to run a nested X server. Install
+xserver-xephyr+ on Debian or +xorg-server-xephyr+ on Arch Linux.
-.Installing testsuite dependencies using cpanminus (preferred)
+.Installing testsuite dependencies using cpanminus
--------------------------------------------------------------------------------
-$ cd ~/i3/testcases
-$ sudo apt-get install cpanminus
-$ sudo cpanm .
+# Install testsuite system-level dependencies. Xvfb is optional but recommended.
+$ sudo apt-get install xcb-proto cpanminus xvfb xserver-xephyr
+# Install dependencies in ~/perl5 local library
+$ cpanm --local-lib=~/perl5 local::lib App::cpanminus Module::Install
+# Activate the local library
+$ eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib)
+$ cd ~/i3/testcases/
+$ cpanm .
$ cd ~/i3/AnyEvent-I3
-$ sudo cpanm Module::Install
-$ sudo cpanm .
+$ cpanm .
--------------------------------------------------------------------------------
-If you don’t want to use cpanminus for some reason, the same works with cpan:
-
-.Installing testsuite dependencies using cpan
---------------------------------------------------------------------------------
-$ cd ~/i3/testcases
-$ sudo cpan .
-$ cd ~/i3/AnyEvent-I3
-$ sudo cpan Module::Install
-$ sudo cpan .
---------------------------------------------------------------------------------
-
-In case you don’t have root permissions, you can also install into your home
-directory, see https://michael.stapelberg.de/cpan/
-
=== Mechanisms
==== Script: complete-run
@@ -119,48 +109,57 @@ tests are run under Xvfb.
---------------------------------------
$ cd ~/i3
-$ mkdir -p build && cd build
+$ mkdir -p build
-$ meson ..
+$ meson setup build
+$ cd build
-$ ninja
+$ meson compile
# output omitted because it is very long
-$ cd testcases
-
$ ./complete-run.pl
# output omitted because it is very long
All tests successful.
Files=78, Tests=734, 27 wallclock secs ( 0.38 usr 0.48 sys + 17.65 cusr 3.21 csys = 21.72 CPU)
Result: PASS
-$ ./complete-run.pl t/04-floating.t
-[:3] i3 startup: took 0.07s, status = 1
-[:3] Running t/04-floating.t with logfile testsuite-2011-09-24-16-06-04-4.0.2-226-g1eb011a/i3-log-for-04-floating.t
-[:3] t/04-floating.t finished
-[:3] killing i3
-output for t/04-floating.t:
-ok 1 - use X11::XCB::Window;
-ok 2 - The object isa X11::XCB::Window
-ok 3 - Window is mapped
-ok 4 - i3 raised the width to 75
-ok 5 - i3 raised the height to 50
-ok 6 - i3 did not map it to (0x0)
-ok 7 - The object isa X11::XCB::Window
-ok 8 - i3 let the width at 80
-ok 9 - i3 let the height at 90
-ok 10 - i3 mapped it to x=1
-ok 11 - i3 mapped it to y=18
-ok 12 - The object isa X11::XCB::Window
-ok 13 - i3 let the width at 80
-ok 14 - i3 let the height at 90
-1..14
+$ ./complete-run.pl t/005-floating.t
+Running tests under Xvfb display :99
+Starting 1 Xephyr instances, starting at :100...
+
+Rough time estimate for this run: 9.65 seconds
+
+Writing logfile to 'testsuite-2024-05-01-21-33-45-4.23-28-g5834b7e8/complete-run.log'...
+[:100] i3/testcases/t/005-floating.t: finished
+completed 0 of 1 tests
All tests successful.
-Files=1, Tests=14, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.19 cusr 0.03 csys = 0.23 CPU)
+Files=1, Tests=13, 0 wallclock secs ( 0.00 usr + 0.00 sys = 0.00 CPU)
Result: PASS
-$ less latest/i3-log-for-04-floating.t
+The slowest tests are:
+ i3/testcases/t/005-floating.t with 0.07 seconds
+
+Test output:
+[:100] i3/testcases/t/005-floating.t: starting
+[:100] i3/testcases/t/005-floating.t: finished
+output for i3/testcases/t/005-floating.t:
+ok 1 - An object of class 'X11::XCB::Window' isa 'X11::XCB::Window'
+ok 2 - Window is mapped
+ok 3 - i3 raised the width to 75
+ok 4 - i3 raised the height to 50
+ok 5 - i3 did not map it to (0x0)
+ok 6 - An object of class 'X11::XCB::Window' isa 'X11::XCB::Window'
+ok 7 - i3 let the width at 80
+ok 8 - i3 let the height at 90
+ok 9 - i3 mapped it to x=20
+ok 10 - i3 mapped it to y=20
+ok 11 - An object of class 'X11::XCB::Window' isa 'X11::XCB::Window'
+ok 12 - i3 let the width at 80
+ok 13 - i3 let the height at 90
+1..13
+
+$ less latest/i3-log-for-005-floating.t
----------------------------------------
If your attempt to run the tests with a bare call to ./complete-run.pl fails, try this:
@@ -172,37 +171,34 @@ $ ./complete-run.pl --parallel=1 --keep-xserver-output
This will show the output of Xephyr, which is the X server implementation we
use for testing.
-===== make command: +make check+
-Make check runs the i3 testsuite.
-You can still use ./testcases/complete-run.pl to get the interactive progress output.
+===== ninja command: +ninja test+
++ninja test+ runs the i3 testsuite.
+You can still use ./complete-run.pl to get the interactive progress output.
-.Example invocation of +make check+
+.Example invocation of +ninja test+
---------------------------------------
$ cd ~/i3
-$ mkdir -p build && cd build
+$ mkdir -p build
-$ meson ..
+$ meson setup build
+$ cd build
-$ ninja
-# output omitted because it is very long
+$ ninja test
+[1/102] Generating config.h with a custom command
+[1/2] Running all tests.
+1/1 complete-run OK 34.39s
-$ make check
-# output omitted because it is very long
-PASS: testcases/complete-run.pl
-============================================================================
-Testsuite summary for i3 4.13
-============================================================================
-# TOTAL: 1
-# PASS: 1
-# SKIP: 0
-# XFAIL: 0
-# FAIL: 0
-# XPASS: 0
-# ERROR: 0
-============================================================================
+Ok: 1
+Expected Fail: 0
+Fail: 0
+Unexpected Pass: 0
+Skipped: 0
+Timeout: 0
-$ less test-suite.log
+Full log written to i3/build/meson-logs/testlog.txt
+
+$ less latest/complete-run.log
----------------------------------------
==== Coverage testing
diff --git a/docs/userguide b/docs/userguide
index 120e4b90..171afda4 100644
--- a/docs/userguide
+++ b/docs/userguide
@@ -202,7 +202,8 @@ Floating windows are always on top of tiling windows.
Since i3 4.21, it's possible to drag tiling containers using the mouse. The
drag can be initiated either by dragging the window's titlebar or by pressing
the <> and dragging the container while holding the
-left-click button.
+left-click button. See the <> option for configuring which
+action triggers the tiling drag.
Once the drag is initiated and the cursor has left the original container, drop
indicators are created according to the position of the cursor relatively to
@@ -215,6 +216,10 @@ Drop on container::
This happens when the mouse is relatively near the center of a container.
If the mouse is released, the result is exactly as if you had run the
+move container to mark+ command. See <>.
+ If the swap modifier is pressed before initiating the drag (+tiling_drag
+ swap_modifier+ set to Shift by default), the containers are swapped
+ instead. In that case, the result is exactly as if you had run the +swap
+ container with mark+ command. See <>.
Drop as sibling::
This happens when the mouse is relatively near the edge of a container. If
the mouse is released, the dragged container will become a sibling of the
@@ -317,7 +322,7 @@ single workspace on which you open three terminal windows. All these terminal
windows are directly attached to one node inside i3’s layout tree, the
workspace node. By default, the workspace node’s orientation is +horizontal+.
-Now you move one of these terminals down (+$mod+Shift+j+ by default). The
+Now you move one of these terminals down (+$mod+Shift+k+ by default). The
workspace node’s orientation will be changed to +vertical+. The terminal window
you moved down is directly attached to the workspace and appears on the bottom
of the screen. A new (horizontal) container was created to accommodate the
@@ -353,15 +358,6 @@ keyboard layout. To start the wizard, use the command +i3-config-wizard+.
Please note that you must not have +~/.i3/config+, otherwise the wizard will
exit.
-Since i3 4.0, a new configuration format is used. i3 will try to automatically
-detect the format version of a config file based on a few different keywords,
-but if you want to make sure that your config is read with the new format,
-include the following line in your config file:
-
----------------------
-# i3 config file (v4)
----------------------
-
[[include]]
=== Include directive
@@ -511,8 +507,8 @@ your bindings in the same physical location on the keyboard, use keycodes.
If you don’t switch layouts, and want a clean and simple config file, use
keysyms.
-Some tools (such as +import+ or +xdotool+) might be unable to run upon a
-KeyPress event, because the keyboard/pointer is still grabbed. For these
+Some tools (such as +xdotool+) might be unable to run upon a
+KeyPress event, because the keyboard is still grabbed. For these
situations, the +--release+ flag can be used, which will execute the command
after the keys have been released.
@@ -742,8 +738,11 @@ This option determines which border style *new* windows will have. The default
+normal+. Note that default_floating_border applies only to windows which are starting out as
floating windows, e.g., dialog windows, but not windows that are floated later on.
-Setting border style to +pixel+ eliminates title bars. The border style +normal+ allows you to
-adjust edge border width while keeping your title bar.
+Setting border style to +pixel+ eliminates title bars in split layouts. The border style
++normal+ allows you to adjust edge border width while keeping your title bar.
+
+The title bar is always visible in stacking and tabbed layouts, and this cannot be changed
+through configuration.
*Syntax*:
---------------------------------------------
@@ -788,6 +787,10 @@ The "smart_no_gaps" setting hides edge-specific borders of a container if the
container is the only container on its workspace and the gaps to the screen edge
are +0+.
+[[_smart_borders]]
++hide_edge_borders+ has replaced the old +smart_borders+ syntax. Use the former
+instead of the latter.
+
*Syntax*:
-----------------------------------------------
hide_edge_borders none|vertical|horizontal|both|smart|smart_no_gaps
@@ -798,27 +801,6 @@ hide_edge_borders none|vertical|horizontal|both|smart|smart_no_gaps
hide_edge_borders vertical
----------------------
-[[_smart_borders]]
-=== Smart borders
-
-Smart borders will draw borders on windows only if there is more than one window
-in a workspace. This feature can also be enabled only if the gap size between
-window and screen edge is +0+.
-
-*Syntax*:
------------------------------------------------
-smart_borders on|off|no_gaps
------------------------------------------------
-
-*Example*:
-----------------------
-# Activate smart borders (always)
-smart_borders on
-
-# Activate smart borders (only when there are effectively no gaps)
-smart_borders no_gaps
-----------------------
-
[[for_window]]
=== Arbitrary commands for specific windows (for_window)
@@ -1242,19 +1224,21 @@ mouse_warping none
When you are in fullscreen mode, some applications still open popup windows
(take Xpdf for example). This is because these applications might not be aware
that they are in fullscreen mode (they do not check the corresponding hint).
-There are three things which are possible to do in this situation:
+i3 supports four options for this situation:
-1. Display the popup if it belongs to the fullscreen application only. This is
- the default and should be reasonable behavior for most users.
-2. Just ignore the popup (don’t map it). This won’t interrupt you while you are
- in fullscreen. However, some apps might react badly to this (deadlock until
- you go out of fullscreen).
-3. Leave fullscreen mode.
+1. +smart+: Display the popup if it belongs to the fullscreen application only.
+ This is the default and should be reasonable behavior for most users.
+2. +ignore+: Just ignore the popup (don’t map it). This won’t interrupt you
+ while you are in fullscreen. However, some apps might react badly to this
+ (deadlock until you go out of fullscreen).
+3. +leave_fullscreen+: Leave fullscreen mode.
+4. +all+: Since i3 4.24: Display all floating windows regardless to which
+ application they belong to.
*Syntax*:
------------------------------------------------------
-popup_during_fullscreen smart|ignore|leave_fullscreen
------------------------------------------------------
+---------------------------------------------------------
+popup_during_fullscreen smart|ignore|leave_fullscreen|all
+---------------------------------------------------------
*Example*:
------------------------------
@@ -1438,10 +1422,18 @@ bindsym Mod1+F fullscreen toggle
You can configure how to initiate the tiling drag feature (see <>).
+The default is +modifier+.
+
+Since i3 4.24, you can configure a modifier key which, when pressed, will swap
+instead of moving containers when dropping directly onto another container.
+Defaults to +Shift+. Note that you have to be pressing both the floating
+modifier and the swap modifier before the drag is initiated.
+
*Syntax*:
--------------------------------
tiling_drag off
tiling_drag modifier|titlebar [modifier|titlebar]
+tiling_drag swap_modifier
--------------------------------
*Examples*:
@@ -1454,6 +1446,14 @@ tiling_drag modifier titlebar
# Disable tiling drag altogether
tiling_drag off
+
+# Use Control to swap containers
+tiling_drag swap_modifier Control
+
+# Setting the swap_modifier to be the same key as the floating modifier will
+# always swap without the need to hold two keys
+floating_modifier Mod4
+tiling_drag swap_modifier Mod4
--------------------------------
[[gaps]]
@@ -1611,6 +1611,30 @@ bar {
}
-------------------------------------------------
+[[workspace_command]]
+=== Workspace buttons command
+
+Since i3 4.23, i3bar can run a program and use its +stdout+ output to define
+the workspace buttons displayed on the left hand side of the bar. With this
+feature, you can, for example, rename the buttons of workspaces, hide specific
+workspaces, always show a workspace button even if the workspace does not exist
+or change the order of the buttons.
+
+Also see <> for the statusline option and
+https://i3wm.org/docs/i3bar-workspace-protocol.html for the detailed protocol.
+
+*Syntax*:
+------------------------
+workspace_command
+------------------------
+
+*Example*:
+-------------------------------------------------
+bar {
+ workspace_command /path/to/script.sh
+}
+-------------------------------------------------
+
=== Display mode
You can either have i3bar be visible permanently at one edge of the screen
@@ -2174,6 +2198,10 @@ bindsym $mod+x [class="(?i)firefox"] kill
# kill only the About dialog from Firefox
bindsym $mod+x [class="Firefox" window_role="About"] kill
+# kill all windows except for Firefox and Gnome Terminal.
+# case-insensitive and uses negative lookaheads, supported by PCRE
+bindsym $mod+x [class="^(?i)(?!firefox)(?!gnome-terminal).*"] kill
+
# enable floating mode and move container to workspace 4
for_window [class="^evil-app$"] floating enable, move container to workspace 4
@@ -2397,6 +2425,9 @@ available:
::
Sets focus to the container that matches the specified criteria.
See <>.
+workspace::
+ Sets focus to the workspace that contains the container that matches the
+ specified criteria.
left|right|up|down::
Sets focus to the nearest container in the given direction.
parent::
@@ -2423,6 +2454,7 @@ output::
*Syntax*:
----------------------------------------------
focus
+ focus workspace
focus left|right|down|up
focus parent|child|floating|tiling|mode_toggle
focus next|prev [sibling]
@@ -2434,6 +2466,10 @@ focus output left|right|down|up|current|primary|nonprimary|next| [outpu
# Focus firefox
bindsym $mod+F1 [class="Firefox"] focus
+# Focus the workspace where firefox is, without necessarily focusing firefox
+# itself.
+bindsym $mod+x [class="Firefox"] focus workspace
+
# Focus container on the left, bottom, top, right
bindsym $mod+j focus left
bindsym $mod+k focus down
@@ -2447,7 +2483,7 @@ bindsym $mod+u focus parent
bindsym $mod+g focus mode_toggle
# Focus the next output (effectively toggles when you only have two outputs)
-bindsym $mod+x move workspace to output next
+bindsym $mod+x focus output next
# Focus the output right to the current one
bindsym $mod+x focus output right
@@ -2462,7 +2498,7 @@ bindsym $mod+x focus output primary
bindsym $mod+x focus output nonprimary
# Cycle focus between outputs VGA1 and LVDS1 but not DVI0
-bindsym $mod+x move workspace to output VGA1 LVDS1
+bindsym $mod+x focus output VGA1 LVDS1
-------------------------------------------------
Note that you might not have a primary output configured yet. To do so, run:
@@ -2519,6 +2555,7 @@ bindsym $mod+c move absolute position center
bindsym $mod+m move position mouse
-------------------------------------------------------
+[[swapping_containers]]
=== Swapping containers
Two containers can be swapped (i.e., move to each other's position) by using
diff --git a/generate-command-parser.pl b/generate-command-parser.pl
index 17728736..42f2da15 100755
--- a/generate-command-parser.pl
+++ b/generate-command-parser.pl
@@ -1,7 +1,7 @@
#!/usr/bin/env perl
# vim:ts=4:sw=4:expandtab
#
-# i3 - an improved dynamic tiling window manager
+# i3 - an improved tiling window manager
# © 2009 Michael Stapelberg and contributors (see also: LICENSE)
#
# generate-command-parser.pl: script to generate parts of the command parser
@@ -147,11 +147,12 @@ for my $state (@keys) {
$next_state ||= 'INITIAL';
my $fmt = $cmd;
# Replace the references to identified literals (like $workspace) with
- # calls to get_string(). Also replaces state names (like FOR_WINDOW)
- # with their ID (useful for cfg_criteria_init(FOR_WINDOW) e.g.).
+ # calls to parser_get_string(). Also replaces state names (like
+ # FOR_WINDOW) with their ID (useful for cfg_criteria_init(FOR_WINDOW)
+ # e.g.).
$cmd =~ s/$_/$statenum{$_}/g for @keys;
- $cmd =~ s/\$([a-z_]+)/get_string(stack, "$1")/g;
- $cmd =~ s/\&([a-z_]+)/get_long(stack, "$1")/g;
+ $cmd =~ s/\$([a-z_]+)/parser_get_string(stack, "$1")/g;
+ $cmd =~ s/\&([a-z_]+)/parser_get_long(stack, "$1")/g;
# For debugging/testing, we print the call using printf() and thus need
# to generate a format string. The format uses %d for s,
# literal numbers or state IDs and %s for NULL, s and literal
diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c
index c5ec071e..5d08ca21 100644
--- a/i3-config-wizard/main.c
+++ b/i3-config-wizard/main.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-config-wizard: Program to convert configs using keycodes to configs using
@@ -75,9 +75,9 @@ xcb_visualtype_t *visual_type = NULL;
#define WIN_HEIGHT (15 * font.height + TEXT_PADDING)
#define col_x(col) \
- (((col)-1) * char_width + TEXT_PADDING)
+ (((col) - 1) * char_width + TEXT_PADDING)
#define row_y(row) \
- (((row)-1) * font.height + TEXT_PADDING)
+ (((row) - 1) * font.height + TEXT_PADDING)
enum { STEP_WELCOME,
STEP_GENERATE } current_step = STEP_WELCOME;
@@ -157,8 +157,9 @@ static struct stack_entry stack[10];
static void push_string(const char *identifier, const char *str) {
for (int c = 0; c < 10; c++) {
if (stack[c].identifier != NULL &&
- strcmp(stack[c].identifier, identifier) != 0)
+ strcmp(stack[c].identifier, identifier) != 0) {
continue;
+ }
if (stack[c].identifier == NULL) {
/* Found a free slot, let’s store it here. */
stack[c].identifier = identifier;
@@ -184,8 +185,9 @@ static void push_string(const char *identifier, const char *str) {
static void push_long(const char *identifier, long num) {
for (int c = 0; c < 10; c++) {
- if (stack[c].identifier != NULL)
+ if (stack[c].identifier != NULL) {
continue;
+ }
/* Found a free slot, let’s store it here. */
stack[c].identifier = identifier;
stack[c].val.num = num;
@@ -204,18 +206,21 @@ static void push_long(const char *identifier, long num) {
static const char *get_string(const char *identifier) {
for (int c = 0; c < 10; c++) {
- if (stack[c].identifier == NULL)
+ if (stack[c].identifier == NULL) {
break;
- if (strcmp(identifier, stack[c].identifier) == 0)
+ }
+ if (strcmp(identifier, stack[c].identifier) == 0) {
return stack[c].val.str;
+ }
}
return NULL;
}
static void clear_stack(void) {
for (int c = 0; c < 10; c++) {
- if (stack[c].type == STACK_STR)
+ if (stack[c].type == STACK_STR) {
free(stack[c].val.str);
+ }
stack[c].identifier = NULL;
stack[c].val.str = NULL;
stack[c].val.num = 0;
@@ -233,11 +238,13 @@ static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) {
max_keycode = xcb_get_setup(conn)->max_keycode;
for (i = min_keycode; i && i <= max_keycode; i++) {
- if (i == except_keycode)
+ if (i == except_keycode) {
continue;
+ }
for (int level = 0; level < 4; level++) {
- if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym)
+ if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym) {
continue;
+ }
return true;
}
}
@@ -269,22 +276,27 @@ static char *next_state(const cmdp_token *token) {
* qwerty (yes, that happens quite often). */
const xkb_keysym_t *syms;
int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, 0, &syms);
- if (num == 0)
+ if (num == 0) {
errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
- if (!keysym_used_on_other_key(syms[0], keycode))
+ }
+ if (!keysym_used_on_other_key(syms[0], keycode)) {
level = 0;
+ }
}
const xkb_keysym_t *syms;
int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, level, &syms);
- if (num == 0)
+ if (num == 0) {
errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
- if (num > 1)
+ }
+ if (num > 1) {
printf("xkb_keymap_key_get_syms_by_level (keycode = %d) returned %d symbolsinstead of 1, using only the first one.\n", keycode, num);
+ }
char str[4096];
- if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1)
+ if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1) {
errx(EXIT_FAILURE, "xkb_keysym_get_name(%u) failed", syms[0]);
+ }
const char *release = get_string("release");
char *res;
char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers));
@@ -303,8 +315,9 @@ static char *next_state(const cmdp_token *token) {
/* See if we are jumping back to a state in which we were in previously
* (statelist contains INITIAL) and just move statelist_idx accordingly. */
for (int i = 0; i < statelist_idx; i++) {
- if (statelist[i] != _next_state)
+ if (statelist[i] != _next_state) {
continue;
+ }
statelist_idx = i + 1;
return NULL;
}
@@ -329,8 +342,9 @@ static char *rewrite_binding(const char *input) {
while ((size_t)(walk - input) <= len) {
/* Skip whitespace before every token, newlines are relevant since they
* separate configuration directives. */
- while ((*walk == ' ' || *walk == '\t') && *walk != '\0')
+ while ((*walk == ' ' || *walk == '\t') && *walk != '\0') {
walk++;
+ }
cmdp_token_ptr *ptr = &(tokens[state]);
for (c = 0; c < ptr->n; c++) {
@@ -339,11 +353,13 @@ static char *rewrite_binding(const char *input) {
/* A literal. */
if (token->name[0] == '\'') {
if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
- if (token->identifier != NULL)
+ if (token->identifier != NULL) {
push_string(token->identifier, token->name + 1);
+ }
walk += strlen(token->name) - 1;
- if ((result = next_state(token)) != NULL)
+ if ((result = next_state(token)) != NULL) {
return result;
+ }
break;
}
continue;
@@ -355,20 +371,24 @@ static char *rewrite_binding(const char *input) {
errno = 0;
long int num = strtol(walk, &end, 10);
if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) ||
- (errno != 0 && num == 0))
+ (errno != 0 && num == 0)) {
continue;
+ }
/* No valid numbers found */
- if (end == walk)
+ if (end == walk) {
continue;
+ }
- if (token->identifier != NULL)
+ if (token->identifier != NULL) {
push_long(token->identifier, num);
+ }
/* Set walk to the first non-number character */
walk = end;
- if ((result = next_state(token)) != NULL)
+ if ((result = next_state(token)) != NULL) {
return result;
+ }
break;
}
@@ -379,12 +399,14 @@ static char *rewrite_binding(const char *input) {
if (*walk == '"') {
beginning++;
walk++;
- while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\'))
+ while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\')) {
walk++;
+ }
} else {
if (token->name[0] == 's') {
- while (*walk != '\0' && *walk != '\r' && *walk != '\n')
+ while (*walk != '\0' && *walk != '\r' && *walk != '\n') {
walk++;
+ }
} else {
/* For a word, the delimiters are white space (' ' or
* '\t'), closing square bracket (]), comma (,) and
@@ -392,8 +414,9 @@ static char *rewrite_binding(const char *input) {
while (*walk != ' ' && *walk != '\t' &&
*walk != ']' && *walk != ',' &&
*walk != ';' && *walk != '\r' &&
- *walk != '\n' && *walk != '\0')
+ *walk != '\n' && *walk != '\0') {
walk++;
+ }
}
}
if (walk != beginning) {
@@ -406,27 +429,32 @@ static char *rewrite_binding(const char *input) {
/* We only handle escaped double quotes to not break
* backwards compatibility with people using \w in
* regular expressions etc. */
- if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
+ if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"') {
inpos++;
+ }
str[outpos] = beginning[inpos];
}
- if (token->identifier)
+ if (token->identifier) {
push_string(token->identifier, str);
+ }
free(str);
/* If we are at the end of a quoted string, skip the ending
* double quote. */
- if (*walk == '"')
+ if (*walk == '"') {
walk++;
- if ((result = next_state(token)) != NULL)
+ }
+ if ((result = next_state(token)) != NULL) {
return result;
+ }
break;
}
}
if (strcmp(token->name, "end") == 0) {
if (*walk == '\0' || *walk == '\n' || *walk == '\r') {
- if ((result = next_state(token)) != NULL)
+ if ((result = next_state(token)) != NULL) {
return result;
+ }
/* To make sure we start with an appropriate matching data
* structure for commands which do *not* specify any
* criteria, we re-initialize the criteria system after
@@ -513,17 +541,19 @@ static int handle_expose(void) {
txt(13, 10, "to abort", white, black);
/* the not-selected modifier */
- if (modifier == MOD_Mod4)
+ if (modifier == MOD_Mod4) {
txt(5, 5, "", white, black);
- else
+ } else {
txt(5, 4, "", white, black);
+ }
/* the selected modifier */
set_font(&bold_font);
- if (modifier == MOD_Mod4)
+ if (modifier == MOD_Mod4) {
txt(2, 4, "-> ", white, black);
- else
+ } else {
txt(2, 5, "-> ", white, black);
+ }
set_font(&font);
txt(4, 9, "", green, black);
@@ -562,8 +592,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
strlen("i3: generate config"),
"i3: generate config");
xcb_flush(conn);
- } else
+ } else {
finish();
+ }
}
/* Swap between modifiers when up or down is pressed. */
@@ -573,8 +604,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
}
/* cancel any time */
- if (sym == XK_Escape)
+ if (sym == XK_Escape) {
exit(0);
+ }
/* Check if this is Mod1 or Mod4. The modmap contains Shift, Lock, Control,
* Mod1, Mod2, Mod3, Mod4, Mod5 (in that order) */
@@ -583,8 +615,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
int mask = 3;
for (int i = 0; i < modmap_reply->keycodes_per_modifier; i++) {
xcb_keycode_t code = modmap[(mask * modmap_reply->keycodes_per_modifier) + i];
- if (code == XCB_NONE)
+ if (code == XCB_NONE) {
continue;
+ }
printf("Modifier keycode for Mod1: 0x%02x\n", code);
if (code == event->detail) {
modifier = MOD_Mod1;
@@ -596,8 +629,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
mask = 6;
for (int i = 0; i < modmap_reply->keycodes_per_modifier; i++) {
xcb_keycode_t code = modmap[(mask * modmap_reply->keycodes_per_modifier) + i];
- if (code == XCB_NONE)
+ if (code == XCB_NONE) {
continue;
+ }
printf("Modifier keycode for Mod4: 0x%02x\n", code);
if (code == event->detail) {
modifier = MOD_Mod4;
@@ -614,11 +648,13 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
*
*/
static void handle_button_press(xcb_button_press_event_t *event) {
- if (current_step != STEP_GENERATE)
+ if (current_step != STEP_GENERATE) {
return;
+ }
- if (event->event_x < col_x(5) || event->event_x > col_x(10))
+ if (event->event_x < col_x(5) || event->event_x > col_x(10)) {
return;
+ }
if (event->event_y >= row_y(4) && event->event_y <= (row_y(4) + font.height)) {
modifier = MOD_Mod4;
@@ -640,20 +676,24 @@ static void finish(void) {
struct xkb_context *xkb_context;
- if ((xkb_context = xkb_context_new(0)) == NULL)
+ if ((xkb_context = xkb_context_new(0)) == NULL) {
errx(1, "could not create xkbcommon context");
+ }
int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn);
- if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL)
+ if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL) {
errx(1, "xkb_x11_keymap_new_from_device failed");
+ }
FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
- if (kc_config == NULL)
+ if (kc_config == NULL) {
err(1, "Could not open input file \"%s\"", SYSCONFDIR "/i3/config.keycodes");
+ }
FILE *ks_config = fopen(config_path, "w");
- if (ks_config == NULL)
+ if (ks_config == NULL) {
err(1, "Could not open output config file \"%s\"", config_path);
+ }
free(config_path);
char *line = NULL;
@@ -684,8 +724,9 @@ static void finish(void) {
#endif
/* skip the warning block at the beginning of the input file */
if (head_of_file &&
- strncmp("# WARNING", line, strlen("# WARNING")) == 0)
+ strncmp("# WARNING", line, strlen("# WARNING")) == 0) {
continue;
+ }
head_of_file = false;
@@ -699,10 +740,11 @@ static void finish(void) {
/* Set the modifier the user chose */
if (strncmp(walk, "set $mod ", strlen("set $mod ")) == 0) {
- if (modifier == MOD_Mod1)
+ if (modifier == MOD_Mod1) {
fputs("set $mod Mod1\n", ks_config);
- else
+ } else {
fputs("set $mod Mod4\n", ks_config);
+ }
continue;
}
@@ -767,12 +809,13 @@ int main(int argc, char *argv[]) {
return 0;
case 'm':
headless_run = true;
- if (strcmp(optarg, "alt") == 0)
+ if (strcmp(optarg, "alt") == 0) {
modifier = MOD_Mod1;
- else if (strcmp(optarg, "win") == 0)
+ } else if (strcmp(optarg, "win") == 0) {
modifier = MOD_Mod4;
- else
+ } else {
err(EXIT_FAILURE, "Invalid modifier key %s", optarg);
+ }
break;
case 'h':
printf("i3-config-wizard " I3_VERSION "\n");
@@ -789,8 +832,9 @@ int main(int argc, char *argv[]) {
}
/* Always write to $XDG_CONFIG_HOME/i3/config by default. */
- if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
+ if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) {
xdg_config_home = "~/.config";
+ }
xdg_config_home = resolve_tilde(xdg_config_home);
sasprintf(&config_path, "%s/i3/config", xdg_config_home);
@@ -799,9 +843,11 @@ int main(int argc, char *argv[]) {
char *config_dir;
struct stat stbuf;
sasprintf(&config_dir, "%s/i3", xdg_config_home);
- if (stat(config_dir, &stbuf) != 0)
- if (mkdirp(config_dir, DEFAULT_DIR_MODE) != 0)
+ if (stat(config_dir, &stbuf) != 0) {
+ if (mkdirp(config_dir, DEFAULT_DIR_MODE) != 0) {
err(EXIT_FAILURE, "mkdirp(%s) failed", config_dir);
+ }
+ }
free(config_dir);
free(xdg_config_home);
@@ -815,8 +861,9 @@ int main(int argc, char *argv[]) {
int screen;
if ((conn = xcb_connect(NULL, &screen)) == NULL ||
- xcb_connection_has_error(conn))
+ xcb_connection_has_error(conn)) {
errx(1, "Cannot open display");
+ }
if (xkb_x11_setup_xkb_extension(conn,
XKB_X11_MIN_MAJOR_XKB_VERSION,
@@ -825,8 +872,9 @@ int main(int argc, char *argv[]) {
NULL,
NULL,
&xkb_base_event,
- &xkb_base_error) != 1)
+ &xkb_base_error) != 1) {
errx(EXIT_FAILURE, "Could not setup XKB extension.");
+ }
keysyms = xcb_key_symbols_alloc(conn);
xcb_get_modifier_mapping_cookie_t modmap_cookie;
@@ -852,8 +900,9 @@ int main(int argc, char *argv[]) {
root_screen = xcb_aux_get_screen(conn, screen);
root = root_screen->root;
- if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL)))
+ if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL))) {
errx(EXIT_FAILURE, "Could not get modifier mapping");
+ }
xcb_numlock_mask = get_mod_mask_for(XCB_NUM_LOCK, symbols, modmap_reply);
diff --git a/i3-dump-log/main.c b/i3-dump-log/main.c
index 0ce22264..35872ccd 100644
--- a/i3-dump-log/main.c
+++ b/i3-dump-log/main.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-dump-log/main.c: Dumps the i3 SHM log to stdout.
@@ -39,8 +39,9 @@ static int ipcfd = -1;
static void disable_shmlog(void) {
const char *disablecmd = "debuglog off; shmlog off";
if (ipc_send_message(ipcfd, strlen(disablecmd),
- I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)disablecmd) != 0)
+ I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)disablecmd) != 0) {
err(EXIT_FAILURE, "IPC send");
+ }
/* Ensure the command was sent by waiting for the reply: */
uint32_t reply_length = 0;
@@ -53,8 +54,9 @@ static void disable_shmlog(void) {
}
static int check_for_wrap(void) {
- if (wrap_count == header->wrap_count)
+ if (wrap_count == header->wrap_count) {
return 0;
+ }
/* The log wrapped. Print the remaining content and reset walk to the top
* of the log. */
@@ -94,8 +96,7 @@ int main(int argc, char *argv[]) {
{"follow", no_argument, 0, 'f'},
#endif
{"help", no_argument, 0, 'h'},
- {0, 0, 0, 0}
- };
+ {0, 0, 0, 0}};
#if !defined(__OpenBSD__)
char *options_string = "s:vfVh";
@@ -148,8 +149,9 @@ int main(int argc, char *argv[]) {
ipcfd = ipc_connect(NULL);
const char *enablecmd = "debuglog on; shmlog 5242880";
if (ipc_send_message(ipcfd, strlen(enablecmd),
- I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)enablecmd) != 0)
+ I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)enablecmd) != 0) {
err(EXIT_FAILURE, "IPC send");
+ }
/* By the time we receive a reply, I3_SHMLOG_PATH is set: */
uint32_t reply_length = 0;
uint8_t *reply = NULL;
@@ -175,8 +177,9 @@ int main(int argc, char *argv[]) {
}
}
- if (*shmname == '\0')
+ if (*shmname == '\0') {
errx(EXIT_FAILURE, "Cannot dump log: SHM logging is disabled in i3.");
+ }
struct stat statbuf;
@@ -211,8 +214,9 @@ int main(int argc, char *argv[]) {
/* In case there was a write to the buffer already, skip the first
* old line, it very likely is mangled. Not a problem, though, the log
* is chatty enough to have plenty lines left. */
- while (*walk != '\n')
+ while (*walk != '\n') {
walk++;
+ }
walk++;
}
diff --git a/i3-input/keysym2ucs.c b/i3-input/keysym2ucs.c
index 80375099..7de7a37f 100644
--- a/i3-input/keysym2ucs.c
+++ b/i3-input/keysym2ucs.c
@@ -822,21 +822,23 @@ long keysym2ucs(xcb_keysym_t keysym) {
/* first check for Latin-1 characters (1:1 mapping) */
if ((keysym >= 0x0020 && keysym <= 0x007e) ||
- (keysym >= 0x00a0 && keysym <= 0x00ff))
+ (keysym >= 0x00a0 && keysym <= 0x00ff)) {
return keysym;
+ }
/* also check for directly encoded 24-bit UCS characters */
- if ((keysym & 0xff000000) == 0x01000000)
+ if ((keysym & 0xff000000) == 0x01000000) {
return keysym & 0x00ffffff;
+ }
/* binary search in table */
while (max >= min) {
mid = (min + max) / 2;
- if (keysymtab[mid].keysym < keysym)
+ if (keysymtab[mid].keysym < keysym) {
min = mid + 1;
- else if (keysymtab[mid].keysym > keysym)
+ } else if (keysymtab[mid].keysym > keysym) {
max = mid - 1;
- else {
+ } else {
/* found it */
return keysymtab[mid].ucs;
}
diff --git a/i3-input/main.c b/i3-input/main.c
index ef9e0701..2ea94142 100644
--- a/i3-input/main.c
+++ b/i3-input/main.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-input/main.c: Utility which lets the user input commands and sends them
@@ -102,7 +102,7 @@ static uint8_t *concat_strings(char **glyphs, int max) {
walk += strlen(glyphs[c]);
}
}
- printf("output = %s\n", output);
+ printf("output = %s\n", (char *)output);
return output;
}
@@ -111,7 +111,7 @@ static uint8_t *concat_strings(char **glyphs, int max) {
* be called from the code with event == NULL or from X with event != NULL.
*
*/
-static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t *event) {
+static int handle_expose(xcb_connection_t *conn) {
printf("expose!\n");
color_t border_color = draw_util_hex_to_color("#FF0000");
@@ -163,29 +163,32 @@ static void finish_input(void) {
char *command = (char *)concat_strings(glyphs_utf8, input_position);
/* 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')
+ const size_t len = strlen(format);
+ size_t cnt = 0;
+ for (size_t c = 0; c < (len - 1); c++) {
+ if (format[c] == '%' && format[c + 1] == 's') {
cnt++;
- printf("occurrences = %d\n", cnt);
+ }
+ }
+ printf("occurrences = %zu\n", cnt);
/* allocate space for the output */
- int inputlen = strlen(command);
- char *full = scalloc(strlen(format) - (2 * cnt) /* format without all %s */
- + (inputlen * cnt) /* replaced %s */
- + 1, /* trailing NUL */
- 1);
+ const size_t input_len = strlen(command);
+ const size_t full_len = MAX(input_len, /* avoid compiler warning */
+ strlen(format) - (2 * cnt) /* format without all %s */
+ + (input_len * cnt) /* replaced %s */
+ + 1 /* trailing NUL */
+ );
+ char *full = scalloc(full_len, 1);
char *dest = full;
- for (c = 0; c < len; c++) {
- /* if this is not % or it is % but without a following 's',
- * just copy the character */
- if (format[c] != '%' || (c == (len - 1)) || format[c + 1] != 's')
+ for (size_t c = 0; c < len; c++) {
+ /* if this is not % or it is % but without a following 's', just copy
+ * the character */
+ if (format[c] != '%' || (c == (len - 1)) || format[c + 1] != 's') {
*(dest++) = format[c];
- else {
- strncat(dest, command, inputlen);
- dest += inputlen;
+ } else {
+ strncat(dest, command, input_len);
+ dest += input_len;
/* skip the following 's' of '%s' */
c++;
}
@@ -223,8 +226,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
/* If modeswitch is currently active, we need to look in group 2 or 3,
* respectively. */
- if (modeswitch_active)
+ if (modeswitch_active) {
col += 2;
+ }
xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, col);
if (sym == XK_Mode_switch) {
@@ -233,17 +237,19 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
return 1;
}
- if (sym == XK_Return)
+ if (sym == XK_Return) {
finish_input();
+ }
if (sym == XK_BackSpace) {
- if (input_position == 0)
+ if (input_position == 0) {
return 1;
+ }
input_position--;
free(glyphs_utf8[input_position]);
- handle_expose(NULL, conn, NULL);
+ handle_expose(conn);
return 1;
}
if (sym == XK_Escape) {
@@ -259,8 +265,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
printf("xcb_is_misc_function_key = %d\n", xcb_is_misc_function_key(sym));
printf("xcb_is_modifier_key = %d\n", xcb_is_modifier_key(sym));
- if (xcb_is_modifier_key(sym) || xcb_is_cursor_key(sym))
+ if (xcb_is_modifier_key(sym) || xcb_is_cursor_key(sym)) {
return 1;
+ }
printf("sym = %c (%d)\n", sym, sym);
@@ -284,10 +291,11 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
glyphs_utf8[input_position] = out;
input_position++;
- if (input_position == limit)
+ if (input_position == limit) {
finish_input();
+ }
- handle_expose(NULL, conn, NULL);
+ handle_expose(conn);
return 1;
}
@@ -399,7 +407,7 @@ int main(int argc, char *argv[]) {
socket_path = sstrdup(optarg);
break;
case 'v':
- printf("i3-input " I3_VERSION);
+ printf("i3-input " I3_VERSION "\n");
return EXIT_OK;
case 'p':
/* This option is deprecated, but will still work in i3 v4.1, 4.2 and 4.3 */
@@ -439,8 +447,9 @@ int main(int argc, char *argv[]) {
int screen;
conn = xcb_connect(NULL, &screen);
- if (!conn || xcb_connection_has_error(conn))
+ if (!conn || xcb_connection_has_error(conn)) {
die("Cannot open display");
+ }
sockfd = ipc_connect(socket_path);
@@ -453,8 +462,9 @@ int main(int argc, char *argv[]) {
font = load_font(pattern ? pattern : "pango:monospace 8", true);
set_font(&font);
- if (prompt != NULL)
+ if (prompt != NULL) {
prompt_offset = predict_text_width(prompt);
+ }
const xcb_rectangle_t win_pos = get_window_position();
@@ -508,11 +518,12 @@ int main(int argc, char *argv[]) {
while ((event = xcb_wait_for_event(conn)) != NULL) {
if (event->response_type == 0) {
fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence);
+ free(event);
continue;
}
/* Strip off the highest bit (set if the event is generated) */
- int type = (event->response_type & 0x7F);
+ const int type = (event->response_type & 0x7F);
switch (type) {
case XCB_KEY_PRESS:
@@ -525,9 +536,10 @@ int main(int argc, char *argv[]) {
case XCB_EXPOSE:
if (((xcb_expose_event_t *)event)->count == 0) {
- handle_expose(NULL, conn, (xcb_expose_event_t *)event);
+ handle_expose(conn);
}
-
+ break;
+ default:
break;
}
diff --git a/i3-msg/main.c b/i3-msg/main.c
index 239ac46f..38f7e022 100644
--- a/i3-msg/main.c
+++ b/i3-msg/main.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-msg/main.c: Utility which sends messages to a running i3-instance using
@@ -61,22 +61,24 @@ static int exit_code = 0;
static reply_t last_reply;
static int reply_boolean_cb(void *params, int val) {
- if (strcmp(last_key, "success") == 0)
+ if (strcmp(last_key, "success") == 0) {
last_reply.success = val;
+ }
return 1;
}
static int reply_string_cb(void *params, const unsigned char *val, size_t len) {
char *str = sstrndup((const char *)val, len);
- if (strcmp(last_key, "error") == 0)
+ if (strcmp(last_key, "error") == 0) {
last_reply.error = str;
- else if (strcmp(last_key, "input") == 0)
+ } else if (strcmp(last_key, "input") == 0) {
last_reply.input = str;
- else if (strcmp(last_key, "errorposition") == 0)
+ } else if (strcmp(last_key, "errorposition") == 0) {
last_reply.errorposition = str;
- else
+ } else {
free(str);
+ }
return 1;
}
@@ -146,10 +148,6 @@ static yajl_callbacks config_callbacks = {
};
int main(int argc, char *argv[]) {
-#if defined(__OpenBSD__)
- if (pledge("stdio rpath unix", NULL) == -1)
- err(EXIT_FAILURE, "pledge");
-#endif
char *socket_path = NULL;
int o, option_index = 0;
uint32_t message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND;
@@ -244,12 +242,14 @@ int main(int argc, char *argv[]) {
optind++;
}
- if (!payload)
+ if (!payload) {
payload = sstrdup("");
+ }
int sockfd = ipc_connect(socket_path);
- if (ipc_send_message(sockfd, strlen(payload), message_type, (uint8_t *)payload) == -1)
+ if (ipc_send_message(sockfd, strlen(payload), message_type, (uint8_t *)payload) == -1) {
err(EXIT_FAILURE, "IPC: write()");
+ }
free(payload);
uint32_t reply_length;
@@ -257,12 +257,14 @@ int main(int argc, char *argv[]) {
uint8_t *reply;
int ret;
if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
- if (ret == -1)
+ if (ret == -1) {
err(EXIT_FAILURE, "IPC: read()");
+ }
exit(1);
}
- if (reply_type != message_type)
+ if (reply_type != message_type) {
errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected %d", reply_type, message_type);
+ }
/* For the reply of commands, have a look if that command was successful.
* If not, nicely format the error message. */
if (reply_type == I3_IPC_REPLY_TYPE_COMMAND) {
@@ -303,8 +305,9 @@ int main(int argc, char *argv[]) {
do {
free(reply);
if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
- if (ret == -1)
+ if (ret == -1) {
err(EXIT_FAILURE, "IPC: read()");
+ }
exit(1);
}
diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c
index 7d9c0901..a191f4b3 100644
--- a/i3-nagbar/main.c
+++ b/i3-nagbar/main.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-nagbar is a utility which displays a nag message, for example in the case
@@ -118,7 +118,7 @@ static void start_application(const char *command) {
if (fork() == 0) {
/* This is the child */
execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, NULL);
- /* not reached */
+ err(EXIT_FAILURE, "execl return"); /* only reached on error */
}
exit(0);
}
@@ -126,9 +126,11 @@ static void start_application(const char *command) {
}
static button_t *get_button_at(int16_t x, int16_t y) {
- for (int c = 0; c < buttoncnt; c++)
- if (x >= (buttons[c].x) && x <= (buttons[c].x + buttons[c].width))
+ for (int c = 0; c < buttoncnt; c++) {
+ if (x >= (buttons[c].x) && x <= (buttons[c].x + buttons[c].width)) {
return &buttons[c];
+ }
+ }
return NULL;
}
@@ -148,11 +150,13 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
printf("button released on x = %d, y = %d\n",
event->event_x, event->event_y);
/* If the user hits the close button, we exit(0) */
- if (event->event_x >= btn_close.x && event->event_x < btn_close.x + btn_close.width)
+ if (event->event_x >= btn_close.x && event->event_x < btn_close.x + btn_close.width) {
exit(0);
+ }
button_t *button = get_button_at(event->event_x, event->event_y);
- if (!button)
+ if (!button) {
return;
+ }
/* We need to create a custom script containing our actual command
* since not every terminal emulator which is contained in
@@ -238,7 +242,7 @@ static int button_draw(button_t *button, int position) {
* be called from the code with event == NULL or from X with event != NULL.
*
*/
-static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
+static int handle_expose(xcb_connection_t *conn) {
/* draw background */
draw_util_clear_surface(&bar, color_background);
/* draw message */
@@ -460,8 +464,9 @@ int main(int argc, char *argv[]) {
buttons[buttoncnt].action);
buttoncnt++;
printf("now %d buttons\n", buttoncnt);
- if (optind < argc)
+ if (optind < argc) {
optind++;
+ }
break;
}
}
@@ -470,8 +475,9 @@ int main(int argc, char *argv[]) {
int screens;
if ((conn = xcb_connect(NULL, &screens)) == NULL ||
- xcb_connection_has_error(conn))
+ xcb_connection_has_error(conn)) {
die("Cannot open display");
+ }
/* Place requests for the atoms we need as soon as possible */
#define xmacro(atom) \
@@ -497,7 +503,7 @@ int main(int argc, char *argv[]) {
} else {
/* Yellowish theme for warnings */
color_button_background = draw_util_hex_to_color("#ffc100");
- color_background = draw_util_hex_to_color("#ffa8000");
+ color_background = draw_util_hex_to_color("#ffa800");
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");
@@ -507,11 +513,6 @@ int main(int argc, char *argv[]) {
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
-
/* Default values if we cannot determine the preferred window position. */
xcb_rectangle_t win_pos = (xcb_rectangle_t){50, 50, 500, font.height + 2 * MSG_PADDING + BAR_BORDER};
if (position_on_primary) {
@@ -632,7 +633,7 @@ int main(int argc, char *argv[]) {
switch (type) {
case XCB_EXPOSE:
if (((xcb_expose_event_t *)event)->count == 0) {
- handle_expose(conn, (xcb_expose_event_t *)event);
+ handle_expose(conn);
}
break;
diff --git a/i3-sensible-terminal b/i3-sensible-terminal
index bee303f8..01b0c180 100755
--- a/i3-sensible-terminal
+++ b/i3-sensible-terminal
@@ -14,7 +14,7 @@
# 2. Distribution-specific mechanisms come next, e.g. x-terminal-emulator
# 3. The terminal emulator with best accessibility comes first.
# 4. No order is guaranteed/desired for the remaining terminal emulators.
-for terminal in "$TERMINAL" x-terminal-emulator mate-terminal gnome-terminal terminator xfce4-terminal urxvt rxvt termit Eterm aterm uxterm xterm roxterm termite lxterminal terminology st qterminal lilyterm tilix terminix konsole kitty guake tilda alacritty hyper wezterm; do
+for terminal in "$TERMINAL" x-terminal-emulator mate-terminal gnome-terminal terminator xfce4-terminal urxvt rxvt termit Eterm aterm uxterm xterm roxterm termite lxterminal terminology st qterminal lilyterm tilix terminix konsole kitty guake tilda alacritty hyper wezterm rio; do
if command -v "$terminal" > /dev/null 2>&1; then
exec "$terminal" "$@"
fi
diff --git a/i3bar/.gitignore b/i3bar/.gitignore
index 6aad070b..f15fdbe5 100644
--- a/i3bar/.gitignore
+++ b/i3bar/.gitignore
@@ -1,4 +1,3 @@
i3bar
-*.o
core
doc/i3bar.1
diff --git a/i3bar/include/child.h b/i3bar/include/child.h
index ae523bc0..e77b51e3 100644
--- a/i3bar/include/child.h
+++ b/i3bar/include/child.h
@@ -11,7 +11,7 @@
#include
-#include
+#include
#define STDIN_CHUNK_SIZE 1024
@@ -40,6 +40,18 @@ typedef struct {
*/
bool click_events;
bool click_events_init;
+
+ /**
+ * stdin- and SIGCHLD-watchers
+ */
+ ev_io *stdin_io;
+ ev_child *child_sig;
+ int stdin_fd;
+
+ /**
+ * Line read from child that did not include a newline character.
+ */
+ char *pending_line;
} i3bar_child;
/*
@@ -50,36 +62,66 @@ void clear_statusline(struct statusline_head *head, bool free_resources);
/*
* Start a child process with the specified command and reroute stdin.
- * We actually start a $SHELL to execute the command so we don't have to care
- * about arguments and such
+ * We actually start a shell to execute the command so we don't have to care
+ * about arguments and such.
+ *
+ * If `command' is NULL, such as in the case when no `status_command' is given
+ * in the bar config, no child will be started.
*
*/
void start_child(char *command);
+/*
+ * Same as start_child but starts the configured client that manages workspace
+ * buttons.
+ *
+ */
+void start_ws_child(char *command);
+
+/*
+ * Returns true if the status child process is alive.
+ *
+ */
+bool status_child_is_alive(void);
+
+/*
+ * Returns true if the workspace child process is alive.
+ *
+ */
+bool ws_child_is_alive(void);
+
/*
* kill()s the child process (if any). Called when exit()ing.
*
*/
-void kill_child_at_exit(void);
+void kill_children_at_exit(void);
/*
- * kill()s the child process (if any) and closes and
- * free()s the stdin- and SIGCHLD-watchers
+ * kill()s the child process (if any) and closes and free()s the stdin- and
+ * SIGCHLD-watchers
*
*/
void kill_child(void);
+/*
+ * kill()s the workspace child process (if any) and closes and free()s the
+ * stdin- and SIGCHLD-watchers.
+ * Similar to kill_child.
+ *
+ */
+void kill_ws_child(void);
+
/*
* Sends a SIGSTOP to the child process (if existent)
*
*/
-void stop_child(void);
+void stop_children(void);
/*
* Sends a SIGCONT to the child process (if existent)
*
*/
-void cont_child(void);
+void cont_children(void);
/*
* Whether or not the child want click events
@@ -92,3 +134,14 @@ bool child_want_click_events(void);
*
*/
void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int out_x, int out_y, int width, int height, int mods);
+
+/*
+ * When workspace_command is enabled this function is used to re-parse the
+ * latest received JSON from the client.
+ */
+void repeat_last_ws_json(void);
+
+/*
+ * Replaces the workspace buttons with an error message.
+ */
+void set_workspace_button_error(const char *message);
diff --git a/i3bar/include/common.h b/i3bar/include/common.h
index e0f2e7e4..9c13d02f 100644
--- a/i3bar/include/common.h
+++ b/i3bar/include/common.h
@@ -46,6 +46,9 @@ struct status_block {
i3String *full_text;
i3String *short_text;
+ bool use_short;
+ uint32_t render_length;
+
char *color;
char *background;
char *border;
diff --git a/i3bar/include/configuration.h b/i3bar/include/configuration.h
index 24079c5d..c9bae7c3 100644
--- a/i3bar/include/configuration.h
+++ b/i3bar/include/configuration.h
@@ -62,6 +62,7 @@ typedef struct config_t {
bool strip_ws_name;
char *bar_id;
char *command;
+ char *workspace_command;
char *fontname;
i3String *separator_symbol;
TAILQ_HEAD(tray_outputs_head, tray_output_t) tray_outputs;
@@ -79,17 +80,17 @@ typedef struct config_t {
extern config_t config;
/**
- * Start parsing the received bar configuration JSON string
+ * Parse the received bar configuration JSON string
*
*/
-void parse_config_json(char *json);
+void parse_config_json(const unsigned char *json, size_t size);
/**
- * Start parsing the received bar configuration list. The only usecase right
- * now is to automatically get the first bar id.
+ * Parse the received bar configuration list. The only usecase right now is to
+ * automatically get the first bar id.
*
*/
-void parse_get_first_i3bar_config(char *json);
+void parse_get_first_i3bar_config(const unsigned char *json, size_t size);
/**
* free()s the color strings as soon as they are not needed anymore.
diff --git a/i3bar/include/mode.h b/i3bar/include/mode.h
index e8e4296d..4646b9f4 100644
--- a/i3bar/include/mode.h
+++ b/i3bar/include/mode.h
@@ -24,7 +24,7 @@ struct mode {
typedef struct mode mode;
/*
- * Start parsing the received JSON string
+ * Parse the received JSON string
*
*/
-void parse_mode_json(char *json);
+void parse_mode_json(const unsigned char *json, size_t size);
diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h
index 4685e51e..dfe145fe 100644
--- a/i3bar/include/outputs.h
+++ b/i3bar/include/outputs.h
@@ -22,10 +22,10 @@ SLIST_HEAD(outputs_head, i3_output);
extern struct outputs_head* outputs;
/*
- * Start parsing the received JSON string
+ * Parse the received JSON string
*
*/
-void parse_outputs_json(char* json);
+void parse_outputs_json(const unsigned char* json, size_t size);
/*
* Initiate the outputs list
@@ -65,8 +65,6 @@ struct i3_output {
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;
diff --git a/i3bar/include/util.h b/i3bar/include/util.h
index 1f563611..bb135c9e 100644
--- a/i3bar/include/util.h
+++ b/i3bar/include/util.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/i3bar/include/workspaces.h b/i3bar/include/workspaces.h
index ff61450c..6c8e7145 100644
--- a/i3bar/include/workspaces.h
+++ b/i3bar/include/workspaces.h
@@ -18,10 +18,10 @@ typedef struct i3_ws i3_ws;
TAILQ_HEAD(ws_head, i3_ws);
/*
- * Start parsing the received JSON string
+ * Parse the received JSON string
*
*/
-void parse_workspaces_json(char *json);
+void parse_workspaces_json(const unsigned char *json, size_t size);
/*
* free() all workspace data structures
@@ -38,7 +38,6 @@ struct i3_ws {
bool visible; /* If the ws is currently visible on an output */
bool focused; /* If the ws is currently focused */
bool urgent; /* If the urgent hint of the ws is set */
- rect rect; /* The rect of the ws (not used (yet)) */
struct i3_output *output; /* The current output of the ws */
TAILQ_ENTRY(i3_ws) tailq; /* Pointer for the TAILQ-Macro */
diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h
index 0e3ca22d..fa274d01 100644
--- a/i3bar/include/xcb.h
+++ b/i3bar/include/xcb.h
@@ -12,7 +12,6 @@
#include
#include
-//#include "outputs.h"
#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
diff --git a/i3bar/src/child.c b/i3bar/src/child.c
index df4c6601..740912f0 100644
--- a/i3bar/src/child.c
+++ b/i3bar/src/child.c
@@ -8,8 +8,10 @@
*
*/
#include "common.h"
+#include "queue.h"
#include "yajl_utils.h"
+#include /* isspace */
#include
#include
#include
@@ -20,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
@@ -27,14 +30,30 @@
#include
/* Global variables for child_*() */
-i3bar_child child = {0};
-#define DLOG_CHILD DLOG("%s: pid=%ld stopped=%d stop_signal=%d cont_signal=%d click_events=%d click_events_init=%d\n", \
- __func__, (long)child.pid, child.stopped, child.stop_signal, child.cont_signal, child.click_events, child.click_events_init)
+i3bar_child status_child = {0};
+i3bar_child ws_child = {0};
-/* stdin- and SIGCHLD-watchers */
-ev_io *stdin_io;
-int stdin_fd;
-ev_child *child_sig;
+#define DLOG_CHILD(c) \
+ do { \
+ if ((c).pid == 0) { \
+ DLOG("%s: child pid = 0\n", __func__); \
+ } else if ((c).pid == status_child.pid) { \
+ DLOG("%s: status_command: pid=%ld stopped=%d stop_signal=%d cont_signal=%d click_events=%d click_events_init=%d\n", \
+ __func__, (long)(c).pid, (c).stopped, (c).stop_signal, (c).cont_signal, (c).click_events, (c).click_events_init); \
+ } else if ((c).pid == ws_child.pid) { \
+ DLOG("%s: workspace_command: pid=%ld stopped=%d stop_signal=%d cont_signal=%d click_events=%d click_events_init=%d\n", \
+ __func__, (long)(c).pid, (c).stopped, (c).stop_signal, (c).cont_signal, (c).click_events, (c).click_events_init); \
+ } else { \
+ ELOG("%s: unknown child, this should never happen " \
+ "pid=%ld stopped=%d stop_signal=%d cont_signal=%d click_events=%d click_events_init=%d\n", \
+ __func__, (long)(c).pid, (c).stopped, (c).stop_signal, (c).cont_signal, (c).click_events, (c).click_events_init); \
+ } \
+ } while (0)
+#define DLOG_CHILDREN \
+ do { \
+ DLOG_CHILD(status_child); \
+ DLOG_CHILD(ws_child); \
+ } while (0)
/* JSON parser for stdin */
yajl_handle parser;
@@ -127,7 +146,7 @@ __attribute__((format(printf, 1, 2))) static void set_statusline_error(const cha
TAILQ_INSERT_TAIL(&statusline_head, message_block, blocks);
finish:
- FREE(message);
+ free(message);
va_end(args);
}
@@ -135,22 +154,27 @@ finish:
* Stop and free() the stdin- and SIGCHLD-watchers
*
*/
-static void cleanup(void) {
- if (stdin_io != NULL) {
- ev_io_stop(main_loop, stdin_io);
- FREE(stdin_io);
- close(stdin_fd);
- stdin_fd = 0;
- close(child_stdin);
- child_stdin = 0;
+static void cleanup(i3bar_child *c) {
+ DLOG_CHILD(*c);
+
+ if (c->stdin_io != NULL) {
+ ev_io_stop(main_loop, c->stdin_io);
+ FREE(c->stdin_io);
+
+ if (c->pid == status_child.pid) {
+ close(child_stdin);
+ child_stdin = 0;
+ }
+ close(c->stdin_fd);
}
- if (child_sig != NULL) {
- ev_child_stop(main_loop, child_sig);
- FREE(child_sig);
+ if (c->child_sig != NULL) {
+ ev_child_stop(main_loop, c->child_sig);
+ FREE(c->child_sig);
}
- memset(&child, 0, sizeof(i3bar_child));
+ FREE(c->pending_line);
+ memset(c, 0, sizeof(i3bar_child));
}
/*
@@ -173,10 +197,11 @@ static int stdin_start_map(void *context) {
memset(&(ctx->block), '\0', sizeof(struct status_block));
/* Default width of the separator block. */
- if (config.separator_symbol == NULL)
+ if (config.separator_symbol == NULL) {
ctx->block.sep_block_width = logical_px(9);
- else
+ } else {
ctx->block.sep_block_width = logical_px(8) + separator_symbol_width;
+ }
/* By default we draw all four borders if a border is set. */
ctx->block.border_top = 1;
@@ -190,7 +215,7 @@ static int stdin_start_map(void *context) {
static int stdin_map_key(void *context, const unsigned char *key, size_t len) {
parser_ctx *ctx = context;
FREE(ctx->last_map_key);
- sasprintf(&(ctx->last_map_key), "%.*s", len, key);
+ sasprintf(&(ctx->last_map_key), "%.*s", (int)len, key);
return 1;
}
@@ -229,15 +254,15 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) {
return 1;
}
if (strcasecmp(ctx->last_map_key, "color") == 0) {
- sasprintf(&(ctx->block.color), "%.*s", len, val);
+ sasprintf(&(ctx->block.color), "%.*s", (int)len, val);
return 1;
}
if (strcasecmp(ctx->last_map_key, "background") == 0) {
- sasprintf(&(ctx->block.background), "%.*s", len, val);
+ sasprintf(&(ctx->block.background), "%.*s", (int)len, val);
return 1;
}
if (strcasecmp(ctx->last_map_key, "border") == 0) {
- sasprintf(&(ctx->block.border), "%.*s", len, val);
+ sasprintf(&(ctx->block.border), "%.*s", (int)len, val);
return 1;
}
if (strcasecmp(ctx->last_map_key, "markup") == 0) {
@@ -255,15 +280,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) {
- sasprintf(&(ctx->block.min_width_str), "%.*s", len, val);
+ sasprintf(&(ctx->block.min_width_str), "%.*s", (int)len, val);
return 1;
}
if (strcasecmp(ctx->last_map_key, "name") == 0) {
- sasprintf(&(ctx->block.name), "%.*s", len, val);
+ sasprintf(&(ctx->block.name), "%.*s", (int)len, val);
return 1;
}
if (strcasecmp(ctx->last_map_key, "instance") == 0) {
- sasprintf(&(ctx->block.instance), "%.*s", len, val);
+ sasprintf(&(ctx->block.instance), "%.*s", (int)len, val);
return 1;
}
@@ -315,10 +340,12 @@ static int stdin_end_map(void *context) {
memcpy(new_block, &(ctx->block), sizeof(struct status_block));
/* Ensure we have a full_text set, so that when it is missing (or null),
* i3bar doesn’t crash and the user gets an annoying message. */
- if (!new_block->full_text)
+ if (!new_block->full_text) {
new_block->full_text = i3string_from_utf8("SPEC VIOLATION: full_text is NULL!");
- if (new_block->urgent)
+ }
+ if (new_block->urgent) {
ctx->has_urgent = true;
+ }
if (new_block->min_width_str) {
i3String *text = i3string_from_utf8(new_block->min_width_str);
@@ -329,10 +356,13 @@ static int stdin_end_map(void *context) {
i3string_set_markup(new_block->full_text, new_block->pango_markup);
- if (new_block->short_text != NULL)
+ new_block->use_short = false;
+ if (new_block->short_text != NULL) {
i3string_set_markup(new_block->short_text, new_block->pango_markup);
+ }
TAILQ_INSERT_TAIL(&statusline_buffer, new_block, blocks);
+
return 1;
}
@@ -362,15 +392,13 @@ static int stdin_end_array(void *context) {
* Returns NULL on EOF.
*
*/
-static unsigned char *get_buffer(ev_io *watcher, int *ret_buffer_len) {
- int fd = watcher->fd;
- int n = 0;
+static unsigned char *get_buffer(int fd, int *ret_buffer_len) {
int rec = 0;
int buffer_len = STDIN_CHUNK_SIZE;
unsigned char *buffer = smalloc(buffer_len + 1);
buffer[0] = '\0';
while (1) {
- n = read(fd, buffer + rec, buffer_len - rec);
+ const ssize_t n = read(fd, buffer + rec, buffer_len - rec);
if (n == -1) {
if (errno == EAGAIN) {
/* finish up */
@@ -390,10 +418,11 @@ static unsigned char *get_buffer(ev_io *watcher, int *ret_buffer_len) {
if (rec == buffer_len) {
buffer_len += STDIN_CHUNK_SIZE;
- buffer = srealloc(buffer, buffer_len);
+ buffer = srealloc(buffer, buffer_len + 1);
}
}
- if (*buffer == '\0') {
+ buffer[rec] = '\0';
+ if (buffer[0] == '\0') {
FREE(buffer);
rec = -1;
}
@@ -423,8 +452,9 @@ static bool read_json_input(unsigned char *input, int length) {
char *message = (char *)yajl_get_error(parser, 0, input, length);
/* strip the newline yajl adds to the error message */
- if (message[strlen(message) - 1] == '\n')
+ if (message[strlen(message) - 1] == '\n') {
message[strlen(message) - 1] = '\0';
+ }
fprintf(stderr, "[i3bar] Could not parse JSON input (code = %d, message = %s): %.*s\n",
status, message, length, input);
@@ -443,13 +473,14 @@ static bool read_json_input(unsigned char *input, int length) {
* in statusline
*
*/
-static void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+static void stdin_io_cb(int fd) {
int rec;
- unsigned char *buffer = get_buffer(watcher, &rec);
- if (buffer == NULL)
+ unsigned char *buffer = get_buffer(fd, &rec);
+ if (buffer == NULL) {
return;
+ }
bool has_urgent = false;
- if (child.version > 0) {
+ if (status_child.version > 0) {
has_urgent = read_json_input(buffer, rec);
} else {
read_flat_input((char *)buffer, rec);
@@ -463,22 +494,23 @@ static void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
* whether this is JSON or plain text
*
*/
-static void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+static void stdin_io_first_line_cb(int fd) {
int rec;
- unsigned char *buffer = get_buffer(watcher, &rec);
- if (buffer == NULL)
+ unsigned char *buffer = get_buffer(fd, &rec);
+ if (buffer == NULL) {
return;
+ }
DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer);
/* Detect whether this is JSON or plain text. */
unsigned int consumed = 0;
/* At the moment, we don’t care for the version. This might change
* in the future, but for now, we just discard it. */
- parse_json_header(&child, buffer, rec, &consumed);
- if (child.version > 0) {
- /* If hide-on-modifier is set, we start of by sending the
- * child a SIGSTOP, because the bars aren't mapped at start */
+ parse_json_header(&status_child, buffer, rec, &consumed);
+ if (status_child.version > 0) {
+ /* If hide-on-modifier is set, we start of by sending the status_child
+ * a SIGSTOP, because the bars aren't mapped at start */
if (config.hide_on_modifier) {
- stop_child();
+ stop_children();
}
draw_bars(read_json_input(buffer + consumed, rec - consumed));
} else {
@@ -489,9 +521,133 @@ static void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int rev
read_flat_input((char *)buffer, rec);
}
free(buffer);
- ev_io_stop(main_loop, stdin_io);
- ev_io_init(stdin_io, &stdin_io_cb, stdin_fd, EV_READ);
- ev_io_start(main_loop, stdin_io);
+}
+
+static bool isempty(char *s) {
+ while (*s != '\0') {
+ if (!isspace(*s)) {
+ return false;
+ }
+ s++;
+ }
+ return true;
+}
+
+static char *append_string(const char *previous, const char *str) {
+ if (previous != NULL) {
+ char *result;
+ sasprintf(&result, "%s%s", previous, str);
+ return result;
+ }
+ return sstrdup(str);
+}
+
+static char *ws_last_json;
+
+static void ws_stdin_io_cb(int fd) {
+ int rec;
+ unsigned char *buffer = get_buffer(fd, &rec);
+ if (buffer == NULL) {
+ return;
+ }
+
+ gchar **strings = g_strsplit((const char *)buffer, "\n", 0);
+ for (int idx = 0; strings[idx] != NULL; idx++) {
+ if (ws_child.pending_line == NULL && isempty(strings[idx])) {
+ /* In the normal case where the buffer ends with '\n', the last
+ * string should be empty */
+ continue;
+ }
+
+ if (strings[idx + 1] == NULL) {
+ /* This is the last string but it is not empty, meaning that we have
+ * read data that is incomplete, save it for later. */
+ char *new = append_string(ws_child.pending_line, strings[idx]);
+ free(ws_child.pending_line);
+ ws_child.pending_line = new;
+ continue;
+ }
+
+ free(ws_last_json);
+ ws_last_json = append_string(ws_child.pending_line, strings[idx]);
+ FREE(ws_child.pending_line);
+
+ parse_workspaces_json((const unsigned char *)ws_last_json, strlen(ws_last_json));
+ }
+
+ g_strfreev(strings);
+ free(buffer);
+
+ draw_bars(false);
+}
+
+static void common_stdin_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+ if (watcher == status_child.stdin_io) {
+ if (status_child.version == (uint32_t)-1) {
+ stdin_io_first_line_cb(watcher->fd);
+ } else {
+ stdin_io_cb(watcher->fd);
+ }
+ } else if (watcher == ws_child.stdin_io) {
+ ws_stdin_io_cb(watcher->fd);
+ } else {
+ ELOG("Got callback for unknown watcher fd=%d\n", watcher->fd);
+ }
+}
+
+/*
+ * When workspace_command is enabled this function is used to re-parse the
+ * latest received JSON from the client.
+ */
+void repeat_last_ws_json(void) {
+ if (ws_last_json) {
+ DLOG("Repeating last workspace JSON\n");
+ parse_workspaces_json((const unsigned char *)ws_last_json, strlen(ws_last_json));
+ }
+}
+
+/*
+ * Wrapper around set_workspace_button_error to mimic the call of
+ * set_statusline_error.
+ */
+__attribute__((format(printf, 1, 2))) static void set_workspace_button_error_f(const char *format, ...) {
+ char *message;
+ va_list args;
+ va_start(args, format);
+ if (vasprintf(&message, format, args) == -1) {
+ goto finish;
+ }
+
+ set_workspace_button_error(message);
+
+finish:
+ free(message);
+ va_end(args);
+}
+
+/*
+ * Replaces the workspace buttons with an error message.
+ */
+void set_workspace_button_error(const char *message) {
+ free_workspaces();
+
+ char *name = NULL;
+ sasprintf(&name, "Error: %s", message);
+
+ i3_output *output;
+ SLIST_FOREACH (output, outputs, slist) {
+ i3_ws *fake_ws = scalloc(1, sizeof(i3_ws));
+ /* Don't set the canonical_name field to make this workspace unfocusable. */
+ fake_ws->name = i3string_from_utf8(name);
+ fake_ws->name_width = predict_text_width(fake_ws->name);
+ fake_ws->num = -1;
+ fake_ws->urgent = fake_ws->visible = true;
+ fake_ws->output = output;
+
+ TAILQ_INSERT_TAIL(output->workspaces, fake_ws, tailq);
+ }
+
+ free(name);
}
/*
@@ -501,27 +657,45 @@ static void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int rev
*
*/
static void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
- int exit_status = WEXITSTATUS(watcher->rstatus);
+ const int exit_status = WEXITSTATUS(watcher->rstatus);
ELOG("Child (pid: %d) unexpectedly exited with status %d\n",
- child.pid,
+ watcher->pid,
exit_status);
+ __attribute__((format(printf, 1, 2))) void (*error_function_pointer)(const char *, ...) = NULL;
+ const char *command_type = "";
+ i3bar_child *c = NULL;
+ if (watcher->pid == status_child.pid) {
+ command_type = "status_command";
+ error_function_pointer = set_statusline_error;
+ c = &status_child;
+ } else if (watcher->pid == ws_child.pid) {
+ command_type = "workspace_command";
+ error_function_pointer = set_workspace_button_error_f;
+ c = &ws_child;
+ } else {
+ ELOG("Unknown child pid, this should never happen\n");
+ return;
+ }
+ DLOG_CHILD(*c);
+
/* this error is most likely caused by a user giving a nonexecutable or
* nonexistent file, so we will handle those cases separately. */
- if (exit_status == 126)
- set_statusline_error("status_command is not executable (exit %d)", exit_status);
- else if (exit_status == 127)
- set_statusline_error("status_command not found or is missing a library dependency (exit %d)", exit_status);
- else
- set_statusline_error("status_command process exited unexpectedly (exit %d)", exit_status);
+ if (exit_status == 126) {
+ error_function_pointer("%s is not executable (exit %d)", command_type, exit_status);
+ } else if (exit_status == 127) {
+ error_function_pointer("%s not found or is missing a library dependency (exit %d)", command_type, exit_status);
+ } else {
+ error_function_pointer("%s process exited unexpectedly (exit %d)", command_type, exit_status);
+ }
- cleanup();
+ cleanup(c);
draw_bars(false);
}
static void child_write_output(void) {
- if (child.click_events) {
+ if (status_child.click_events) {
const unsigned char *output;
size_t size;
ssize_t n;
@@ -529,13 +703,14 @@ static void child_write_output(void) {
yajl_gen_get_buf(gen, &output, &size);
n = writeall(child_stdin, output, size);
- if (n != -1)
+ if (n != -1) {
n = writeall(child_stdin, "\n", 1);
+ }
yajl_gen_clear(gen);
if (n == -1) {
- child.click_events = false;
+ status_child.click_events = false;
kill_child();
set_statusline_error("child_write_output failed");
draw_bars(false);
@@ -543,6 +718,42 @@ static void child_write_output(void) {
}
}
+static pid_t sfork(void) {
+ const pid_t pid = fork();
+ if (pid == -1) {
+ ELOG("Couldn't fork(): %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ return pid;
+}
+
+static void spipe(int pipedes[2]) {
+ if (pipe(pipedes) == -1) {
+ err(EXIT_FAILURE, "pipe(pipe_in)");
+ }
+}
+
+static void exec_shell(char *command) {
+ execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, (char *)NULL);
+ err(EXIT_FAILURE, "execl return"); /* only reached on error */
+}
+
+static void setup_child_cb(i3bar_child *child) {
+ /* We set O_NONBLOCK because blocking is evil in event-driven software */
+ fcntl(child->stdin_fd, F_SETFL, O_NONBLOCK);
+
+ child->stdin_io = smalloc(sizeof(ev_io));
+ ev_io_init(child->stdin_io, &common_stdin_cb, child->stdin_fd, EV_READ);
+ ev_io_start(main_loop, child->stdin_io);
+
+ /* We must cleanup, if the child unexpectedly terminates */
+ child->child_sig = smalloc(sizeof(ev_child));
+ ev_child_init(child->child_sig, &child_sig_cb, child->pid, 0);
+ ev_child_start(main_loop, child->child_sig);
+
+ DLOG_CHILD(*child);
+}
+
/*
* Start a child process with the specified command and reroute stdin.
* We actually start a shell to execute the command so we don't have to care
@@ -553,8 +764,9 @@ static void child_write_output(void) {
*
*/
void start_child(char *command) {
- if (command == NULL)
+ if (command == NULL) {
return;
+ }
/* Allocate a yajl parser which will be used to parse stdin. */
static yajl_callbacks callbacks = {
@@ -568,69 +780,76 @@ void start_child(char *command) {
.yajl_end_array = stdin_end_array,
};
parser = yajl_alloc(&callbacks, NULL, &parser_context);
-
gen = yajl_gen_alloc(NULL);
int pipe_in[2]; /* pipe we read from */
int pipe_out[2]; /* pipe we write to */
+ spipe(pipe_in);
+ spipe(pipe_out);
- if (pipe(pipe_in) == -1)
- err(EXIT_FAILURE, "pipe(pipe_in)");
- if (pipe(pipe_out) == -1)
- err(EXIT_FAILURE, "pipe(pipe_out)");
+ status_child.pid = sfork();
+ if (status_child.pid == 0) {
+ /* Child-process. Reroute streams and start shell */
+ close(pipe_in[0]);
+ close(pipe_out[1]);
- child.pid = fork();
- switch (child.pid) {
- case -1:
- ELOG("Couldn't fork(): %s\n", strerror(errno));
- exit(EXIT_FAILURE);
- case 0:
- /* Child-process. Reroute streams and start shell */
+ dup2(pipe_in[1], STDOUT_FILENO);
+ dup2(pipe_out[0], STDIN_FILENO);
- close(pipe_in[0]);
- close(pipe_out[1]);
+ setpgid(status_child.pid, 0);
+ exec_shell(command);
+ return;
+ }
+ /* Parent-process. Reroute streams */
+ close(pipe_in[1]);
+ close(pipe_out[0]);
- dup2(pipe_in[1], STDOUT_FILENO);
- dup2(pipe_out[0], STDIN_FILENO);
+ status_child.stdin_fd = pipe_in[0];
+ child_stdin = pipe_out[1];
+ status_child.version = -1;
- setpgid(child.pid, 0);
- execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, (char *)NULL);
- return;
- default:
- /* Parent-process. Reroute streams */
+ setup_child_cb(&status_child);
+}
- close(pipe_in[1]);
- close(pipe_out[0]);
-
- stdin_fd = pipe_in[0];
- child_stdin = pipe_out[1];
-
- break;
+/*
+ * Same as start_child but starts the configured client that manages workspace
+ * buttons.
+ *
+ */
+void start_ws_child(char *command) {
+ if (command == NULL) {
+ return;
}
- /* We set O_NONBLOCK because blocking is evil in event-driven software */
- fcntl(stdin_fd, F_SETFL, O_NONBLOCK);
+ ws_child.stop_signal = SIGSTOP;
+ ws_child.cont_signal = SIGCONT;
- stdin_io = smalloc(sizeof(ev_io));
- ev_io_init(stdin_io, &stdin_io_first_line_cb, stdin_fd, EV_READ);
- ev_io_start(main_loop, stdin_io);
+ int pipe_in[2]; /* pipe we read from */
+ spipe(pipe_in);
- /* We must cleanup, if the child unexpectedly terminates */
- child_sig = smalloc(sizeof(ev_child));
- ev_child_init(child_sig, &child_sig_cb, child.pid, 0);
- ev_child_start(main_loop, child_sig);
+ ws_child.pid = sfork();
+ if (ws_child.pid == 0) {
+ /* Child-process. Reroute streams and start shell */
+ close(pipe_in[0]);
+ dup2(pipe_in[1], STDOUT_FILENO);
- atexit(kill_child_at_exit);
- DLOG_CHILD;
+ setpgid(ws_child.pid, 0);
+ exec_shell(command);
+ }
+ /* Parent-process. Reroute streams */
+ close(pipe_in[1]);
+ ws_child.stdin_fd = pipe_in[0];
+
+ setup_child_cb(&ws_child);
}
static void child_click_events_initialize(void) {
- DLOG_CHILD;
+ DLOG_CHILD(status_child);
- if (!child.click_events_init) {
+ if (!status_child.click_events_init) {
yajl_gen_array_open(gen);
child_write_output();
- child.click_events_init = true;
+ status_child.click_events_init = true;
}
}
@@ -639,7 +858,7 @@ static void child_click_events_initialize(void) {
*
*/
void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int out_x, int out_y, int width, int height, int mods) {
- if (!child.click_events) {
+ if (!status_child.click_events) {
return;
}
@@ -662,20 +881,27 @@ void send_block_clicked(int button, const char *name, const char *instance, int
ystr("modifiers");
yajl_gen_array_open(gen);
- if (mods & XCB_MOD_MASK_SHIFT)
+ if (mods & XCB_MOD_MASK_SHIFT) {
ystr("Shift");
- if (mods & XCB_MOD_MASK_CONTROL)
+ }
+ if (mods & XCB_MOD_MASK_CONTROL) {
ystr("Control");
- if (mods & XCB_MOD_MASK_1)
+ }
+ if (mods & XCB_MOD_MASK_1) {
ystr("Mod1");
- if (mods & XCB_MOD_MASK_2)
+ }
+ if (mods & XCB_MOD_MASK_2) {
ystr("Mod2");
- if (mods & XCB_MOD_MASK_3)
+ }
+ if (mods & XCB_MOD_MASK_3) {
ystr("Mod3");
- if (mods & XCB_MOD_MASK_4)
+ }
+ if (mods & XCB_MOD_MASK_4) {
ystr("Mod4");
- if (mods & XCB_MOD_MASK_5)
+ }
+ if (mods & XCB_MOD_MASK_5) {
ystr("Mod5");
+ }
yajl_gen_array_close(gen);
ystr("x");
@@ -706,35 +932,85 @@ void send_block_clicked(int button, const char *name, const char *instance, int
child_write_output();
}
+static bool is_alive(i3bar_child *c) {
+ return c->pid > 0;
+}
+
+/*
+ * Returns true if the status child process is alive.
+ *
+ */
+bool status_child_is_alive(void) {
+ return is_alive(&status_child);
+}
+
+/*
+ * Returns true if the workspace child process is alive.
+ *
+ */
+bool ws_child_is_alive(void) {
+ return is_alive(&ws_child);
+}
+
/*
* kill()s the child process (if any). Called when exit()ing.
*
*/
-void kill_child_at_exit(void) {
- DLOG_CHILD;
+void kill_children_at_exit(void) {
+ DLOG_CHILDREN;
+ cont_children();
- if (child.pid > 0) {
- if (child.cont_signal > 0 && child.stopped)
- killpg(child.pid, child.cont_signal);
- killpg(child.pid, SIGTERM);
+ if (is_alive(&status_child)) {
+ killpg(status_child.pid, SIGTERM);
+ }
+ if (is_alive(&ws_child)) {
+ killpg(ws_child.pid, SIGTERM);
}
}
+static void cont_child(i3bar_child *c) {
+ if (is_alive(c) && c->cont_signal > 0 && c->stopped) {
+ c->stopped = false;
+ killpg(c->pid, c->cont_signal);
+ }
+}
+
+static void kill_and_wait(i3bar_child *c) {
+ DLOG_CHILD(*c);
+ if (!is_alive(c)) {
+ return;
+ }
+
+ cont_child(c);
+ killpg(c->pid, SIGTERM);
+ int status;
+ waitpid(c->pid, &status, 0);
+ cleanup(c);
+}
+
/*
- * kill()s the child process (if existent) and closes and
- * free()s the stdin- and SIGCHLD-watchers
+ * kill()s the child process (if any) and closes and free()s the stdin- and
+ * SIGCHLD-watchers
*
*/
void kill_child(void) {
- DLOG_CHILD;
+ kill_and_wait(&status_child);
+}
- if (child.pid > 0) {
- if (child.cont_signal > 0 && child.stopped)
- killpg(child.pid, child.cont_signal);
- killpg(child.pid, SIGTERM);
- int status;
- waitpid(child.pid, &status, 0);
- cleanup();
+/*
+ * kill()s the workspace child process (if any) and closes and free()s the
+ * stdin- and SIGCHLD-watchers.
+ * Similar to kill_child.
+ *
+ */
+void kill_ws_child(void) {
+ kill_and_wait(&ws_child);
+}
+
+static void stop_child(i3bar_child *c) {
+ if (c->stop_signal > 0 && !c->stopped) {
+ c->stopped = true;
+ killpg(c->pid, c->stop_signal);
}
}
@@ -742,26 +1018,21 @@ void kill_child(void) {
* Sends a SIGSTOP to the child process (if existent)
*
*/
-void stop_child(void) {
- DLOG_CHILD;
-
- if (child.stop_signal > 0 && !child.stopped) {
- child.stopped = true;
- killpg(child.pid, child.stop_signal);
- }
+void stop_children(void) {
+ DLOG_CHILDREN;
+ stop_child(&status_child);
+ stop_child(&ws_child);
}
/*
* Sends a SIGCONT to the child process (if existent)
*
*/
-void cont_child(void) {
- DLOG_CHILD;
+void cont_children(void) {
+ DLOG_CHILDREN;
- if (child.cont_signal > 0 && child.stopped) {
- child.stopped = false;
- killpg(child.pid, child.cont_signal);
- }
+ cont_child(&status_child);
+ cont_child(&ws_child);
}
/*
@@ -769,5 +1040,5 @@ void cont_child(void) {
*
*/
bool child_want_click_events(void) {
- return child.click_events;
+ return status_child.click_events;
}
diff --git a/i3bar/src/config.c b/i3bar/src/config.c
index ccea937d..65fa9ba9 100644
--- a/i3bar/src/config.c
+++ b/i3bar/src/config.c
@@ -29,7 +29,7 @@ static bool parsing_padding;
*/
static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
FREE(cur_key);
- sasprintf(&(cur_key), "%.*s", keyLen, keyVal);
+ sasprintf(&(cur_key), "%.*s", (int)keyLen, keyVal);
if (strcmp(cur_key, "bindings") == 0) {
parsing_bindings = true;
@@ -76,8 +76,9 @@ static int config_null_cb(void *params_) {
static int config_string_cb(void *params_, const unsigned char *val, size_t _len) {
int len = (int)_len;
/* The id and socket_path are ignored, we already know them. */
- if (!strcmp(cur_key, "id") || !strcmp(cur_key, "socket_path"))
+ if (!strcmp(cur_key, "id") || !strcmp(cur_key, "socket_path")) {
return 1;
+ }
if (parsing_bindings) {
if (strcmp(cur_key, "command") == 0) {
@@ -188,11 +189,17 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
}
if (!strcmp(cur_key, "status_command")) {
- DLOG("command = %.*s\n", len, val);
+ DLOG("status_command = %.*s\n", len, val);
sasprintf(&config.command, "%.*s", len, val);
return 1;
}
+ if (!strcmp(cur_key, "workspace_command")) {
+ DLOG("workspace_command = %.*s\n", len, val);
+ sasprintf(&config.workspace_command, "%.*s", len, val);
+ return 1;
+ }
+
if (!strcmp(cur_key, "font")) {
DLOG("font = %.*s\n", len, val);
FREE(config.fontname);
@@ -396,16 +403,15 @@ static yajl_callbacks outputs_callbacks = {
};
/*
- * Start parsing the received bar configuration JSON string
+ * Parse the received bar configuration JSON string
*
*/
-void parse_config_json(char *json) {
- yajl_handle handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
-
+void parse_config_json(const unsigned char *json, size_t size) {
TAILQ_INIT(&(config.bindings));
TAILQ_INIT(&(config.tray_outputs));
- yajl_status state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
+ yajl_handle handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
+ yajl_status state = yajl_parse(handle, json, size);
/* FIXME: Proper error handling for JSON parsing */
switch (state) {
@@ -418,6 +424,11 @@ void parse_config_json(char *json) {
break;
}
+ if (config.disable_ws && config.workspace_command) {
+ ELOG("You have specified 'workspace_buttons no'. Your 'workspace_command %s' will be ignored.\n", config.workspace_command);
+ FREE(config.workspace_command);
+ }
+
yajl_free(handle);
}
@@ -427,16 +438,16 @@ static int i3bar_config_string_cb(void *params_, const unsigned char *val, size_
}
/*
- * Start parsing the received bar configuration list. The only usecase right
- * now is to automatically get the first bar id.
+ * Parse the received bar configuration list. The only usecase right now is to
+ * automatically get the first bar id.
*
*/
-void parse_get_first_i3bar_config(char *json) {
+void parse_get_first_i3bar_config(const unsigned char *json, size_t size) {
yajl_callbacks configs_callbacks = {
.yajl_string = i3bar_config_string_cb,
};
yajl_handle handle = yajl_alloc(&configs_callbacks, NULL, NULL);
- yajl_parse(handle, (const unsigned char *)json, strlen(json));
+ yajl_parse(handle, json, size);
yajl_free(handle);
}
diff --git a/i3bar/src/ipc.c b/i3bar/src/ipc.c
index 06ddf9b5..35a17aed 100644
--- a/i3bar/src/ipc.c
+++ b/i3bar/src/ipc.c
@@ -24,14 +24,24 @@ ev_io *i3_connection;
const char *sock_path;
-typedef void (*handler_t)(char *);
+typedef void (*handler_t)(const unsigned char *, size_t);
+
+/*
+ * Returns true when i3bar is configured to read workspace information from i3
+ * via JSON over the i3 IPC interface, as opposed to reading workspace
+ * information from the workspace_command via JSON over stdout.
+ *
+ */
+static bool i3_provides_workspaces(void) {
+ return !config.disable_ws && config.workspace_command == NULL;
+}
/*
* Called, when we get a reply to a command from i3.
* Since i3 does not give us much feedback on commands, we do not much
*
*/
-static void got_command_reply(char *reply) {
+static void got_command_reply(const unsigned char *reply, size_t size) {
/* TODO: Error handling for command replies */
}
@@ -39,9 +49,9 @@ static void got_command_reply(char *reply) {
* Called, when we get a reply with workspaces data
*
*/
-static void got_workspace_reply(char *reply) {
+static void got_workspace_reply(const unsigned char *reply, size_t size) {
DLOG("Got workspace data!\n");
- parse_workspaces_json(reply);
+ parse_workspaces_json(reply, size);
draw_bars(false);
}
@@ -50,7 +60,7 @@ static void got_workspace_reply(char *reply) {
* Since i3 does not give us much feedback on commands, we do not much
*
*/
-static void got_subscribe_reply(char *reply) {
+static void got_subscribe_reply(const unsigned char *reply, size_t size) {
DLOG("Got subscribe reply: %s\n", reply);
/* TODO: Error handling for subscribe commands */
}
@@ -59,12 +69,12 @@ static void got_subscribe_reply(char *reply) {
* Called, when we get a reply with outputs data
*
*/
-static void got_output_reply(char *reply) {
+static void got_output_reply(const unsigned char *reply, size_t size) {
DLOG("Clearing old output configuration...\n");
free_outputs();
DLOG("Parsing outputs JSON...\n");
- parse_outputs_json(reply);
+ parse_outputs_json(reply, size);
DLOG("Reconfiguring windows...\n");
reconfig_windows(false);
@@ -73,8 +83,19 @@ static void got_output_reply(char *reply) {
kick_tray_clients(o_walk);
}
- if (!config.disable_ws) {
+ if (i3_provides_workspaces()) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
+ } else if (config.workspace_command) {
+ /* Communication with the workspace child is one-way. Since we called
+ * free_outputs() and free_workspaces() we have lost our workspace
+ * information which will result in no workspace buttons. A
+ * well-behaving client should be subscribed to output events as well
+ * and re-send the output information to i3bar. Even in that case
+ * though there is a race condition where the child can send the new
+ * workspace information after the output change before i3bar receives
+ * the output event from i3. For this reason, we re-parse the latest
+ * received JSON. */
+ repeat_last_ws_json();
}
draw_bars(false);
@@ -84,10 +105,10 @@ static void got_output_reply(char *reply) {
* Called when we get the configuration for our bar instance
*
*/
-static void got_bar_config(char *reply) {
+static void got_bar_config(const unsigned char *reply, size_t size) {
if (!config.bar_id) {
DLOG("Received bar list \"%s\"\n", reply);
- parse_get_first_i3bar_config(reply);
+ parse_get_first_i3bar_config(reply, size);
if (!config.bar_id) {
ELOG("No bar configuration found, please configure a bar block in your i3 config file.\n");
@@ -106,13 +127,14 @@ static void got_bar_config(char *reply) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
free_colors(&(config.colors));
- parse_config_json(reply);
+ parse_config_json(reply, size);
/* Now we can actually use 'config', so let's subscribe to the appropriate
* events and request the workspaces if necessary. */
subscribe_events();
- if (!config.disable_ws)
+ if (i3_provides_workspaces()) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
+ }
/* Initialize the rest of XCB */
init_xcb_late(config.fontname);
@@ -121,6 +143,7 @@ static void got_bar_config(char *reply) {
init_colors(&(config.colors));
start_child(config.command);
+ start_ws_child(config.workspace_command);
}
/* Data structure to easily call the reply handlers later */
@@ -143,7 +166,7 @@ handler_t reply_handlers[] = {
* Called, when a workspace event arrives (i.e. the user changed the workspace)
*
*/
-static void got_workspace_event(char *event) {
+static void got_workspace_event(const unsigned char *event, size_t size) {
DLOG("Got workspace event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
}
@@ -152,7 +175,7 @@ static void got_workspace_event(char *event) {
* Called, when an output event arrives (i.e. the screen configuration changed)
*
*/
-static void got_output_event(char *event) {
+static void got_output_event(const unsigned char *event, size_t size) {
DLOG("Got output event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
}
@@ -161,9 +184,9 @@ static void got_output_event(char *event) {
* Called, when a mode event arrives (i3 changed binding mode).
*
*/
-static void got_mode_event(char *event) {
+static void got_mode_event(const unsigned char *event, size_t size) {
DLOG("Got mode event!\n");
- parse_mode_json(event);
+ parse_mode_json(event, size);
draw_bars(false);
}
@@ -183,14 +206,15 @@ static bool strings_differ(char *a, char *b) {
* Called, when a barconfig_update event arrives (i.e. i3 changed the bar hidden_state or mode)
*
*/
-static void got_bar_config_update(char *event) {
+static void got_bar_config_update(const unsigned char *event, size_t size) {
/* check whether this affect this bar instance by checking the bar_id */
char *expected_id;
sasprintf(&expected_id, "\"id\":\"%s\"", config.bar_id);
- char *found_id = strstr(event, expected_id);
+ char *found_id = strstr((const char *)event, expected_id);
FREE(expected_id);
- if (found_id == NULL)
+ if (found_id == NULL) {
return;
+ }
/* reconfigure the bar based on the current outputs */
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
@@ -201,10 +225,12 @@ static void got_bar_config_update(char *event) {
DLOG("Received bar config update \"%s\"\n", event);
char *old_command = config.command;
+ char *old_workspace_command = config.workspace_command;
config.command = NULL;
+ config.workspace_command = NULL;
bar_display_mode_t old_mode = config.hide_on_modifier;
- parse_config_json(event);
+ parse_config_json(event, size);
if (old_mode != config.hide_on_modifier) {
reconfig_windows(true);
}
@@ -214,13 +240,21 @@ static void got_bar_config_update(char *event) {
init_colors(&(config.colors));
/* restart status command process */
- if (strings_differ(old_command, config.command)) {
+ if (!status_child_is_alive() || strings_differ(old_command, config.command)) {
kill_child();
clear_statusline(&statusline_head, true);
start_child(config.command);
}
free(old_command);
+ /* restart workspace command process */
+ if (!ws_child_is_alive() || strings_differ(old_workspace_command, config.workspace_command)) {
+ free_workspaces();
+ kill_ws_child();
+ start_ws_child(config.workspace_command);
+ }
+ free(old_workspace_command);
+
draw_bars(false);
}
@@ -249,7 +283,7 @@ static void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
* we have to expect */
uint32_t rec = 0;
while (rec < header_len) {
- int n = read(fd, header + rec, header_len - rec);
+ const int n = read(fd, header + rec, header_len - rec);
if (n == -1) {
ELOG("read() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
@@ -258,10 +292,11 @@ static 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");
+ clean_xcb();
+ free(header);
#ifdef I3_ASAN_ENABLED
__lsan_do_leak_check();
#endif
- clean_xcb();
exit(EXIT_SUCCESS);
}
rec += n;
@@ -284,7 +319,7 @@ static void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
/* Now that we know, what to expect, we can start read()ing the rest
* of the message */
- char *buffer = smalloc(size + 1);
+ unsigned char *buffer = smalloc(size + 1);
rec = 0;
while (rec < size) {
@@ -304,10 +339,11 @@ static void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
/* And call the callback (indexed by the type) */
if (type & (1UL << 31)) {
type ^= 1UL << 31;
- event_handlers[type](buffer);
+ event_handlers[type](buffer, size);
} else {
- if (reply_handlers[type])
- reply_handlers[type](buffer);
+ if (reply_handlers[type]) {
+ reply_handlers[type](buffer, size);
+ }
}
FREE(header);
@@ -340,8 +376,9 @@ int i3_send_msg(uint32_t type, const char *payload) {
memcpy(walk, &type, sizeof(uint32_t));
walk += sizeof(uint32_t);
- if (payload != NULL)
- strncpy(walk, payload, len);
+ if (payload != NULL) {
+ memcpy(walk, payload, len);
+ }
swrite(i3_connection->fd, buffer, to_write);
@@ -376,9 +413,9 @@ void destroy_connection(void) {
*
*/
void subscribe_events(void) {
- if (config.disable_ws) {
- i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\", \"barconfig_update\" ]");
- } else {
+ if (i3_provides_workspaces()) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\", \"barconfig_update\" ]");
+ } else {
+ i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\", \"barconfig_update\" ]");
}
}
diff --git a/i3bar/src/main.c b/i3bar/src/main.c
index 4e93bb02..ce5257bf 100644
--- a/i3bar/src/main.c
+++ b/i3bar/src/main.c
@@ -185,12 +185,12 @@ int main(int argc, char **argv) {
ev_signal_start(main_loop, sig_int);
ev_signal_start(main_loop, sig_hup);
+ atexit(kill_children_at_exit);
+
/* From here on everything should run smooth for itself, just start listening for
* events. We stop simply stop the event loop, when we are finished */
ev_loop(main_loop, 0);
- kill_child();
-
clean_xcb();
ev_default_destroy();
diff --git a/i3bar/src/mode.c b/i3bar/src/mode.c
index 13d02110..0eb8a17f 100644
--- a/i3bar/src/mode.c
+++ b/i3bar/src/mode.c
@@ -16,7 +16,6 @@
/* A datatype to pass through the callbacks to save the state */
struct mode_json_params {
- char *json;
char *cur_key;
char *name;
bool pango_markup;
@@ -31,7 +30,7 @@ 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")) {
- sasprintf(&(params->name), "%.*s", len, val);
+ sasprintf(&(params->name), "%.*s", (int)len, val);
FREE(params->cur_key);
return 1;
}
@@ -68,7 +67,7 @@ static int mode_boolean_cb(void *params_, int val) {
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);
+ sasprintf(&(params->cur_key), "%.*s", (int)keyLen, keyVal);
return 1;
}
@@ -96,26 +95,17 @@ static yajl_callbacks mode_callbacks = {
};
/*
- * Start parsing the received JSON string
+ * Parse the received JSON string
*
*/
-void parse_mode_json(char *json) {
- /* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
- * JSON in chunks */
+void parse_mode_json(const unsigned char *json, size_t size) {
struct mode_json_params params;
-
mode binding;
-
params.cur_key = NULL;
- params.json = json;
params.mode = &binding;
- yajl_handle handle;
- yajl_status state;
-
- handle = yajl_alloc(&mode_callbacks, NULL, (void *)¶ms);
-
- state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
+ yajl_handle handle = yajl_alloc(&mode_callbacks, NULL, (void *)¶ms);
+ yajl_status state = yajl_parse(handle, json, size);
/* FIXME: Proper error handling for JSON parsing */
switch (state) {
@@ -129,8 +119,9 @@ void parse_mode_json(char *json) {
}
/* We don't want to indicate default binding mode */
- if (strcmp("default", i3string_as_utf8(params.mode->name)) == 0)
+ if (strcmp("default", i3string_as_utf8(params.mode->name)) == 0) {
I3STRING_FREE(params.mode->name);
+ }
/* Set the new binding mode */
set_current_mode(&binding);
diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c
index 168f3eef..f855ba7c 100644
--- a/i3bar/src/outputs.c
+++ b/i3bar/src/outputs.c
@@ -18,10 +18,8 @@
/* A datatype to pass through the callbacks to save the state */
struct outputs_json_params {
- struct outputs_head *outputs;
i3_output *outputs_walk;
char *cur_key;
- char *json;
bool in_rect;
};
@@ -108,14 +106,15 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
if (!strcmp(params->cur_key, "current_workspace")) {
char *copy = NULL;
- sasprintf(©, "%.*s", len, val);
+ sasprintf(©, "%.*s", (int)len, val);
char *end;
errno = 0;
long parsed_num = strtol(copy, &end, 10);
if (errno == 0 &&
- (end && *end == '\0'))
+ (end && *end == '\0')) {
params->outputs_walk->ws = parsed_num;
+ }
FREE(copy);
FREE(params->cur_key);
@@ -126,7 +125,7 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
return 0;
}
- sasprintf(&(params->outputs_walk->name), "%.*s", len, val);
+ sasprintf(&(params->outputs_walk->name), "%.*s", (int)len, val);
FREE(params->cur_key);
return 1;
@@ -148,7 +147,6 @@ static int outputs_start_map_cb(void *params_) {
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));
memset(&new_output->bar, 0, sizeof(surface_t));
memset(&new_output->buffer, 0, sizeof(surface_t));
@@ -237,7 +235,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);
- sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
+ sasprintf(&(params->cur_key), "%.*s", (int)keyLen, keyVal);
return 1;
}
@@ -263,21 +261,17 @@ void init_outputs(void) {
}
/*
- * Start parsing the received JSON string
+ * Parse the received JSON string
*
*/
-void parse_outputs_json(char *json) {
+void parse_outputs_json(const unsigned char *json, size_t size) {
struct outputs_json_params params;
params.outputs_walk = NULL;
params.cur_key = NULL;
- params.json = json;
params.in_rect = false;
- yajl_handle handle;
- yajl_status state;
- handle = yajl_alloc(&outputs_callbacks, NULL, (void *)¶ms);
-
- state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
+ yajl_handle handle = yajl_alloc(&outputs_callbacks, NULL, (void *)¶ms);
+ yajl_status state = yajl_parse(handle, json, size);
/* FIXME: Proper errorhandling for JSON-parsing */
switch (state) {
@@ -291,6 +285,7 @@ void parse_outputs_json(char *json) {
}
yajl_free(handle);
+ free(params.cur_key);
}
/*
@@ -319,12 +314,14 @@ void free_outputs(void) {
*
*/
i3_output *get_output_by_name(char *name) {
- i3_output *walk;
if (name == NULL) {
return NULL;
}
+ const bool is_primary = !strcasecmp(name, "primary");
+
+ i3_output *walk;
SLIST_FOREACH (walk, outputs, slist) {
- if (!strcmp(walk->name, name)) {
+ if ((is_primary && walk->primary) || !strcmp(walk->name, name)) {
break;
}
}
diff --git a/i3bar/src/parse_json_header.c b/i3bar/src/parse_json_header.c
index c74a62fe..b4490574 100644
--- a/i3bar/src/parse_json_header.c
+++ b/i3bar/src/parse_json_header.c
@@ -106,11 +106,13 @@ void parse_json_header(i3bar_child *child, const unsigned char *buffer, int leng
yajl_status state = yajl_parse(handle, buffer, length);
if (state != yajl_status_ok) {
child_init(child);
- if (consumed != NULL)
+ if (consumed != NULL) {
*consumed = 0;
+ }
} else {
- if (consumed != NULL)
+ if (consumed != NULL) {
*consumed = yajl_get_bytes_consumed(handle);
+ }
}
yajl_free(handle);
diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c
index bd56f5d0..26abf452 100644
--- a/i3bar/src/workspaces.c
+++ b/i3bar/src/workspaces.c
@@ -19,15 +19,15 @@ struct workspaces_json_params {
struct ws_head *workspaces;
i3_ws *workspaces_walk;
char *cur_key;
- char *json;
+ bool parsing_rect;
};
/*
* Parse a boolean value (visible, focused, urgent)
*
*/
-static int workspaces_boolean_cb(void *params_, int val) {
- struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
+static int workspaces_boolean_cb(void *params_, const int val) {
+ struct workspaces_json_params *params = params_;
if (!strcmp(params->cur_key, "visible")) {
params->workspaces_walk->visible = val;
@@ -56,8 +56,8 @@ static int workspaces_boolean_cb(void *params_, int val) {
* Parse an integer (num or the rect)
*
*/
-static int workspaces_integer_cb(void *params_, long long val) {
- struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
+static int workspaces_integer_cb(void *params_, const long long val) {
+ struct workspaces_json_params *params = params_;
if (!strcmp(params->cur_key, "id")) {
params->workspaces_walk->id = val;
@@ -71,26 +71,23 @@ static int workspaces_integer_cb(void *params_, long long val) {
return 1;
}
+ /* rect is unused, so we don't bother to save it */
if (!strcmp(params->cur_key, "x")) {
- params->workspaces_walk->rect.x = (int)val;
FREE(params->cur_key);
return 1;
}
if (!strcmp(params->cur_key, "y")) {
- params->workspaces_walk->rect.y = (int)val;
FREE(params->cur_key);
return 1;
}
if (!strcmp(params->cur_key, "width")) {
- params->workspaces_walk->rect.w = (int)val;
FREE(params->cur_key);
return 1;
}
if (!strcmp(params->cur_key, "height")) {
- params->workspaces_walk->rect.h = (int)val;
FREE(params->cur_key);
return 1;
}
@@ -103,8 +100,8 @@ static int workspaces_integer_cb(void *params_, long long val) {
* Parse a string (name, output)
*
*/
-static int workspaces_string_cb(void *params_, const unsigned char *val, size_t len) {
- struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
+static int workspaces_string_cb(void *params_, const unsigned char *val, const size_t len) {
+ struct workspaces_json_params *params = params_;
if (!strcmp(params->cur_key, "name")) {
const char *ws_name = (const char *)val;
@@ -120,14 +117,15 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
size_t offset = strspn(ws_name, ws_num);
/* Also strip off the conventional ws name delimiter */
- if (offset && ws_name[offset] == ':')
+ if (offset && ws_name[offset] == ':') {
offset += 1;
+ }
if (config.strip_ws_numbers) {
/* Offset may be equal to length, in which case display the number */
- params->workspaces_walk->name = (offset < len
- ? i3string_from_markup_with_length(ws_name + offset, len - offset)
- : i3string_from_markup(ws_num));
+ params->workspaces_walk->name = offset < len
+ ? i3string_from_markup_with_length(ws_name + offset, len - offset)
+ : i3string_from_markup(ws_num);
} else {
params->workspaces_walk->name = i3string_from_markup(ws_num);
}
@@ -153,18 +151,18 @@ 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 */
char *output_name = NULL;
- sasprintf(&output_name, "%.*s", len, val);
+ sasprintf(&output_name, "%.*s", (int)len, val);
i3_output *target = get_output_by_name(output_name);
+ i3_ws *ws = params->workspaces_walk;
if (target != NULL) {
- params->workspaces_walk->output = target;
-
- TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces,
- params->workspaces_walk,
- tailq);
+ ws->output = target;
+ TAILQ_INSERT_TAIL(ws->output->workspaces, ws, tailq);
}
FREE(output_name);
+ FREE(params->cur_key);
+
return 1;
}
@@ -172,28 +170,52 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
}
/*
- * We hit the start of a JSON map (rect or a new output)
+ * We hit the start of a JSON map (rect or a new workspace)
*
*/
static int workspaces_start_map_cb(void *params_) {
- struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
-
- i3_ws *new_workspace = NULL;
+ struct workspaces_json_params *params = params_;
if (params->cur_key == NULL) {
- new_workspace = smalloc(sizeof(i3_ws));
+ i3_ws *new_workspace = scalloc(1, sizeof(i3_ws));
new_workspace->num = -1;
- new_workspace->name = NULL;
- new_workspace->visible = 0;
- new_workspace->focused = 0;
- new_workspace->urgent = 0;
- memset(&new_workspace->rect, 0, sizeof(rect));
- new_workspace->output = NULL;
params->workspaces_walk = new_workspace;
+ params->parsing_rect = false;
+ } else {
+ params->parsing_rect = true;
+ }
+
+ return 1;
+}
+
+static int workspaces_end_map_cb(void *params_) {
+ struct workspaces_json_params *params = params_;
+
+ if (params->parsing_rect) {
+ params->parsing_rect = false;
return 1;
}
+ i3_ws *ws = params->workspaces_walk;
+ if (!ws || ws->output) {
+ return 1; /* workspace already assigned to output */
+ }
+
+ if (!ws->name || SLIST_EMPTY(outputs)) { /* Invalid state */
+ I3STRING_FREE(ws->name);
+ FREE(ws->canonical_name);
+ FREE(params->workspaces_walk);
+ return 1;
+ }
+
+ /* Handle no output case */
+ ws->output = get_output_by_name("primary");
+ if (ws->output == NULL) {
+ ws->output = SLIST_FIRST(outputs);
+ }
+ TAILQ_INSERT_TAIL(ws->output->workspaces, ws, tailq);
+
return 1;
}
@@ -203,56 +225,55 @@ static int workspaces_start_map_cb(void *params_) {
* Essentially we just save it in the parsing state
*
*/
-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_;
+static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, const size_t keyLen) {
+ struct workspaces_json_params *params = params_;
FREE(params->cur_key);
- sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
+ sasprintf(¶ms->cur_key, "%.*s", (int)keyLen, keyVal);
return 1;
}
-/* A datastructure to pass all these callbacks to yajl */
+/* A data structure to pass all these callbacks to yajl */
static yajl_callbacks workspaces_callbacks = {
.yajl_boolean = workspaces_boolean_cb,
.yajl_integer = workspaces_integer_cb,
.yajl_string = workspaces_string_cb,
.yajl_start_map = workspaces_start_map_cb,
+ .yajl_end_map = workspaces_end_map_cb,
.yajl_map_key = workspaces_map_key_cb,
};
/*
- * Start parsing the received JSON string
+ * Parse the received JSON string
*
*/
-void parse_workspaces_json(char *json) {
- /* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
- * JSON in chunks */
- struct workspaces_json_params params;
-
+void parse_workspaces_json(const unsigned char *json, const size_t size) {
free_workspaces();
- params.workspaces_walk = NULL;
- params.cur_key = NULL;
- params.json = json;
-
- yajl_handle handle;
- yajl_status state;
- handle = yajl_alloc(&workspaces_callbacks, NULL, (void *)¶ms);
-
- state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
+ struct workspaces_json_params params = {0};
+ const yajl_handle handle = yajl_alloc(&workspaces_callbacks, NULL, ¶ms);
+ const yajl_status state = yajl_parse(handle, json, size);
/* FIXME: Proper error handling for JSON parsing */
switch (state) {
case yajl_status_ok:
break;
case yajl_status_client_canceled:
- case yajl_status_error:
- ELOG("Could not parse workspaces reply!\n");
- exit(EXIT_FAILURE);
+ case yajl_status_error: {
+ unsigned char *err = yajl_get_error(handle, 1, json, size);
+ ELOG("Could not parse workspaces reply, error:\n%s\njson:---%s---\n", (char *)err, (char *)json);
+ yajl_free_error(handle, err);
+
+ if (config.workspace_command) {
+ kill_ws_child();
+ set_workspace_button_error("Could not parse workspace_command's JSON");
+ } else {
+ exit(EXIT_FAILURE);
+ }
break;
+ }
}
yajl_free(handle);
-
FREE(params.cur_key);
}
@@ -261,14 +282,14 @@ void parse_workspaces_json(char *json) {
*
*/
void free_workspaces(void) {
- i3_output *outputs_walk;
if (outputs == NULL) {
return;
}
- i3_ws *ws_walk;
+ i3_output *outputs_walk;
SLIST_FOREACH (outputs_walk, outputs, slist) {
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
+ i3_ws *ws_walk;
TAILQ_FOREACH (ws_walk, outputs_walk->workspaces, tailq) {
I3STRING_FREE(ws_walk->name);
FREE(ws_walk->canonical_name);
diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c
index 0cda125c..2ae7fcde 100644
--- a/i3bar/src/xcb.c
+++ b/i3bar/src/xcb.c
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
@@ -138,8 +139,9 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
}
static uint32_t get_sep_offset(struct status_block *block) {
- if (!block->no_separator && block->sep_block_width > 0)
+ if (!block->no_separator && block->sep_block_width > 0) {
return block->sep_block_width / 2 + block->sep_block_width % 2;
+ }
return 0;
}
@@ -147,12 +149,14 @@ static int get_tray_width(struct tc_head *trayclients) {
trayclient *trayclient;
int tray_width = 0;
TAILQ_FOREACH_REVERSE (trayclient, trayclients, tc_head, tailq) {
- if (!trayclient->mapped)
+ if (!trayclient->mapped) {
continue;
+ }
tray_width += icon_size + logical_px(config.tray_padding);
}
- if (tray_width > 0)
+ if (tray_width > 0) {
tray_width += logical_px(tray_loff_px);
+ }
return tray_width;
}
@@ -165,8 +169,9 @@ static void draw_separator(i3_output *output, uint32_t x, struct status_block *b
color_t bar_bg = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
uint32_t sep_offset = get_sep_offset(block);
- if (TAILQ_NEXT(block, blocks) == NULL || sep_offset == 0)
+ if (TAILQ_NEXT(block, blocks) == NULL || sep_offset == 0) {
return;
+ }
uint32_t center_x = x - sep_offset;
if (config.separator_symbol == NULL) {
@@ -184,50 +189,103 @@ static void draw_separator(i3_output *output, uint32_t x, struct status_block *b
}
}
-static uint32_t predict_statusline_length(bool use_short_text) {
+static void predict_block_length(struct status_block *block) {
+ i3String *text = block->full_text;
+ struct status_block_render_desc *render = &block->full_render;
+ if (block->use_short && block->short_text != NULL) {
+ text = block->short_text;
+ render = &block->short_render;
+ }
+
+ if (i3string_get_num_bytes(text) == 0) {
+ block->render_length = 0;
+ return;
+ }
+
+ render->width = predict_text_width(text);
+ if (block->border) {
+ render->width += logical_px(block->border_left + block->border_right);
+ }
+
+ /* Compute offset and append for text alignment in min_width. */
+ if (block->min_width <= render->width) {
+ render->x_offset = 0;
+ render->x_append = 0;
+ } else {
+ uint32_t padding_width = block->min_width - render->width;
+ switch (block->align) {
+ case ALIGN_LEFT:
+ render->x_append = padding_width;
+ break;
+ case ALIGN_RIGHT:
+ render->x_offset = padding_width;
+ break;
+ case ALIGN_CENTER:
+ render->x_offset = padding_width / 2;
+ render->x_append = padding_width / 2 + padding_width % 2;
+ break;
+ }
+ }
+
+ block->render_length = render->width + render->x_offset + render->x_append;
+}
+
+static uint32_t predict_statusline_length(void) {
uint32_t width = 0;
struct status_block *block;
TAILQ_FOREACH (block, &statusline_head, blocks) {
- i3String *text = block->full_text;
- struct status_block_render_desc *render = &block->full_render;
- if (use_short_text && block->short_text != NULL) {
- text = block->short_text;
- render = &block->short_render;
- }
-
- if (i3string_get_num_bytes(text) == 0)
+ predict_block_length(block);
+ uint32_t block_width = block->render_length;
+ if (block_width == 0) {
continue;
-
- render->width = predict_text_width(text);
- if (block->border)
- render->width += logical_px(block->border_left + block->border_right);
-
- /* Compute offset and append for text alignment in min_width. */
- if (block->min_width <= render->width) {
- render->x_offset = 0;
- render->x_append = 0;
- } else {
- uint32_t padding_width = block->min_width - render->width;
- switch (block->align) {
- case ALIGN_LEFT:
- render->x_append = padding_width;
- break;
- case ALIGN_RIGHT:
- render->x_offset = padding_width;
- break;
- case ALIGN_CENTER:
- render->x_offset = padding_width / 2;
- render->x_append = padding_width / 2 + padding_width % 2;
- break;
- }
}
- width += render->width + render->x_offset + render->x_append;
+ width += block_width;
/* If this is not the last block, add some pixels for a separator. */
- if (TAILQ_NEXT(block, blocks) != NULL)
+ if (TAILQ_NEXT(block, blocks) != NULL) {
width += block->sep_block_width;
+ }
+ }
+
+ return width;
+}
+
+static uint32_t switch_block_to_short(struct status_block *block) {
+ /* Skip blocks that have no short form or are already in short form */
+ if (block->short_text == NULL || block->use_short) {
+ return 0;
+ }
+ uint32_t full = block->render_length;
+ block->use_short = true;
+ predict_block_length(block);
+ return full - block->render_length;
+}
+
+static uint32_t adjust_statusline_length(uint32_t max_length) {
+ uint32_t width = predict_statusline_length();
+
+ /* Progressively switch the blocks to short mode */
+ struct status_block *block;
+ TAILQ_FOREACH (block, &statusline_head, blocks) {
+ if (width < max_length) {
+ break;
+ }
+ width -= switch_block_to_short(block);
+
+ /* Provide support for representing a single logical block using multiple
+ * JSON blocks: if one block is shortened, ensure that all other blocks
+ * with the same name are also shortened such that the entire logical block uses
+ * the short form text. */
+ if (block->name) {
+ struct status_block *other;
+ TAILQ_FOREACH (other, &statusline_head, blocks) {
+ if (other->name && !strcmp(other->name, block->name)) {
+ width -= switch_block_to_short(other);
+ }
+ }
+ }
}
return width;
@@ -236,7 +294,7 @@ static uint32_t predict_statusline_length(bool use_short_text) {
/*
* Redraws the statusline to the output's statusline_buffer
*/
-static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors, bool use_short_text) {
+static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors) {
struct status_block *block;
color_t bar_color = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
@@ -254,13 +312,14 @@ static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focu
TAILQ_FOREACH (block, &statusline_head, blocks) {
i3String *text = block->full_text;
struct status_block_render_desc *render = &block->full_render;
- if (use_short_text && block->short_text != NULL) {
+ if (block->use_short && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
}
- if (i3string_get_num_bytes(text) == 0)
+ if (i3string_get_num_bytes(text) == 0) {
continue;
+ }
color_t fg_color;
if (block->urgent) {
@@ -284,10 +343,12 @@ static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focu
border_color = colors.urgent_ws_border;
bg_color = colors.urgent_ws_bg;
} else {
- if (block->border)
+ if (block->border) {
border_color = draw_util_hex_to_color(block->border);
- if (block->background)
+ }
+ if (block->background) {
bg_color = draw_util_hex_to_color(block->background);
+ }
}
/* Draw the border. */
@@ -334,7 +395,7 @@ static void hide_bars(void) {
}
xcb_unmap_window(xcb_connection, walk->bar.id);
}
- stop_child();
+ stop_children();
}
/*
@@ -351,7 +412,7 @@ static void unhide_bars(void) {
uint32_t mask;
uint32_t values[5];
- cont_child();
+ cont_children();
SLIST_FOREACH (walk, outputs, slist) {
if (walk->bar.id == XCB_NONE) {
@@ -363,10 +424,11 @@ static void unhide_bars(void) {
XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_STACK_MODE;
values[0] = walk->rect.x;
- if (config.position == POS_TOP)
+ if (config.position == POS_TOP) {
values[1] = walk->rect.y;
- else
+ } else {
values[1] = walk->rect.y + walk->rect.h - bar_height;
+ }
values[2] = walk->rect.w;
values[3] = bar_height;
values[4] = XCB_STACK_MODE_ABOVE;
@@ -434,8 +496,9 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
static bool execute_custom_command(xcb_keycode_t input_code, bool event_is_release) {
binding_t *binding;
TAILQ_FOREACH (binding, &(config.bindings), bindings) {
- if ((binding->input_code != input_code) || (binding->release != event_is_release))
+ if ((binding->input_code != input_code) || (binding->release != event_is_release)) {
continue;
+ }
i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, binding->command);
return true;
@@ -454,7 +517,7 @@ static void child_handle_button(xcb_button_press_event_t *event, i3_output *outp
TAILQ_FOREACH (block, &statusline_head, blocks) {
i3String *text;
struct status_block_render_desc *render;
- if (output->statusline_short_text && block->short_text != NULL) {
+ if (block->use_short && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
} else {
@@ -500,6 +563,49 @@ static int predict_button_width(int name_width) {
logical_px(config.ws_min_width));
}
+static char *quote_workspace_name(const char *in) {
+ /* To properly handle workspace names with double quotes in them, we need
+ * to escape the double quotes. We allocate a large enough buffer (twice
+ * the unescaped size is always enough), then we copy character by
+ * character. */
+ const size_t namelen = strlen(in);
+ const size_t len = namelen + strlen("workspace \"\"") + 1;
+ char *out = scalloc(2 * len, 1);
+ memcpy(out, "workspace \"", strlen("workspace \""));
+ size_t inpos, outpos;
+ for (inpos = 0, outpos = strlen("workspace \"");
+ inpos < namelen;
+ inpos++, outpos++) {
+ if (in[inpos] == '"' || in[inpos] == '\\') {
+ out[outpos] = '\\';
+ outpos++;
+ }
+ out[outpos] = in[inpos];
+ }
+ out[outpos] = '"';
+ return out;
+}
+
+static void focus_workspace(i3_ws *ws) {
+ char *buffer = NULL;
+ if (ws->id != 0) {
+ /* Workspace ID has higher precedence since the workspace_command is
+ * allowed to change workspace names as long as it provides a valid ID. */
+ sasprintf(&buffer, "[con_id=%lu] focus workspace", ws->id);
+ goto done;
+ }
+
+ if (ws->canonical_name == NULL) {
+ return;
+ }
+
+ buffer = quote_workspace_name(ws->canonical_name);
+
+done:
+ i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, buffer);
+ free(buffer);
+}
+
/*
* Handle a button press event (i.e. a mouse click on one of our bars).
* We determine, whether the click occurred on a workspace button or if the scroll-
@@ -526,19 +632,27 @@ static void handle_button(xcb_button_press_event_t *event) {
/* During button release events, only check for custom commands. */
const bool event_is_release = (event->response_type & ~0x80) == XCB_BUTTON_RELEASE;
- int32_t x = event->event_x >= 0 ? event->event_x : 0;
+ const int x = (event->event_x >= 0 ? event->event_x : 0) - logical_px(config.padding.x);
+ if (x < 0) {
+ /* Ignore clicks in padding */
+ return;
+ }
+
int workspace_width = 0;
i3_ws *cur_ws = NULL, *clicked_ws = NULL, *ws_walk;
TAILQ_FOREACH (ws_walk, walk->workspaces, tailq) {
int w = predict_button_width(ws_walk->name_width);
- if (x >= workspace_width && x <= workspace_width + w)
+ if (x >= workspace_width && x <= workspace_width + w) {
clicked_ws = ws_walk;
- if (ws_walk->visible)
+ }
+ if (ws_walk->visible) {
cur_ws = ws_walk;
+ }
workspace_width += w;
- if (TAILQ_NEXT(ws_walk, tailq) != NULL)
+ if (TAILQ_NEXT(ws_walk, tailq) != NULL) {
workspace_width += logical_px(ws_spacing_px);
+ }
}
if (child_want_click_events() && x > workspace_width) {
@@ -583,8 +697,9 @@ static void handle_button(xcb_button_press_event_t *event) {
* If there is no more workspace, don’t even send the workspace
* command, otherwise (with workspace auto_back_and_forth) we’d end
* up on the wrong workspace. */
- if (cur_ws == TAILQ_FIRST(walk->workspaces))
+ if (cur_ws == TAILQ_FIRST(walk->workspaces)) {
return;
+ }
cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
break;
@@ -594,8 +709,9 @@ static void handle_button(xcb_button_press_event_t *event) {
* If there is no more workspace, don’t even send the workspace
* command, otherwise (with workspace auto_back_and_forth) we’d end
* up on the wrong workspace. */
- if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head))
+ if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head)) {
return;
+ }
cur_ws = TAILQ_NEXT(cur_ws, tailq);
break;
@@ -606,51 +722,23 @@ static void handle_button(xcb_button_press_event_t *event) {
* workspace if it is not already focused */
if (cur_ws == NULL) {
TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) {
- if (cur_ws->visible && !cur_ws->focused)
+ if (cur_ws->visible && !cur_ws->focused) {
break;
+ }
}
}
/* if there is nothing to focus, we are done */
- if (cur_ws == NULL)
+ if (cur_ws == NULL) {
return;
+ }
break;
default:
return;
}
- /* To properly handle workspace names with double quotes in them, we need
- * to escape the double quotes. Unfortunately, that’s rather ugly in C: We
- * first count the number of double quotes, then we allocate a large enough
- * buffer, then we copy character by character. */
- int num_quotes = 0;
- size_t namelen = 0;
- const char *utf8_name = cur_ws->canonical_name;
- for (const char *walk = utf8_name; *walk != '\0'; walk++) {
- if (*walk == '"' || *walk == '\\')
- num_quotes++;
- /* While we’re looping through the name anyway, we can save one
- * strlen(). */
- namelen++;
- }
-
- const size_t len = namelen + strlen("workspace \"\"") + 1;
- char *buffer = scalloc(len + num_quotes, 1);
- memcpy(buffer, "workspace \"", strlen("workspace \""));
- size_t inpos, outpos;
- for (inpos = 0, outpos = strlen("workspace \"");
- inpos < namelen;
- inpos++, outpos++) {
- if (utf8_name[inpos] == '"' || utf8_name[inpos] == '\\') {
- buffer[outpos] = '\\';
- outpos++;
- }
- buffer[outpos] = utf8_name[inpos];
- }
- buffer[outpos] = '"';
- i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, buffer);
- free(buffer);
+ focus_workspace(cur_ws);
}
/*
@@ -674,9 +762,9 @@ static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
}
if (num_visible == 0) {
- stop_child();
+ stop_children();
} else {
- cont_child();
+ cont_children();
}
}
@@ -824,12 +912,10 @@ static void handle_client_message(xcb_client_message_event_t *event) {
DLOG("_NET_SYSTEM_TRAY_OPCODE received\n");
/* event->data.data32[0] is the timestamp */
uint32_t op = event->data.data32[1];
- uint32_t mask;
uint32_t values[2];
if (op == SYSTEM_TRAY_REQUEST_DOCK) {
- xcb_window_t client = event->data.data32[2];
-
- mask = XCB_CW_EVENT_MASK;
+ const xcb_window_t client = event->data.data32[2];
+ uint32_t mask = XCB_CW_EVENT_MASK;
/* Needed to get the most recent value of XEMBED_MAPPED. */
values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
@@ -872,8 +958,9 @@ static void handle_client_message(xcb_client_message_event_t *event) {
DLOG("xembed flags = %d\n", xembed[1]);
map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED);
xe_version = xembed[0];
- if (xe_version > 1)
+ if (xe_version > 1) {
xe_version = 1;
+ }
free(xembedr);
} else {
ELOG("Window %08x violates the XEMBED protocol, _XEMBED_INFO not set\n", client);
@@ -891,8 +978,9 @@ static void handle_client_message(xcb_client_message_event_t *event) {
output_for_tray->bar.id,
output_for_tray->rect.w - icon_size - logical_px(config.tray_padding),
logical_px(config.tray_padding));
- if (xcb_request_failed(rcookie, "Could not reparent window. Maybe it is using an incorrect depth/visual?"))
+ if (xcb_request_failed(rcookie, "Could not reparent window. Maybe it is using an incorrect depth/visual?")) {
return;
+ }
/* We reconfigure the window to use a reasonable size. The systray
* specification explicitly says:
@@ -1091,17 +1179,20 @@ static void handle_configuration_change(xcb_window_t window) {
trayclient *trayclient;
i3_output *output;
SLIST_FOREACH (output, outputs, slist) {
- if (!output->active)
+ if (!output->active) {
continue;
+ }
int clients = 0;
TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) {
- if (!trayclient->mapped)
+ if (!trayclient->mapped) {
continue;
+ }
clients++;
- if (trayclient->win != window)
+ if (trayclient->win != window) {
continue;
+ }
xcb_rectangle_t rect;
rect.x = output->rect.w - (clients * (icon_size + logical_px(config.tray_padding)));
@@ -1348,8 +1439,9 @@ static void deregister_xkb_keyevents(void) {
*
*/
void init_xcb_late(char *fontname) {
- if (fontname == NULL)
+ if (fontname == NULL) {
fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
+ }
/* Load the font */
font = load_font(fontname, true);
@@ -1377,13 +1469,15 @@ void init_xcb_late(char *fontname) {
bar_height = default_px + padding_scaled;
icon_size = bar_height - 2 * logical_px(config.tray_padding);
- if (config.separator_symbol)
+ if (config.separator_symbol) {
separator_symbol_width = predict_text_width(config.separator_symbol);
+ }
xcb_flush(xcb_connection);
- if (config.hide_on_modifier == M_HIDE)
+ if (config.hide_on_modifier == M_HIDE) {
register_xkb_keyevents();
+ }
}
/*
@@ -1419,11 +1513,15 @@ static void send_tray_clientmessage(void) {
static void init_tray(void) {
DLOG("Initializing system tray functionality\n");
/* request the tray manager atom for the X11 display we are running on */
- char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
- snprintf(atomname, strlen("_NET_SYSTEM_TRAY_S") + 11, "_NET_SYSTEM_TRAY_S%d", screen);
+ /* The following line cannot use strlen as that makes compilation fail with
+ * some versions of clang (-Wgnu-folding-constant): */
+ const size_t systray_len = strlen("_NET_SYSTEM_TRAY_S") + 11;
+ char atomname[systray_len];
+ snprintf(atomname, systray_len, "_NET_SYSTEM_TRAY_S%d", screen);
xcb_intern_atom_cookie_t tray_cookie;
- if (tray_reply == NULL)
+ if (tray_reply == NULL) {
tray_cookie = xcb_intern_atom(xcb_connection, 0, strlen(atomname), atomname);
+ }
/* tray support: we need a window to own the selection */
selwin = xcb_generate_id(xcb_connection);
@@ -1590,8 +1688,9 @@ void get_atoms(void) {
*
*/
void kick_tray_clients(i3_output *output) {
- if (TAILQ_EMPTY(output->trayclients))
+ if (TAILQ_EMPTY(output->trayclients)) {
return;
+ }
trayclient *trayclient;
while (!TAILQ_EMPTY(output->trayclients)) {
@@ -1830,6 +1929,7 @@ void reconfig_windows(bool redraw_bars) {
8,
len,
class);
+ free(class);
char *name;
sasprintf(&name, "i3bar for output %s", walk->name);
@@ -1886,10 +1986,11 @@ void reconfig_windows(bool redraw_bars) {
XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_STACK_MODE;
values[0] = walk->rect.x;
- if (config.position == POS_TOP)
+ if (config.position == POS_TOP) {
values[1] = walk->rect.y;
- else
+ } else {
values[1] = walk->rect.y + walk->rect.h - bar_height;
+ }
values[2] = walk->rect.w;
values[3] = bar_height;
values[4] = XCB_STACK_MODE_ABOVE;
@@ -1945,10 +2046,10 @@ void reconfig_windows(bool redraw_bars) {
/* Unmap the window, and draw it again when in dock mode */
umap_cookie = xcb_unmap_window_checked(xcb_connection, walk->bar.id);
if (config.hide_on_modifier == M_DOCK) {
- cont_child();
+ cont_children();
map_cookie = xcb_map_window_checked(xcb_connection, walk->bar.id);
} else {
- stop_child();
+ stop_children();
}
if (config.hide_on_modifier == M_HIDE) {
@@ -2012,9 +2113,6 @@ static void draw_button(surface_t *surface, color_t fg_color, color_t bg_color,
void draw_bars(bool unhide) {
DLOG("Drawing bars...\n");
- uint32_t full_statusline_width = predict_statusline_length(false);
- uint32_t short_statusline_width = predict_statusline_length(true);
-
i3_output *outputs_walk;
SLIST_FOREACH (outputs_walk, outputs, slist) {
int workspace_width = logical_px(config.padding.x);
@@ -2065,8 +2163,9 @@ void draw_bars(bool unhide) {
workspace_width, w, ws_walk->name_width, ws_walk->name);
workspace_width += w;
- if (TAILQ_NEXT(ws_walk, tailq) != NULL)
+ if (TAILQ_NEXT(ws_walk, tailq) != NULL) {
workspace_width += logical_px(ws_spacing_px);
+ }
}
}
@@ -2088,27 +2187,28 @@ void draw_bars(bool unhide) {
uint32_t hoff = logical_px(((workspace_width > 0) + (tray_width > 0)) * sb_hoff_px);
uint32_t max_statusline_width = outputs_walk->rect.w - workspace_width - tray_width - hoff;
uint32_t clip_left = 0;
- uint32_t statusline_width = full_statusline_width;
- bool use_short_text = false;
+
+ /* Reset short mode between outputs */
+ struct status_block *block;
+ TAILQ_FOREACH (block, &statusline_head, blocks) {
+ block->use_short = false;
+ }
+
+ uint32_t statusline_width = adjust_statusline_length(max_statusline_width);
if (statusline_width > max_statusline_width) {
- statusline_width = short_statusline_width;
- use_short_text = true;
- if (statusline_width > max_statusline_width) {
- clip_left = statusline_width - max_statusline_width;
- }
+ clip_left = statusline_width - max_statusline_width;
}
int16_t visible_statusline_width = MIN(statusline_width, max_statusline_width);
int x_dest = outputs_walk->rect.w - tray_width - logical_px((tray_width > 0) * sb_hoff_px) - visible_statusline_width;
x_dest -= logical_px(config.padding.width);
- draw_statusline(outputs_walk, clip_left, use_focus_colors, use_short_text);
+ draw_statusline(outputs_walk, clip_left, use_focus_colors);
draw_util_copy_surface(&outputs_walk->statusline_buffer, &outputs_walk->buffer, 0, 0,
x_dest, 0, visible_statusline_width, (int16_t)bar_height);
outputs_walk->statusline_width = statusline_width;
- outputs_walk->statusline_short_text = use_short_text;
}
}
diff --git a/include/all.h b/include/all.h
index 0d4dbc4e..1306ef23 100644
--- a/include/all.h
+++ b/include/all.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* This header file includes all relevant files of i3 and the most often used
@@ -28,9 +28,10 @@
#include
#include
-#include
#include
#include
+#include
+#include
#include "libi3.h"
#include "data.h"
diff --git a/include/assignments.h b/include/assignments.h
index a4dc766b..67b5d16f 100644
--- a/include/assignments.h
+++ b/include/assignments.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* assignments.c: Assignments for specific windows (for_window).
diff --git a/include/bindings.h b/include/bindings.h
index df3c32a5..b78a00fc 100644
--- a/include/bindings.h
+++ b/include/bindings.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* bindings.h: Functions for configuring, finding, and running bindings.
diff --git a/include/click.h b/include/click.h
index 898f1870..7380c4f5 100644
--- a/include/click.h
+++ b/include/click.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* click.c: Button press (mouse click) events.
diff --git a/include/commands.h b/include/commands.h
index 2ae2643c..6a522681 100644
--- a/include/commands.h
+++ b/include/commands.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* commands.c: all command functions (see commands_parser.c)
@@ -90,7 +90,7 @@ void cmd_nop(I3_CMD, const char *comment);
* Implementation of 'append_layout '.
*
*/
-void cmd_append_layout(I3_CMD, const char *path);
+void cmd_append_layout(I3_CMD, const char *cpath);
/**
* Implementation of 'workspace next|prev|next_on_output|prev_on_output'.
@@ -102,7 +102,7 @@ void cmd_workspace(I3_CMD, const char *which);
* Implementation of 'workspace [--no-auto-back-and-forth] number '
*
*/
-void cmd_workspace_number(I3_CMD, const char *which, const char *no_auto_back_and_forth);
+void cmd_workspace_number(I3_CMD, const char *which, const char *no_auto_back_and_forth_str);
/**
* Implementation of 'workspace back_and_forth'.
@@ -114,7 +114,7 @@ void cmd_workspace_back_and_forth(I3_CMD);
* Implementation of 'workspace [--no-auto-back-and-forth] '
*
*/
-void cmd_workspace_name(I3_CMD, const char *name, const char *no_auto_back_and_forth);
+void cmd_workspace_name(I3_CMD, const char *name, const char *no_auto_back_and_forth_str);
/**
* Implementation of 'mark [--add|--replace] [--toggle] '
@@ -174,13 +174,13 @@ void cmd_exec(I3_CMD, const char *nosn, const char *command);
* Implementation of 'focus left|right|up|down'.
*
*/
-void cmd_focus_direction(I3_CMD, const char *direction);
+void cmd_focus_direction(I3_CMD, const char *direction_str);
/**
* Implementation of 'focus next|prev sibling'
*
*/
-void cmd_focus_sibling(I3_CMD, const char *direction);
+void cmd_focus_sibling(I3_CMD, const char *direction_str);
/**
* Implementation of 'focus tiling|floating|mode_toggle'.
@@ -198,7 +198,7 @@ void cmd_focus_level(I3_CMD, const char *level);
* Implementation of 'focus'.
*
*/
-void cmd_focus(I3_CMD);
+void cmd_focus(I3_CMD, bool focus_workspace);
/**
* Implementation of 'fullscreen [enable|disable|toggle] [global]'.
diff --git a/include/commands_parser.h b/include/commands_parser.h
index 7e1c5203..e2557eac 100644
--- a/include/commands_parser.h
+++ b/include/commands_parser.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* commands.c: all command functions (see commands_parser.c)
@@ -12,6 +12,18 @@
#include
#include
+#include "parser_util.h"
+#include "queue.h"
+
+/**
+ * Helper data structure for an operation window (window on which the operation
+ * will be performed). Used to build the TAILQ owindows.
+ *
+ */
+typedef struct owindow {
+ Con *con;
+ TAILQ_ENTRY(owindow) owindows;
+} owindow;
/**
* Holds an intermediate representation of the result of a call to any command.
@@ -19,6 +31,9 @@
* internally use this struct when calling cmd_floating and cmd_border.
*/
struct CommandResultIR {
+ /* The parser context this command is executing in. */
+ struct cmd_parser_ctx *ctx;
+
/* The JSON generator to append a reply to (may be NULL). */
yajl_gen json_gen;
@@ -35,6 +50,28 @@ struct CommandResultIR {
bool needs_tree_render;
};
+/* Define the owindows head structure here so it's complete */
+TAILQ_HEAD(owindows_head, owindow);
+
+/**
+ * Context structure for the command parser, making it re-entrant.
+ */
+struct cmd_parser_ctx {
+ int state;
+ Match current_match;
+
+ /* The (small) stack where identified literals are stored during the parsing
+ * of a single command (like $workspace). */
+ struct stack stack;
+
+ /* List of operation windows (windows on which operations will be performed).
+ * Used to build the TAILQ owindows. */
+ struct owindows_head owindows;
+
+ struct CommandResultIR subcommand_output;
+ struct CommandResultIR command_output;
+};
+
typedef struct CommandResult CommandResult;
/**
diff --git a/include/con.h b/include/con.h
index e1bb6813..63107137 100644
--- a/include/con.h
+++ b/include/con.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* con.c: Functions which deal with containers directly (creating containers,
@@ -83,6 +83,22 @@ bool con_is_split(Con *con);
*/
bool con_is_hidden(Con *con);
+/**
+ * Returns true if the container is maximized in the given orientation.
+ *
+ * If the container is floating or fullscreen, it is not considered maximized.
+ * Otherwise, it is maximized if it doesn't share space with any other
+ * container in the given orientation. For example, if a workspace contains
+ * a single splitv container with three children, none of them are considered
+ * vertically maximized, but they are all considered horizontally maximized.
+ *
+ * Passing "maximized" hints to the application can help it make the right
+ * choices about how to draw its borders. See discussion in
+ * https://github.com/i3/i3/pull/2380.
+ *
+ */
+bool con_is_maximized(Con *con, orientation_t orientation);
+
/**
* Returns whether the container or any of its children is sticky.
*
diff --git a/include/config_directives.h b/include/config_directives.h
index 600226e9..fd384c0c 100644
--- a/include/config_directives.h
+++ b/include/config_directives.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* config_directives.h: all config storing functions (see config_parser.c)
@@ -17,7 +17,7 @@
* A utility function to convert a string containing the group and modifiers to
* the corresponding bit mask.
*/
-i3_event_state_mask_t event_state_from_str(const char *str);
+i3_event_state_mask_t event_state_from_str(const char *str) __attribute__((__pure__));
/** The beginning of the prototype for every cfg_ function. */
#define I3_CFG Match *current_match, struct ConfigResultIR *result
@@ -43,7 +43,7 @@ CFGFUN(include, const char *pattern);
CFGFUN(font, const char *font);
CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command);
CFGFUN(for_window, const char *command);
-CFGFUN(gaps, const char *workspace, const char *type, const long value);
+CFGFUN(gaps, const char *workspace, const char *scope, const long value);
CFGFUN(smart_borders, const char *enable);
CFGFUN(smart_gaps, const char *enable);
CFGFUN(floating_minimum_size, const long width, const long height);
@@ -69,6 +69,7 @@ CFGFUN(no_focus);
CFGFUN(ipc_socket, const char *path);
CFGFUN(ipc_kill_timeout, const long timeout_ms);
CFGFUN(tiling_drag, const char *value);
+CFGFUN(tiling_drag_swap_modifier, const char *modifiers);
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, const char *child_border);
@@ -78,7 +79,7 @@ CFGFUN(default_border, const char *windowtype, const char *border, const long wi
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 *exclude_titlebar, const char *command);
-CFGFUN(enter_mode, const char *pango_markup, const char *mode);
+CFGFUN(enter_mode, const char *pango_markup, const char *modename);
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command);
CFGFUN(bar_font, const char *font);
@@ -102,9 +103,10 @@ CFGFUN(bar_i3bar_command, const char *i3bar_command);
CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text);
CFGFUN(bar_socket_path, const char *socket_path);
CFGFUN(bar_tray_output, const char *output);
-CFGFUN(bar_tray_padding, const long spacing_px);
+CFGFUN(bar_tray_padding, const long padding_px);
CFGFUN(bar_color_single, const char *colorclass, const char *color);
CFGFUN(bar_status_command, const char *command);
+CFGFUN(bar_workspace_command, const char *command);
CFGFUN(bar_binding_mode_indicator, const char *value);
CFGFUN(bar_workspace_buttons, const char *value);
CFGFUN(bar_workspace_min_width, const long width);
diff --git a/include/config_parser.h b/include/config_parser.h
index 82c57090..9bc924f2 100644
--- a/include/config_parser.h
+++ b/include/config_parser.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* config_parser.h: config parser-related definitions
@@ -13,29 +13,13 @@
#include
+#include "parser_util.h"
+
SLIST_HEAD(variables_head, Variable);
extern pid_t config_error_nagbar_pid;
-struct stack_entry {
- /* Just a pointer, not dynamically allocated. */
- const char *identifier;
- enum {
- STACK_STR = 0,
- STACK_LONG = 1,
- } type;
- union {
- char *str;
- long num;
- } val;
-};
-
-struct stack {
- struct stack_entry stack[10];
-};
-
struct parser_ctx {
bool use_nagbar;
- bool assume_v4;
int state;
Match current_match;
diff --git a/include/configuration.h b/include/configuration.h
index 99f4b64e..c062ae4c 100644
--- a/include/configuration.h
+++ b/include/configuration.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* include/configuration.h: Contains all structs/variables for the configurable
@@ -227,6 +227,9 @@ struct Config {
/** The modifier which needs to be pressed in combination with your mouse
* buttons to do things with floating windows (move, resize) */
uint32_t floating_modifier;
+ /** The modifier which needs to be pressed in combination with the floating
+ * modifier and your mouse buttons to swap containers during tiling drag */
+ uint32_t swap_modifier;
/** Maximum and minimum dimensions of a floating window */
int32_t floating_maximum_width;
@@ -262,6 +265,9 @@ struct Config {
/* just ignore the popup, that is, don’t map it */
PDF_IGNORE = 2,
+
+ /* display all floating windows */
+ PDF_ALL = 3,
} popup_during_fullscreen;
/* The number of currently parsed barconfigs */
@@ -272,9 +278,6 @@ struct Config {
/* Gap sizes */
gaps_t gaps;
- /* Should single containers on a workspace receive a border? */
- smart_borders_t smart_borders;
-
/* Disable gaps if there is only one container on the workspace */
smart_gaps_t smart_gaps;
};
@@ -335,6 +338,10 @@ struct Barconfig {
* Will be passed to the shell. */
char *status_command;
+ /** Command that should be run to get the workspace buttons. Will be passed
+ * to the shell. */
+ char *workspace_command;
+
/** Font specification for all text rendered on the bar. */
char *font;
diff --git a/include/data.h b/include/data.h
index 483aecab..f448d263 100644
--- a/include/data.h
+++ b/include/data.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* include/data.h: This file defines all data structures used by i3
@@ -81,10 +81,6 @@ typedef enum { ADJ_NONE = 0,
ADJ_UPPER_SCREEN_EDGE = (1 << 2),
ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
-typedef enum { SMART_BORDERS_OFF,
- SMART_BORDERS_ON,
- SMART_BORDERS_NO_GAPS } smart_borders_t;
-
typedef enum { SMART_GAPS_OFF,
SMART_GAPS_ON,
SMART_GAPS_INVERSE_OUTER } smart_gaps_t;
diff --git a/include/display_version.h b/include/display_version.h
index 6996038e..9dba20ec 100644
--- a/include/display_version.h
+++ b/include/display_version.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* display_version.c: displays the running i3 version, runs as part of
diff --git a/include/drag.h b/include/drag.h
index 2027f934..93a7c4f0 100644
--- a/include/drag.h
+++ b/include/drag.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* drag.c: click and drag.
diff --git a/include/ewmh.h b/include/ewmh.h
index f616eb84..a4250e72 100644
--- a/include/ewmh.h
+++ b/include/ewmh.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* ewmh.c: Get/set certain EWMH properties easily.
diff --git a/include/fake_outputs.h b/include/fake_outputs.h
index 27a0d41a..25e42fc7 100644
--- a/include/fake_outputs.h
+++ b/include/fake_outputs.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* Faking outputs is useful in pathological situations (like network X servers
diff --git a/include/floating.h b/include/floating.h
index 612874fc..781bb130 100644
--- a/include/floating.h
+++ b/include/floating.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* floating.c: Floating windows.
diff --git a/include/gaps.h b/include/gaps.h
index cb4d0093..50bff3da 100644
--- a/include/gaps.h
+++ b/include/gaps.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/include/handlers.h b/include/handlers.h
index 81012e7b..18416935 100644
--- a/include/handlers.h
+++ b/include/handlers.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* handlers.c: Small handlers for various events (keypresses, focus changes,
diff --git a/include/i3-atoms_NET_SUPPORTED.xmacro.h b/include/i3-atoms_NET_SUPPORTED.xmacro.h
index b491da98..90d03c20 100644
--- a/include/i3-atoms_NET_SUPPORTED.xmacro.h
+++ b/include/i3-atoms_NET_SUPPORTED.xmacro.h
@@ -11,6 +11,8 @@ xmacro(_NET_WM_STATE_DEMANDS_ATTENTION) \
xmacro(_NET_WM_STATE_MODAL) \
xmacro(_NET_WM_STATE_HIDDEN) \
xmacro(_NET_WM_STATE_FOCUSED) \
+xmacro(_NET_WM_STATE_MAXIMIZED_VERT) \
+xmacro(_NET_WM_STATE_MAXIMIZED_HORZ) \
xmacro(_NET_WM_STATE) \
xmacro(_NET_WM_WINDOW_TYPE) \
xmacro(_NET_WM_WINDOW_TYPE_NORMAL) \
diff --git a/include/i3.h b/include/i3.h
index 4e2eb5c0..40a848df 100644
--- a/include/i3.h
+++ b/include/i3.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3.h: global variables that are used all over i3.
@@ -9,23 +9,16 @@
*/
#pragma once
-#include
-
-#include
#include
#include
#include
-#include
-
-#include
#define SN_API_NOT_YET_FROZEN 1
#include
#include "queue.h"
#include "data.h"
-#include "xcb.h"
/** Git commit identifier, from version.c */
extern const char *i3_version;
@@ -75,5 +68,4 @@ extern xcb_colormap_t colormap;
extern bool xkb_supported, shape_supported;
extern xcb_window_t root;
extern struct ev_loop *main_loop;
-extern bool only_check_config;
extern bool force_xinerama;
diff --git a/include/i3/ipc.h b/include/i3/ipc.h
index 187640cd..50fd155c 100644
--- a/include/i3/ipc.h
+++ b/include/i3/ipc.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* This public header defines the different constants and message types to use
diff --git a/include/ipc.h b/include/ipc.h
index e85e6081..de5a6945 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
diff --git a/include/key_press.h b/include/key_press.h
index 8f23854b..a3bd8943 100644
--- a/include/key_press.h
+++ b/include/key_press.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* key_press.c: key press handler
diff --git a/include/libi3.h b/include/libi3.h
index 005167c7..0fba2739 100644
--- a/include/libi3.h
+++ b/include/libi3.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* libi3: contains functions which are used by i3 *and* accompanying tools such
@@ -158,7 +158,7 @@ char *sstrndup(const char *str, size_t size);
* there is no more memory available)
*
*/
-int sasprintf(char **strp, const char *fmt, ...);
+int sasprintf(char **strp, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
/**
* Wrapper around correct write which returns -1 (meaning that
@@ -186,7 +186,7 @@ ssize_t swrite(int fd, const void *buf, size_t count);
* Like strcasecmp but considers the case where either string is NULL.
*
*/
-int strcasecmp_nullable(const char *a, const char *b);
+int strcasecmp_nullable(const char *a, const char *b) __attribute__((pure));
/**
* Build an i3String from an UTF-8 encoded string.
@@ -240,12 +240,10 @@ void i3string_free(i3String *str);
* to prevent accidentally using freed memory.
*
*/
-#define I3STRING_FREE(str) \
- do { \
- if (str != NULL) { \
- i3string_free(str); \
- str = NULL; \
- } \
+#define I3STRING_FREE(str) \
+ do { \
+ i3string_free(str); \
+ str = NULL; \
} while (0)
/**
@@ -506,7 +504,7 @@ void init_dpi(void);
* This function returns the value of the DPI setting.
*
*/
-long get_dpi_value(void);
+long get_dpi_value(void) __attribute__((pure));
/**
* Convert a logical amount of pixels (e.g. 2 pixels on a “standard” 96 DPI
@@ -514,7 +512,7 @@ long get_dpi_value(void);
* screen, e.g. 5 pixels on a 227 DPI MacBook Pro 13" Retina screen.
*
*/
-int logical_px(const int logical);
+int logical_px(int logical) __attribute__((pure));
/**
* This function resolves ~ in pathnames.
@@ -572,6 +570,7 @@ typedef struct surface_t {
/* A classic XCB graphics context. */
xcb_gcontext_t gc;
+ bool owns_gc;
int width;
int height;
@@ -686,3 +685,9 @@ bool is_background_set(xcb_connection_t *conn, xcb_screen_t *screen);
*
*/
bool boolstr(const char *str);
+
+/**
+ * Get depth of visual specified by visualid
+ *
+ */
+uint16_t get_visual_depth(xcb_visualid_t visual_id);
diff --git a/include/load_layout.h b/include/load_layout.h
index 9205800f..fc2d7a2d 100644
--- a/include/load_layout.h
+++ b/include/load_layout.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* load_layout.c: Restore (parts of) the layout, for example after an inplace
diff --git a/include/log.h b/include/log.h
index 3a52c64d..6830584c 100644
--- a/include/log.h
+++ b/include/log.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* log.c: Logging functions.
diff --git a/include/main.h b/include/main.h
index 1f213cce..83645eec 100644
--- a/include/main.h
+++ b/include/main.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* main.c: Initialization, main loop
@@ -10,6 +10,9 @@
#pragma once
#include
+#include
+
+extern locale_t numericC;
/**
* Enable or disable the main X11 event handling function.
diff --git a/include/manage.h b/include/manage.h
index 22fbe527..547914a8 100644
--- a/include/manage.h
+++ b/include/manage.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* manage.c: Initially managing new windows (or existing ones on restart).
diff --git a/include/match.h b/include/match.h
index 043c3a8f..5584dd11 100644
--- a/include/match.h
+++ b/include/match.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* A "match" is a data structure which acts like a mask or expression to match
diff --git a/include/move.h b/include/move.h
index 830488b0..1002ba49 100644
--- a/include/move.h
+++ b/include/move.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* move.c: Moving containers into some direction.
diff --git a/include/output.h b/include/output.h
index a2ad97b0..06331afc 100644
--- a/include/output.h
+++ b/include/output.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* output.c: Output (monitor) related functions.
@@ -31,8 +31,10 @@ Output *get_output_from_string(Output *current_output, const char *output_str);
char *output_primary_name(Output *output);
/**
- * Returns the output for the given con.
- *
+ * Retrieves the output for a given container. Never returns NULL.
+ * There is an assertion that _will_ fail if the container is inside an
+ * internal workspace. Use con_is_internal() if needed before calling this
+ * function.
*/
Output *get_output_for_con(Con *con);
diff --git a/include/parser_util.h b/include/parser_util.h
new file mode 100644
index 00000000..5f7f3d68
--- /dev/null
+++ b/include/parser_util.h
@@ -0,0 +1,59 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved tiling window manager
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * parser_util.h: utility functions for the config and commands parser
+ *
+ */
+#pragma once
+
+struct stack_entry {
+ /* Just a pointer, not dynamically allocated. */
+ const char *identifier;
+ enum {
+ STACK_STR = 0,
+ STACK_LONG = 1,
+ } type;
+ union {
+ char *str;
+ long num;
+ } val;
+};
+
+struct stack {
+ struct stack_entry stack[10];
+};
+
+/**
+ * Pushes a string (identified by 'identifier') on the stack.
+ * If a string with the same identifier is already on the stack, the new
+ * string will be appended, separated by a comma.
+ *
+ */
+void parser_push_string(struct stack *stack, const char *identifier, const char *str);
+
+/**
+ * Pushes a long (identified by 'identifier') on the stack.
+ *
+ */
+void parser_push_long(struct stack *stack, const char *identifier, long num);
+
+/**
+ * Returns the string with the given identifier.
+ *
+ */
+const char *parser_get_string(const struct stack *stack, const char *identifier);
+
+/**
+ * Returns the long with the given identifier.
+ *
+ */
+long parser_get_long(const struct stack *stack, const char *identifier);
+
+/**
+ * Clears the stack.
+ *
+ */
+void parser_clear_stack(struct stack *stack);
diff --git a/include/queue.h b/include/queue.h
index 9b410449..1cff6467 100644
--- a/include/queue.h
+++ b/include/queue.h
@@ -96,7 +96,7 @@
}
#define SLIST_HEAD_INITIALIZER(head) \
- { NULL }
+ {NULL}
#define SLIST_ENTRY(type) \
struct { \
@@ -174,7 +174,7 @@
}
#define LIST_HEAD_INITIALIZER(head) \
- { NULL }
+ {NULL}
#define LIST_ENTRY(type) \
struct { \
@@ -256,7 +256,7 @@
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).sqh_first }
+ {NULL, &(head).sqh_first}
#define SIMPLEQ_ENTRY(type) \
struct { \
@@ -322,7 +322,7 @@
}
#define TAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).tqh_first }
+ {NULL, &(head).tqh_first}
#define TAILQ_ENTRY(type) \
struct { \
@@ -447,9 +447,7 @@
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ \
- CIRCLEQ_END(&head) \
- , CIRCLEQ_END(&head) \
- }
+ CIRCLEQ_END(&head), CIRCLEQ_END(&head)}
#define CIRCLEQ_ENTRY(type) \
struct { \
diff --git a/include/randr.h b/include/randr.h
index 6fd7ea99..71429035 100644
--- a/include/randr.h
+++ b/include/randr.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* For more information on RandR, please see the X.org RandR specification at
diff --git a/include/regex.h b/include/regex.h
index abda3cd7..7f359c0c 100644
--- a/include/regex.h
+++ b/include/regex.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* regex.c: Interface to libPCRE (perl compatible regular expressions).
@@ -35,4 +35,4 @@ void regex_free(struct regex *regex);
* be visible without debug logging.
*
*/
-bool regex_matches(struct regex *regex, const char *input);
+bool regex_matches(const struct regex *regex, const char *input);
diff --git a/include/render.h b/include/render.h
index 03751c01..8d61f01c 100644
--- a/include/render.h
+++ b/include/render.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* render.c: Renders (determines position/sizes) the layout tree, updating the
diff --git a/include/resize.h b/include/resize.h
index 5439fab5..972f9f1d 100644
--- a/include/resize.h
+++ b/include/resize.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* resize.c: Interactive resizing.
diff --git a/include/restore_layout.h b/include/restore_layout.h
index 98b257d9..ae0fc9da 100644
--- a/include/restore_layout.h
+++ b/include/restore_layout.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* restore_layout.c: Everything for restored containers that is not pure state
diff --git a/include/scratchpad.h b/include/scratchpad.h
index b24ffc08..4310d8fe 100644
--- a/include/scratchpad.h
+++ b/include/scratchpad.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* scratchpad.c: Scratchpad functions (TODO: more description)
diff --git a/include/shmlog.h b/include/shmlog.h
index a30852e7..1a4212a6 100644
--- a/include/shmlog.h
+++ b/include/shmlog.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* The format of the shmlog data structure which i3 development versions use by
diff --git a/include/sighandler.h b/include/sighandler.h
index 2cc20cd2..cef74bce 100644
--- a/include/sighandler.h
+++ b/include/sighandler.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/include/startup.h b/include/startup.h
index 0001a77d..a2b52c58 100644
--- a/include/startup.h
+++ b/include/startup.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* startup.c: Startup notification code. Ensures a startup notification context
@@ -56,7 +56,7 @@ void startup_sequence_rename_workspace(const char *old_name, const char *new_nam
* Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
*
*/
-struct Startup_Sequence *startup_sequence_get(i3Window *cwindow,
+struct Startup_Sequence *startup_sequence_get(const i3Window *cwindow,
xcb_get_property_reply_t *startup_id_reply, bool ignore_mapped_leader);
/**
@@ -68,10 +68,10 @@ struct Startup_Sequence *startup_sequence_get(i3Window *cwindow,
* Returns NULL otherwise.
*
*/
-char *startup_workspace_for_window(i3Window *cwindow, xcb_get_property_reply_t *startup_id_reply);
+char *startup_workspace_for_window(const i3Window *cwindow, xcb_get_property_reply_t *startup_id_reply);
/**
* Deletes the startup sequence for a window if it exists.
*
*/
-void startup_sequence_delete_by_window(i3Window *win);
+void startup_sequence_delete_by_window(const i3Window *win);
diff --git a/include/sync.h b/include/sync.h
index e726f99e..b211ff29 100644
--- a/include/sync.h
+++ b/include/sync.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* sync.c: i3 sync protocol: https://i3wm.org/docs/testsuite.html#i3_sync
diff --git a/include/tiling_drag.h b/include/tiling_drag.h
index 3091b734..1c292513 100644
--- a/include/tiling_drag.h
+++ b/include/tiling_drag.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* tiling_drag.h: Reposition tiled windows by dragging.
@@ -9,8 +9,6 @@
*/
#pragma once
-#include "all.h"
-
/**
* Tiling drag initiation modes.
*/
diff --git a/include/tree.h b/include/tree.h
index 0b758d53..e0f8d867 100644
--- a/include/tree.h
+++ b/include/tree.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* tree.c: Everything that primarily modifies the layout tree data structure.
diff --git a/include/util.h b/include/util.h
index 8525b6d9..70fe56b8 100644
--- a/include/util.h
+++ b/include/util.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* util.c: Utility functions, which can be useful everywhere within i3 (see
@@ -71,7 +71,7 @@ Rect rect_sanitize_dimensions(Rect rect);
* Returns true if the name consists of only digits.
*
*/
-__attribute__((pure)) bool name_is_digits(const char *name);
+bool name_is_digits(const char *name) __attribute__((__pure__));
/**
* Set 'out' to the layout_t value for the given layout. The function
@@ -170,28 +170,28 @@ ssize_t slurp(const char *path, char **buf);
* Convert a direction to its corresponding orientation.
*
*/
-orientation_t orientation_from_direction(direction_t direction);
+orientation_t orientation_from_direction(direction_t direction) __attribute__((__const__));
/**
* Convert a direction to its corresponding position.
*
*/
-position_t position_from_direction(direction_t direction);
+position_t position_from_direction(direction_t direction) __attribute__((__const__));
/**
* Convert orientation and position to the corresponding direction.
*
*/
-direction_t direction_from_orientation_position(orientation_t orientation, position_t position);
+direction_t direction_from_orientation_position(orientation_t orientation, position_t position) __attribute__((__const__));
/**
* Converts direction to a string representation.
*
*/
-const char *direction_to_string(direction_t direction);
+const char *direction_to_string(direction_t direction) __attribute__((__const__));
/**
* Converts position to a string representation.
*
*/
-const char *position_to_string(position_t position);
+const char *position_to_string(position_t position) __attribute__((__const__));
diff --git a/include/window.h b/include/window.h
index 7b43ab1f..49ef13ce 100644
--- a/include/window.h
+++ b/include/window.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* window.c: Updates window attributes (X11 hints/properties).
diff --git a/include/workspace.h b/include/workspace.h
index fe6d9f88..0b8304cf 100644
--- a/include/workspace.h
+++ b/include/workspace.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* workspace.c: Modifying workspaces, accessing them, moving containers to
diff --git a/include/x.h b/include/x.h
index d01709ed..8d8039a2 100644
--- a/include/x.h
+++ b/include/x.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* x.c: Interface to X11, transfers our in-memory state to X11 (see also
@@ -101,7 +101,7 @@ void x_push_changes(Con *con);
* next call to x_push_changes() will make the change visible in X11.
*
*/
-void x_raise_con(Con *con);
+void x_raise_con(const Con *con);
/**
* Sets the WM_NAME property (so, no UTF8, but used only for debugging anyways)
diff --git a/include/xcb.h b/include/xcb.h
index ba4ff2f3..d469d8ef 100644
--- a/include/xcb.h
+++ b/include/xcb.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* xcb.c: Helper functions for easier usage of XCB
@@ -31,7 +31,7 @@
/** The XCB_CW_EVENT_MASK for its frame */
#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ \
- XCB_EVENT_MASK_BUTTON_RELEASE | \
+ XCB_EVENT_MASK_BUTTON_RELEASE | \
XCB_EVENT_MASK_POINTER_MOTION | /* …mouse is moved */ \
XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ \
XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* …the frame gets destroyed */ \
@@ -98,12 +98,6 @@ xcb_atom_t xcb_get_preferred_window_type(xcb_get_property_reply_t *reply);
*/
bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom);
-/**
- * Get depth of visual specified by visualid
- *
- */
-uint16_t get_visual_depth(xcb_visualid_t visual_id);
-
/**
* Get visual type specified by visualid
*
diff --git a/include/xcursor.h b/include/xcursor.h
index ad33d506..4c9c304d 100644
--- a/include/xcursor.h
+++ b/include/xcursor.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* xcursor.c: libXcursor support for themed cursors.
diff --git a/include/xinerama.h b/include/xinerama.h
index 52a5db33..e3fe67fc 100644
--- a/include/xinerama.h
+++ b/include/xinerama.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* This is LEGACY code (we support RandR, which can do much more than
diff --git a/include/yajl_utils.h b/include/yajl_utils.h
index 6ab1ff1c..808925b0 100644
--- a/include/yajl_utils.h
+++ b/include/yajl_utils.h
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* yajl_utils.h
diff --git a/libi3/boolstr.c b/libi3/boolstr.c
index 0fa417dd..a97f0af7 100644
--- a/libi3/boolstr.c
+++ b/libi3/boolstr.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/libi3/create_socket.c b/libi3/create_socket.c
index d476f43a..263ef7b3 100644
--- a/libi3/create_socket.c
+++ b/libi3/create_socket.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/libi3/dpi.c b/libi3/dpi.c
index dec38bc8..9841ec35 100644
--- a/libi3/dpi.c
+++ b/libi3/dpi.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -45,7 +45,7 @@ void init_dpi(void) {
char *endptr;
double in_dpi = strtod(resource, &endptr);
- if (in_dpi == HUGE_VAL || dpi < 0 || *endptr != '\0' || endptr == resource) {
+ if (in_dpi == HUGE_VAL || in_dpi < 0 || *endptr != '\0' || endptr == resource) {
ELOG("Xft.dpi = %s is an invalid number and couldn't be parsed.\n", resource);
dpi = 0;
goto init_dpi_end;
@@ -95,7 +95,8 @@ int logical_px(const int logical) {
* systems to 96 dpi in order to get the behavior they expect/are used to,
* but since we can easily detect this case in code, let’s do it for them.
*/
- if ((dpi / 96.0) < 1.25)
+ if ((dpi / 96.0) < 1.25) {
return logical;
+ }
return ceil((dpi / 96.0) * logical);
}
diff --git a/libi3/draw_util.c b/libi3/draw_util.c
index 903e3536..c6555b4a 100644
--- a/libi3/draw_util.c
+++ b/libi3/draw_util.c
@@ -12,7 +12,6 @@
#include
#include
-#include
/* The default visual_type to use if none is specified when creating the surface. Must be defined globally. */
extern xcb_visualtype_t *visual_type;
@@ -28,6 +27,78 @@ static bool surface_initialized(surface_t *surface) {
return true;
}
+/*
+ * Get a GC for the given depth. The given drawable must have this depth.
+ *
+ * Per the X11 protocol manual for "CreateGC":
+ * > The gcontext can be used with any destination drawable having the same root
+ * > and depth as the specified drawable;
+ */
+static xcb_gcontext_t get_gc(xcb_connection_t *conn, uint8_t depth, xcb_drawable_t drawable, bool *should_free) {
+ static struct {
+ uint8_t depth;
+ xcb_gcontext_t gc;
+ } gc_cache[2] = {
+ 0,
+ };
+
+ size_t index = 0;
+ bool cache = false;
+
+ *should_free = false;
+ for (; index < sizeof(gc_cache) / sizeof(gc_cache[0]); index++) {
+ if (gc_cache[index].depth == depth) {
+ return gc_cache[index].gc;
+ }
+ if (gc_cache[index].depth == 0) {
+ cache = true;
+ break;
+ }
+ }
+
+ xcb_gcontext_t gc = xcb_generate_id(conn);
+ /* The drawable is only used to get the root and depth, thus the GC is not
+ * tied to the drawable and it can be re-used with different drawables. */
+ xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(conn, gc, drawable, 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);
+ free(error);
+ return gc;
+ }
+
+ if (cache) {
+ gc_cache[index].depth = depth;
+ gc_cache[index].gc = gc;
+ } else {
+ *should_free = true;
+ }
+
+ return gc;
+}
+
+/*
+ * Get depth of visual specified by visualid
+ *
+ */
+uint16_t get_visual_depth(const xcb_visualid_t visual_id) {
+ xcb_depth_iterator_t depth_iter;
+
+ depth_iter = xcb_screen_allowed_depths_iterator(root_screen);
+ for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
+ xcb_visualtype_iterator_t visual_iter;
+
+ visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
+ for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) {
+ if (visual_id == visual_iter.data->visual_id) {
+ return depth_iter.data->depth;
+ }
+ }
+ }
+ return 0;
+}
+
/*
* Initialize the surface to represent the given drawable.
*
@@ -38,18 +109,11 @@ void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_draw
surface->width = width;
surface->height = height;
- if (visual == NULL)
+ if (visual == NULL) {
visual = visual_type;
-
- 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);
- free(error);
}
+ surface->gc = get_gc(conn, get_visual_depth(visual->visual_id), drawable, &surface->owns_gc);
surface->surface = cairo_xcb_surface_create(conn, surface->id, visual, width, height);
surface->cr = cairo_create(surface->surface);
}
@@ -68,11 +132,9 @@ void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface) {
status, cairo_status_to_string(status));
}
- /* NOTE: This function is also called on uninitialised surface_t instances.
- * The x11 error from xcb_free_gc(conn, XCB_NONE) is silently ignored
- * elsewhere.
- */
- xcb_free_gc(conn, surface->gc);
+ if (surface->owns_gc) {
+ xcb_free_gc(conn, surface->gc);
+ }
cairo_surface_destroy(surface->surface);
cairo_destroy(surface->cr);
diff --git a/libi3/fake_configure_notify.c b/libi3/fake_configure_notify.c
index 5d87c3da..38e1bff5 100644
--- a/libi3/fake_configure_notify.c
+++ b/libi3/fake_configure_notify.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/libi3/font.c b/libi3/font.c
index 10abad05..e2d0e357 100644
--- a/libi3/font.c
+++ b/libi3/font.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -95,11 +95,13 @@ static void draw_text_pango(const char *text, size_t text_len,
pango_layout_set_width(layout, max_width * PANGO_SCALE);
pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
+ pango_layout_set_single_paragraph_mode(layout, true);
- if (pango_markup)
+ if (pango_markup) {
pango_layout_set_markup(layout, text, text_len);
- else
+ } else {
pango_layout_set_text(layout, text, text_len);
+ }
/* Do the drawing */
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
@@ -131,10 +133,11 @@ static int predict_text_width_pango(const char *text, size_t text_len, bool pang
gint width;
pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
- if (pango_markup)
+ if (pango_markup) {
pango_layout_set_markup(layout, text, text_len);
- else
+ } else {
pango_layout_set_text(layout, text, text_len);
+ }
pango_cairo_update_layout(cr, layout);
pango_layout_get_pixel_size(layout, &width, NULL);
@@ -214,10 +217,11 @@ i3Font load_font(const char *pattern, const bool fallback) {
info_cookie = xcb_query_font(conn, font.specific.xcb.id);
free(error);
- if ((error = xcb_request_check(conn, font_cookie)) != NULL)
+ if ((error = xcb_request_check(conn, font_cookie)) != NULL) {
errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks "
"(fixed or -misc-*): X11 error %d",
error->error_code);
+ }
}
}
free(error);
@@ -226,14 +230,16 @@ i3Font load_font(const char *pattern, const bool fallback) {
LOG("Using X font %s\n", pattern);
/* Get information (height/name) for this font */
- if (!(font.specific.xcb.info = xcb_query_font_reply(conn, info_cookie, NULL)))
+ if (!(font.specific.xcb.info = xcb_query_font_reply(conn, info_cookie, NULL))) {
errx(EXIT_FAILURE, "Could not load font \"%s\"", pattern);
+ }
/* Get the font table, if possible */
- if (xcb_query_font_char_infos_length(font.specific.xcb.info) == 0)
+ if (xcb_query_font_char_infos_length(font.specific.xcb.info) == 0) {
font.specific.xcb.table = NULL;
- else
+ } else {
font.specific.xcb.table = xcb_query_font_char_infos(font.specific.xcb.info);
+ }
/* Calculate the font height */
font.height = font.specific.xcb.info->font_ascent + font.specific.xcb.info->font_descent;
@@ -258,8 +264,9 @@ void set_font(i3Font *font) {
*/
void free_font(void) {
/* if there is no saved font, simply return */
- if (savedFont == NULL)
+ if (savedFont == NULL) {
return;
+ }
free(savedFont->pattern);
switch (savedFont->type) {
@@ -340,8 +347,9 @@ static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawabl
text_len -= chunk_size;
/* Check if we're done */
- if (text_len == 0)
+ if (text_len == 0) {
break;
+ }
/* Advance pos_x based on the predicted text width */
x += predict_text_width_xcb(chunk, chunk_size);
@@ -405,8 +413,9 @@ static int xcb_query_text_width(const xcb_char2b_t *text, size_t text_len) {
}
static int predict_text_width_xcb(const xcb_char2b_t *input, size_t text_len) {
- if (text_len == 0)
+ if (text_len == 0) {
return 0;
+ }
int width;
if (savedFont->specific.xcb.table == NULL) {
@@ -427,8 +436,9 @@ static int predict_text_width_xcb(const xcb_char2b_t *input, size_t text_len) {
if (row < font_info->min_byte1 ||
row > font_info->max_byte1 ||
col < font_info->min_char_or_byte2 ||
- col > font_info->max_char_or_byte2)
+ col > font_info->max_char_or_byte2) {
continue;
+ }
/* Don't you ask me, how this one works… (Merovius) */
info = &font_table[((row - font_info->min_byte1) *
diff --git a/libi3/format_placeholders.c b/libi3/format_placeholders.c
index 71870a7b..9542a254 100644
--- a/libi3/format_placeholders.c
+++ b/libi3/format_placeholders.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -19,16 +19,18 @@
*
*/
char *format_placeholders(char *format, placeholder_t *placeholders, int num) {
- if (format == NULL)
+ 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 (!CS_STARTS_WITH(walk, placeholders[i].name))
+ if (!CS_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;
@@ -57,8 +59,9 @@ char *format_placeholders(char *format, placeholder_t *placeholders, int num) {
break;
}
- if (!matched)
+ if (!matched) {
*(outwalk++) = *walk;
+ }
}
*outwalk = '\0';
diff --git a/libi3/get_colorpixel.c b/libi3/get_colorpixel.c
index 45e47725..e147b793 100644
--- a/libi3/get_colorpixel.c
+++ b/libi3/get_colorpixel.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -50,17 +50,18 @@ uint32_t get_colorpixel(const char *hex) {
/* 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 (a << 24) | (r << 16 | g << 8 | b);
+ return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
/* Lookup this colorpixel in the cache */
struct Colorpixel *colorpixel;
SLIST_FOREACH (colorpixel, &(colorpixels), colorpixels) {
- if (strcmp(colorpixel->hex, hex) == 0)
+ if (strcmp(colorpixel->hex, hex) == 0) {
return colorpixel->pixel;
+ }
}
-#define RGB_8_TO_16(i) (65535 * ((i)&0xFF) / 255)
+#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);
diff --git a/libi3/get_config_path.c b/libi3/get_config_path.c
index 1f2b4c05..c401d741 100644
--- a/libi3/get_config_path.c
+++ b/libi3/get_config_path.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/libi3/get_exe_path.c b/libi3/get_exe_path.c
index 3b46ef82..9f489f93 100644
--- a/libi3/get_exe_path.c
+++ b/libi3/get_exe_path.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -83,8 +83,9 @@ char *get_exe_path(const char *argv0) {
const char *component;
char *str = path;
while (1) {
- if ((component = strtok(str, ":")) == NULL)
+ if ((component = strtok(str, ":")) == NULL) {
break;
+ }
str = NULL;
free(destpath);
sasprintf(&destpath, "%s/%s", component, argv0);
diff --git a/libi3/get_mod_mask.c b/libi3/get_mod_mask.c
index 92af456d..f74378f9 100644
--- a/libi3/get_mod_mask.c
+++ b/libi3/get_mod_mask.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -29,8 +29,9 @@ uint32_t aio_get_mod_mask_for(uint32_t keysym, xcb_key_symbols_t *symbols) {
/* Get the current modifier mapping (this is blocking!) */
cookie = xcb_get_modifier_mapping(conn);
- if (!(modmap_r = xcb_get_modifier_mapping_reply(conn, cookie, NULL)))
+ if (!(modmap_r = xcb_get_modifier_mapping_reply(conn, cookie, NULL))) {
return 0;
+ }
uint32_t result = get_mod_mask_for(keysym, symbols, modmap_r);
free(modmap_r);
@@ -53,25 +54,28 @@ uint32_t get_mod_mask_for(uint32_t keysym,
modmap = xcb_get_modifier_mapping_keycodes(modmap_reply);
/* Get the list of keycodes for the given symbol */
- if (!(codes = xcb_key_symbols_get_keycode(symbols, keysym)))
+ if (!(codes = xcb_key_symbols_get_keycode(symbols, keysym))) {
return 0;
+ }
/* Loop through all modifiers (Mod1-Mod5, Shift, Control, Lock) */
- for (int mod = 0; mod < 8; mod++)
+ for (int mod = 0; mod < 8; mod++) {
for (int j = 0; j < modmap_reply->keycodes_per_modifier; j++) {
/* Store the current keycode (for modifier 'mod') */
mod_code = modmap[(mod * modmap_reply->keycodes_per_modifier) + j];
/* Check if that keycode is in the list of previously resolved
* keycodes for our symbol. If so, return the modifier mask. */
for (xcb_keycode_t *code = codes; *code; code++) {
- if (*code != mod_code)
+ if (*code != mod_code) {
continue;
+ }
free(codes);
/* This corresponds to the XCB_MOD_MASK_* constants */
return (1 << mod);
}
}
+ }
return 0;
}
diff --git a/libi3/get_process_filename.c b/libi3/get_process_filename.c
index d29f8db1..0eb2b95b 100644
--- a/libi3/get_process_filename.c
+++ b/libi3/get_process_filename.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/libi3/get_visualtype.c b/libi3/get_visualtype.c
index ccf266db..ce4b2495 100644
--- a/libi3/get_visualtype.c
+++ b/libi3/get_visualtype.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -20,8 +20,9 @@ xcb_visualtype_t *get_visualtype(xcb_screen_t *screen) {
for (visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
visual_iter.rem;
xcb_visualtype_next(&visual_iter)) {
- if (screen->root_visual == visual_iter.data->visual_id)
+ if (screen->root_visual == visual_iter.data->visual_id) {
return visual_iter.data;
+ }
}
}
return NULL;
diff --git a/libi3/ipc_connect.c b/libi3/ipc_connect.c
index 5da9f129..c6b945a6 100644
--- a/libi3/ipc_connect.c
+++ b/libi3/ipc_connect.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -37,7 +37,7 @@ int ipc_connect(const char *socket_path) {
}
if (path == NULL) {
- path = sstrdup("/tmp/i3-ipc.sock");
+ err(EXIT_FAILURE, "Could not determine i3 socket path");
}
int sockfd = ipc_connect_impl(path);
@@ -55,8 +55,9 @@ int ipc_connect(const char *socket_path) {
*/
int ipc_connect_impl(const char *socket_path) {
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
- if (sockfd == -1)
+ if (sockfd == -1) {
err(EXIT_FAILURE, "Could not create socket");
+ }
(void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
diff --git a/libi3/ipc_recv_message.c b/libi3/ipc_recv_message.c
index 516405b0..92a8cc35 100644
--- a/libi3/ipc_recv_message.c
+++ b/libi3/ipc_recv_message.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -36,8 +36,9 @@ int ipc_recv_message(int sockfd, uint32_t *message_type,
uint32_t read_bytes = 0;
while (read_bytes < to_read) {
int n = read(sockfd, msg + read_bytes, to_read - read_bytes);
- if (n == -1)
+ if (n == -1) {
return -1;
+ }
if (n == 0) {
if (read_bytes == 0) {
return -2;
@@ -60,8 +61,9 @@ int ipc_recv_message(int sockfd, uint32_t *message_type,
walk += strlen(I3_IPC_MAGIC);
memcpy(reply_length, walk, sizeof(uint32_t));
walk += sizeof(uint32_t);
- if (message_type != NULL)
+ if (message_type != NULL) {
memcpy(message_type, walk, sizeof(uint32_t));
+ }
*reply = smalloc(*reply_length);
@@ -69,8 +71,9 @@ int ipc_recv_message(int sockfd, uint32_t *message_type,
while (read_bytes < *reply_length) {
const int n = read(sockfd, *reply + read_bytes, *reply_length - read_bytes);
if (n == -1) {
- if (errno == EINTR || errno == EAGAIN)
+ if (errno == EINTR || errno == EAGAIN) {
continue;
+ }
return -1;
}
if (n == 0) {
diff --git a/libi3/ipc_send_message.c b/libi3/ipc_send_message.c
index 4faeea7f..801d250d 100644
--- a/libi3/ipc_send_message.c
+++ b/libi3/ipc_send_message.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -26,11 +26,13 @@ int ipc_send_message(int sockfd, const uint32_t message_size,
.size = message_size,
.type = message_type};
- if (writeall(sockfd, ((void *)&header), sizeof(i3_ipc_header_t)) == -1)
+ if (writeall(sockfd, ((void *)&header), sizeof(i3_ipc_header_t)) == -1) {
return -1;
+ }
- if (writeall(sockfd, payload, message_size) == -1)
+ if (writeall(sockfd, payload, message_size) == -1) {
return -1;
+ }
return 0;
}
diff --git a/libi3/is_background_set.c b/libi3/is_background_set.c
index b3b6e6a6..319e52fc 100644
--- a/libi3/is_background_set.c
+++ b/libi3/is_background_set.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/libi3/is_debug_build.c b/libi3/is_debug_build.c
index 9458f75b..9228b3d2 100644
--- a/libi3/is_debug_build.c
+++ b/libi3/is_debug_build.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/libi3/mkdirp.c b/libi3/mkdirp.c
index d29bb95b..14016fa9 100644
--- a/libi3/mkdirp.c
+++ b/libi3/mkdirp.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -18,8 +18,9 @@
*
*/
int mkdirp(const char *path, mode_t mode) {
- if (mkdir(path, mode) == 0)
+ if (mkdir(path, mode) == 0) {
return 0;
+ }
if (errno == EEXIST) {
struct stat st;
/* Check that the named file actually is a directory. */
@@ -38,8 +39,9 @@ int mkdirp(const char *path, mode_t mode) {
}
char *copy = sstrdup(path);
/* strip trailing slashes, if any */
- while (copy[strlen(copy) - 1] == '/')
+ while (copy[strlen(copy) - 1] == '/') {
copy[strlen(copy) - 1] = '\0';
+ }
char *sep = strrchr(copy, '/');
if (sep == NULL) {
@@ -48,8 +50,9 @@ int mkdirp(const char *path, mode_t mode) {
}
*sep = '\0';
int result = -1;
- if (mkdirp(copy, mode) == 0)
+ if (mkdirp(copy, mode) == 0) {
result = mkdirp(path, mode);
+ }
free(copy);
return result;
diff --git a/libi3/path_exists.c b/libi3/path_exists.c
index 5451c3bd..553b1cb0 100644
--- a/libi3/path_exists.c
+++ b/libi3/path_exists.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/libi3/resolve_tilde.c b/libi3/resolve_tilde.c
index 6dbf132f..c1109dda 100644
--- a/libi3/resolve_tilde.c
+++ b/libi3/resolve_tilde.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -28,9 +28,9 @@ char *resolve_tilde(const char *path) {
int res = glob(head, GLOB_TILDE, NULL, &globbuf);
free(head);
/* no match, or many wildcard matches are bad */
- if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1)
+ if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1) {
result = sstrdup(path);
- else if (res != 0) {
+ } else if (res != 0) {
err(EXIT_FAILURE, "glob() failed");
} else {
head = globbuf.gl_pathv[0];
diff --git a/libi3/root_atom_contents.c b/libi3/root_atom_contents.c
index 6feb31bc..d3d23819 100644
--- a/libi3/root_atom_contents.c
+++ b/libi3/root_atom_contents.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -86,7 +86,8 @@ out:
out_atom:
free(atom_reply);
out_conn:
- if (provided_conn == NULL)
+ if (provided_conn == NULL) {
xcb_disconnect(conn);
+ }
return content;
}
diff --git a/libi3/safewrappers.c b/libi3/safewrappers.c
index 767a0f05..086f7245 100644
--- a/libi3/safewrappers.c
+++ b/libi3/safewrappers.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -20,36 +20,41 @@
*/
void *smalloc(size_t size) {
void *result = malloc(size);
- if (result == NULL)
+ if (result == NULL) {
err(EXIT_FAILURE, "malloc(%zd)", size);
+ }
return result;
}
void *scalloc(size_t num, size_t size) {
void *result = calloc(num, size);
- if (result == NULL)
+ if (result == NULL) {
err(EXIT_FAILURE, "calloc(%zd, %zd)", num, size);
+ }
return result;
}
void *srealloc(void *ptr, size_t size) {
void *result = realloc(ptr, size);
- if (result == NULL && size > 0)
+ if (result == NULL && size > 0) {
err(EXIT_FAILURE, "realloc(%zd)", size);
+ }
return result;
}
char *sstrdup(const char *str) {
char *result = strdup(str);
- if (result == NULL)
+ if (result == NULL) {
err(EXIT_FAILURE, "strdup()");
+ }
return result;
}
char *sstrndup(const char *str, size_t size) {
char *result = strndup(str, size);
- if (result == NULL)
+ if (result == NULL) {
err(EXIT_FAILURE, "strndup()");
+ }
return result;
}
@@ -58,8 +63,9 @@ int sasprintf(char **strp, const char *fmt, ...) {
int result;
va_start(args, fmt);
- if ((result = vasprintf(strp, fmt, args)) == -1)
+ if ((result = vasprintf(strp, fmt, args)) == -1) {
err(EXIT_FAILURE, "asprintf(%s)", fmt);
+ }
va_end(args);
return result;
}
@@ -70,8 +76,9 @@ ssize_t writeall(int fd, const void *buf, size_t count) {
while (written < count) {
const ssize_t n = write(fd, ((char *)buf) + written, count - written);
if (n == -1) {
- if (errno == EINTR || errno == EAGAIN)
+ if (errno == EINTR || errno == EAGAIN) {
continue;
+ }
return n;
}
written += (size_t)n;
@@ -103,10 +110,11 @@ ssize_t swrite(int fd, const void *buf, size_t count) {
ssize_t n;
n = writeall(fd, buf, count);
- if (n == -1)
+ if (n == -1) {
err(EXIT_FAILURE, "Failed to write %d", fd);
- else
+ } else {
return n;
+ }
}
/*
diff --git a/libi3/screenshot_wallpaper.c b/libi3/screenshot_wallpaper.c
index 2c115a9e..28a23852 100644
--- a/libi3/screenshot_wallpaper.c
+++ b/libi3/screenshot_wallpaper.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
diff --git a/libi3/string.c b/libi3/string.c
index da18c550..4d280a87 100644
--- a/libi3/string.c
+++ b/libi3/string.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* string.c: Define an i3String type to automagically handle UTF-8/UCS-2
@@ -113,23 +113,27 @@ i3String *i3string_copy(i3String *str) {
*
*/
void i3string_free(i3String *str) {
- if (str == NULL)
+ if (str == NULL) {
return;
+ }
free(str->utf8);
free(str->ucs2);
free(str);
}
static void i3string_ensure_utf8(i3String *str) {
- if (str->utf8 != NULL)
+ if (str->utf8 != NULL) {
return;
- if ((str->utf8 = convert_ucs2_to_utf8(str->ucs2, str->num_glyphs)) != NULL)
+ }
+ if ((str->utf8 = convert_ucs2_to_utf8(str->ucs2, str->num_glyphs)) != NULL) {
str->num_bytes = strlen(str->utf8);
+ }
}
static void i3string_ensure_ucs2(i3String *str) {
- if (str->ucs2 != NULL)
+ if (str->ucs2 != NULL) {
return;
+ }
str->ucs2 = convert_utf8_to_ucs2(str->utf8, &str->num_glyphs);
}
diff --git a/libi3/strndup.c b/libi3/strndup.c
index 8911732c..dc491b4d 100644
--- a/libi3/strndup.c
+++ b/libi3/strndup.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -20,8 +20,9 @@ char *strndup(const char *str, size_t n) {
size_t len;
char *copy;
- for (len = 0; len < n && str[len]; len++)
+ for (len = 0; len < n && str[len]; len++) {
continue;
+ }
copy = smalloc(len + 1);
memcpy(copy, str, len);
diff --git a/libi3/ucs2_conversion.c b/libi3/ucs2_conversion.c
index c7467239..6eb51f44 100644
--- a/libi3/ucs2_conversion.c
+++ b/libi3/ucs2_conversion.c
@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
- * i3 - an improved dynamic tiling window manager
+ * i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
@@ -32,8 +32,9 @@ char *convert_ucs2_to_utf8(xcb_char2b_t *text, size_t num_glyphs) {
if (utf8_conversion_descriptor == (iconv_t)-1) {
/* Get a new conversion descriptor */
utf8_conversion_descriptor = iconv_open("UTF-8", "UCS-2BE");
- if (utf8_conversion_descriptor == (iconv_t)-1)
+ if (utf8_conversion_descriptor == (iconv_t)-1) {
err(EXIT_FAILURE, "Error opening the conversion context");
+ }
} else {
/* Reset the existing conversion descriptor */
iconv(utf8_conversion_descriptor, NULL, NULL, NULL, NULL);
diff --git a/logo.svg b/logo.svg
index 6f87d0c5..4324e31b 100644
--- a/logo.svg
+++ b/logo.svg
@@ -312,7 +312,7 @@
steckdenis
- Logo for I3, an improved dynamic tiling window manager: http://i3wm.org/
+ Logo for i3 - an improved tiling window manager: http://i3wm.org/
diff --git a/man/i3.man b/man/i3.man
index 1974f0b9..e37aaaec 100644
--- a/man/i3.man
+++ b/man/i3.man
@@ -5,7 +5,7 @@ v4.3, September 2012
== NAME
-i3 - an improved dynamic, tiling window manager
+i3 - an improved tiling window manager
== SYNOPSIS
@@ -317,32 +317,25 @@ xsetroot -solid "#333333"
# Enable core dumps in case something goes wrong
ulimit -c unlimited
-# Start i3 and log to ~/.i3/logfile
-echo "Starting at $(date)" >> ~/.i3/logfile
-exec /usr/bin/i3 -V -d all >> ~/.i3/logfile
+# Start i3 and log to ~/.local/share/i3/log
+mkdir -p ~/.local/share/i3
+echo "Starting at $(date)" >> ~/.local/share/i3/log
+exec /usr/bin/i3 -V -d all >> ~/.local/share/i3/log
-------------------------------------------------------------
== ENVIRONMENT
=== I3SOCK
-This variable overwrites the IPC socket path (placed in
-/tmp/i3-%u.XXXXXX/ipc-socket.%p by default, where %u is replaced with your UNIX
-username, %p is replaced with i3’s PID and XXXXXX is a string of random
-characters from the portable filename character set (see mkdtemp(3))). The IPC
-socket is used by external programs like i3-msg(1) or i3bar(1).
-
-== TODO
-
-There is still lot of work to do. Please check our bugtracker for up-to-date
-information about tasks which are still not finished.
+This variable overwrites the IPC socket path (see userguide for default
+location). The IPC socket is used by external programs like i3-msg(1), i3bar(1)
+or user-made scripts.
== SEE ALSO
You should have a copy of the userguide (featuring nice screenshots/graphics
which is why this is not integrated into this manpage), the debugging guide,
-and the "how to hack" guide. If you are building from source, run:
- +make -C docs+
+and the "how to hack" guide.
You can also access these documents online at https://i3wm.org/
diff --git a/man/i3bar.man b/man/i3bar.man
index 479e10fc..761748f3 100644
--- a/man/i3bar.man
+++ b/man/i3bar.man
@@ -46,9 +46,12 @@ Be verbose.
workspace switching buttons and a statusline generated by i3status(1) or
similar. It is automatically invoked (and configured through) i3.
-i3bar supports colors via a JSON protocol starting from v4.2, see
+i3bar supports using a JSON protocol for setting the status line, see
https://i3wm.org/docs/i3bar-protocol.html
+Since i3 4.23, i3bar supports another JSON protocol for setting workspace
+buttons. See https://i3wm.org/docs/i3bar-workspace-protocol.html.
+
== ENVIRONMENT
=== I3SOCK
diff --git a/meson.build b/meson.build
index b272c271..afff3305 100644
--- a/meson.build
+++ b/meson.build
@@ -6,17 +6,17 @@
project(
'i3',
'c',
- version: '4.22',
+ version: '4.25',
default_options: [
'c_std=c11',
'warning_level=1', # enable all warnings (-Wall)
# TODO(https://github.com/i3/i3/issues/4087): switch to
# 'buildtype=debugoptimized',
],
- # Ubuntu 18.04 (supported until 2023) has meson 0.45.
+ # Ubuntu 20.04 (supported until 2025) has meson 0.53.
# We can revisit our minimum supported meson version
# if it turns out to be too hard to maintain.
- meson_version: '>=0.45.0',
+ meson_version: '>=0.47.0',
)
cc = meson.get_compiler('c')
@@ -86,6 +86,7 @@ if get_option('docs')
'docs/wsbar',
'docs/testsuite',
'docs/i3bar-protocol',
+ 'docs/i3bar-workspace-protocol',
'docs/layout-saving',
]
foreach m : doc_toc_inputs
@@ -125,7 +126,7 @@ if get_option('docs')
endforeach
else
- if run_command('[', '-f', 'docs/hacking-howto.html', ']').returncode() == 0
+ if run_command('[', '-f', 'docs/hacking-howto.html', ']', check: false).returncode() == 0
install_data(
[
'docs/hacking-howto.html',
@@ -135,6 +136,7 @@ else
'docs/wsbar.html',
'docs/testsuite.html',
'docs/i3bar-protocol.html',
+ 'docs/i3bar-workspace-protocol.html',
'docs/layout-saving.html',
'docs/debugging.html',
],
@@ -269,7 +271,7 @@ if get_option('mans')
endforeach
else
- if run_command('[', '-f', 'man/i3.1', ']').returncode() == 0
+ if run_command('[', '-f', 'man/i3.1', ']', check: false).returncode() == 0
install_data(
[
'man/i3.1',
@@ -401,6 +403,7 @@ i3srcs = [
'src/match.c',
'src/move.c',
'src/output.c',
+ 'src/parser_util.c',
'src/randr.c',
'src/regex.c',
'src/render.c',
@@ -683,6 +686,7 @@ executable(
'test.commands_parser',
[
'src/commands_parser.c',
+ 'src/parser_util.c',
command_parser,
],
include_directories: inc,
@@ -695,6 +699,7 @@ executable(
'test.config_parser',
[
'src/config_parser.c',
+ 'src/parser_util.c',
config_parser,
],
include_directories: inc,
diff --git a/meson/meson-dist-script b/meson/meson-dist-script
index 47d9ce36..a86dd90c 100755
--- a/meson/meson-dist-script
+++ b/meson/meson-dist-script
@@ -17,6 +17,7 @@ rm -rf \
docs/slides-2012-03-16/ \
testcases/.gitignore \
travis/ \
+ release-notes/ \
.clang-format \
.editorconfig \
i3bar/.gitignore \
@@ -28,7 +29,7 @@ rm -rf \
mkdir build
cd build
-meson .. -Dprefix=/usr -Ddocs=true -Dmans=true
+meson setup -Dprefix=/usr -Ddocs=true -Dmans=true
ninja
cp *.1 ../man/
cp *.html ../docs/
diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec
index 336a8d42..c7bc3bc1 100644
--- a/parser-specs/commands.spec
+++ b/parser-specs/commands.spec
@@ -1,6 +1,6 @@
# vim:ts=2:sw=2:expandtab
#
-# i3 - an improved dynamic tiling window manager
+# i3 - an improved tiling window manager
# © 2009 Michael Stapelberg and contributors (see also: LICENSE)
#
# parser-specs/commands.spec: Specification file for generate-command-parser.pl
@@ -186,6 +186,7 @@ state WORKSPACE_NUMBER:
# focus output