mirror of
https://github.com/i3/i3.git
synced 2026-02-04 10:15:35 +00:00
Merge branch 'next' into stable
This commit is contained in:
@ -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 ]
|
||||
|
||||
72
.github/ISSUE_TEMPLATE/bug_report.md
vendored
72
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,72 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
---
|
||||
|
||||
<!--
|
||||
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
|
||||
-->
|
||||
|
||||
## I'm submitting a…
|
||||
<!-- Please check one of the following options with "x" -->
|
||||
<pre>
|
||||
[x] Bug
|
||||
[ ] Feature Request
|
||||
[ ] Documentation Request
|
||||
[ ] Other (Please describe in detail)
|
||||
</pre>
|
||||
|
||||
## Current Behavior
|
||||
<!--
|
||||
Describe the current behavior,
|
||||
e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
|
||||
-->
|
||||
|
||||
## Expected Behavior
|
||||
<!--
|
||||
Describe the desired behavior you expect after mitigation of the issue,
|
||||
e.g., »The window left next to the current window should be focused.«
|
||||
-->
|
||||
|
||||
## Reproduction Instructions
|
||||
<!--
|
||||
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«
|
||||
-->
|
||||
|
||||
## Environment
|
||||
<!--
|
||||
Please include your exact i3 version.
|
||||
Note that we only support the latest major release and the current development version. If you are using an older version of i3, please first update to the current release version and reproduce the issue there.
|
||||
-->
|
||||
Output of `i3 --moreversion 2>&-`:
|
||||
<pre>
|
||||
i3 version:
|
||||
</pre>
|
||||
|
||||
<!--
|
||||
Please include your (complete) i3 config with which the issue occurs. You can either paste the file directly or provide a link to a service such as pastebin.
|
||||
|
||||
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.
|
||||
-->
|
||||
<details><summary>Config file</summary><pre>
|
||||
</pre>
|
||||
</details>
|
||||
|
||||
<!--
|
||||
Providing 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.
|
||||
-->
|
||||
<pre>
|
||||
Logfile URL:
|
||||
</pre>
|
||||
|
||||
<!--
|
||||
Please also answer the questions below to help us process your issue faster. If you have any other information to share, please add it here as well.
|
||||
-->
|
||||
<pre>
|
||||
- Linux Distribution & Version:
|
||||
- Are you using a compositor (e.g., xcompmgr or compton):
|
||||
</pre>
|
||||
107
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Executable file
107
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Executable file
@ -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
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -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
|
||||
|
||||
56
.github/ISSUE_TEMPLATE/feature_request.md
vendored
56
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,56 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
---
|
||||
|
||||
<!--
|
||||
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
|
||||
-->
|
||||
|
||||
## I'm submitting a…
|
||||
<!-- Check one of the following options with "x" -->
|
||||
<pre>
|
||||
[ ] Bug
|
||||
[x] Feature Request
|
||||
[ ] Documentation Request
|
||||
[ ] Other (Please describe in detail)
|
||||
</pre>
|
||||
|
||||
## Current Behavior
|
||||
<!--
|
||||
Describe the current behavior,
|
||||
e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
|
||||
-->
|
||||
|
||||
## Desired Behavior
|
||||
<!--
|
||||
Describe the desired behavior you expect after mitigation of the issue,
|
||||
e.g., »The window left next to the current window should be focused.«
|
||||
-->
|
||||
|
||||
## Impact
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<pre>
|
||||
[ ] This feature requires new configuration and/or commands
|
||||
</pre>
|
||||
|
||||
## Environment
|
||||
<!--
|
||||
Please include your exact i3 version.
|
||||
Note that we only support the latest major release and the current development version. If you are using an older version of i3, please first update to the current release version and reproduce the issue there.
|
||||
-->
|
||||
Output of `i3 --moreversion 2>&-`:
|
||||
<pre>
|
||||
i3 version:
|
||||
</pre>
|
||||
|
||||
<!--
|
||||
Please also answer the questions below to help us process your issue faster. If you have any other information to share, please add it here as well.
|
||||
-->
|
||||
<pre>
|
||||
- Linux Distribution & Version:
|
||||
- Are you using a compositor (e.g., xcompmgr or compton):
|
||||
</pre>
|
||||
57
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Executable file
57
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Executable file
@ -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
|
||||
60
.github/workflows/main.yml
vendored
60
.github/workflows/main.yml
vendored
@ -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
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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};
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!perl -T
|
||||
#!perl
|
||||
|
||||
use Test::More tests => 1;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!perl -T
|
||||
#!perl
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
|
||||
use Test::More tests => 3;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!perl -T
|
||||
#!perl
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
|
||||
use Test::More tests => 3;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!perl -T
|
||||
#!perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!perl -T
|
||||
#!perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!perl -T
|
||||
#!perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
49
RELEASE-NOTES-4.25
Normal file
49
RELEASE-NOTES-4.25
Normal file
@ -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
|
||||
@ -319,7 +319,7 @@
|
||||
<dc:title>steckdenis</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:description>Logo for I3, an improved dynamic tiling window manager: http://i3.zekjur.net/</dc:description>
|
||||
<dc:description>Logo for i3 - an improved tiling window manager: http://i3.zekjur.net/</dc:description>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
|
||||
</cc:Work>
|
||||
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
@ -329,7 +329,7 @@
|
||||
<dc:title>steckdenis</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:description>Logo for I3, an improved dynamic tiling window manager: http://i3.zekjur.net/</dc:description>
|
||||
<dc:description>Logo for i3 - an improved tiling window manager: http://i3.zekjur.net/</dc:description>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
|
||||
</cc:Work>
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
14
debian/changelog
vendored
14
debian/changelog
vendored
@ -1,8 +1,20 @@
|
||||
i3-wm (4.24-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Wed, 06 Nov 2024 18:34:06 +0100
|
||||
|
||||
i3-wm (4.23-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Sun, 29 Oct 2023 15:42:11 +0100
|
||||
|
||||
i3-wm (4.22-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Mon, 02 Jan 2023 09:34:02 +0100
|
||||
-- Michael Stapelberg <stapelberg@debian.org> Mon, 02 Jan 2023 09:46:22 +0100
|
||||
|
||||
i3-wm (4.21.2-1) unstable; urgency=medium
|
||||
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@ -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
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ you find necessary, please do not hesitate to contact me.
|
||||
<p>
|
||||
++++
|
||||
This document is not 100% up to date. Specifically, everything up to and
|
||||
including <<data_structures>> has been updated recently. The rest might contain
|
||||
including <<startup>> has been updated recently. The rest might contain
|
||||
outdated information.
|
||||
++++
|
||||
</p>
|
||||
@ -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
|
||||
|
||||
184
docs/i3bar-workspace-protocol
Normal file
184
docs/i3bar-workspace-protocol
Normal file
@ -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" ]'
|
||||
------------------------------
|
||||
38
docs/ipc
38
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
|
||||
|
||||
142
docs/testsuite
142
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
|
||||
|
||||
133
docs/userguide
133
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 <<floating_modifier>> and dragging the container while holding the
|
||||
left-click button.
|
||||
left-click button. See the <<config_tiling_drag>> 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 <<move_to_mark>>.
|
||||
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 <<swapping_containers>>.
|
||||
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 <<tiling_drag>>).
|
||||
|
||||
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 <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 <<status_command>> for the statusline option and
|
||||
https://i3wm.org/docs/i3bar-workspace-protocol.html for the detailed protocol.
|
||||
|
||||
*Syntax*:
|
||||
------------------------
|
||||
workspace_command <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:
|
||||
<criteria>::
|
||||
Sets focus to the container that matches the specified criteria.
|
||||
See <<command_criteria>>.
|
||||
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*:
|
||||
----------------------------------------------
|
||||
<criteria> focus
|
||||
<criteria> 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|<output1> [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
|
||||
|
||||
@ -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 <number>s,
|
||||
# literal numbers or state IDs and %s for NULL, <string>s and literal
|
||||
|
||||
@ -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, "<Alt>", white, black);
|
||||
else
|
||||
} else {
|
||||
txt(5, 4, "<Win>", white, black);
|
||||
}
|
||||
|
||||
/* the selected modifier */
|
||||
set_font(&bold_font);
|
||||
if (modifier == MOD_Mod4)
|
||||
if (modifier == MOD_Mod4) {
|
||||
txt(2, 4, "-> <Win>", white, black);
|
||||
else
|
||||
} else {
|
||||
txt(2, 5, "-> <Alt>", white, black);
|
||||
}
|
||||
|
||||
set_font(&font);
|
||||
txt(4, 9, "<Enter>", 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);
|
||||
|
||||
|
||||
@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
1
i3bar/.gitignore
vendored
1
i3bar/.gitignore
vendored
@ -1,4 +1,3 @@
|
||||
i3bar
|
||||
*.o
|
||||
core
|
||||
doc/i3bar.1
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <ev.h>
|
||||
|
||||
#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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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,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 */
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
//#include "outputs.h"
|
||||
|
||||
#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
|
||||
#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
|
||||
|
||||
@ -8,8 +8,10 @@
|
||||
*
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "queue.h"
|
||||
#include "yajl_utils.h"
|
||||
|
||||
#include <ctype.h> /* isspace */
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <ev.h>
|
||||
@ -20,6 +22,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -27,14 +30,30 @@
|
||||
#include <yajl/yajl_parse.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
101
i3bar/src/ipc.c
101
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\" ]");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
346
i3bar/src/xcb.c
346
i3bar/src/xcb.c
@ -12,6 +12,7 @@
|
||||
#include <err.h>
|
||||
#include <ev.h>
|
||||
#include <i3/ipc.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 <sys/types.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xkb.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
|
||||
#include "libi3.h"
|
||||
#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)
|
||||
*
|
||||
* assignments.c: Assignments for specific windows (for_window).
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 <path>'.
|
||||
*
|
||||
*/
|
||||
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 <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] <name>'
|
||||
*
|
||||
*/
|
||||
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] <mark>'
|
||||
@ -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]'.
|
||||
|
||||
@ -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 <config.h>
|
||||
|
||||
#include <yajl/yajl_gen.h>
|
||||
#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;
|
||||
|
||||
/**
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 <yajl/yajl_gen.h>
|
||||
|
||||
#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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)
|
||||
*
|
||||
*/
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) \
|
||||
|
||||
10
include/i3.h
10
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 <config.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <xcb/shape.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xkb.h>
|
||||
|
||||
#include <X11/XKBlib.h>
|
||||
|
||||
#define SN_API_NOT_YET_FROZEN 1
|
||||
#include <libsn/sn-launcher.h>
|
||||
|
||||
#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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 <config.h>
|
||||
#include <locale.h>
|
||||
|
||||
extern locale_t numericC;
|
||||
|
||||
/**
|
||||
* Enable or disable the main X11 event handling function.
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
59
include/parser_util.h
Normal file
59
include/parser_util.h
Normal file
@ -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);
|
||||
@ -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 { \
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
*
|
||||
*/
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user