Merge branch 'next' into master

next
Michael Stapelberg 2018-11-04 14:47:46 +01:00
commit 1847938d4e
169 changed files with 4806 additions and 2148 deletions

View File

@ -1,18 +1,68 @@
Output of `i3 --moreversion 2>&- || i3 --version`:
<!--
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
-->
_REPLACE: i3 version output_
## I'm submitting a…
<!-- Please check one of the following options with "x" -->
<pre>
[ ] Bug
[ ] Feature Request
[ ] Documentation Request
[ ] Other (Please describe in detail)
</pre>
URL to a logfile as per https://i3wm.org/docs/debugging.html:
## Current Behavior
<!--
Describe the current behavior,
e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
-->
_REPLACE: URL to logfile_
## 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.«
-->
**What I did:**
## Reproduction Instructions
<!--
For bug reports, please provide detailed instructions on how the bug can be reproduced.
For feature requests you can remove this section.
_REPLACE: e.g. "Im pressing Alt+j (focus left)"_
E.g., »Open three windows in a V[A H[B C]] layout on a new workspace«
-->
**What I saw:**
## 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>
_REPLACE: e.g. "i3 changed focus to the window ABOVE the current window"_
<!--
For bug reports, 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.
**What I expected instead:**
_REPLACE: e.g. "Focus should be on the window to the left"_
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.
-->
<pre>
</pre>
<!--
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>

71
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,71 @@
---
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.
-->
<pre>
</pre>
<!--
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>

View File

@ -0,0 +1,47 @@
---
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…
<!-- Please 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.«
-->
## 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>

View File

@ -35,7 +35,7 @@ install:
script:
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-safe-wrappers.sh
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-formatting.sh
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} /bin/sh -c 'autoreconf -fi && mkdir -p build && cd build && (../configure || (cat config.log; false)) && make -j CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror"'
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} /bin/sh -c 'autoreconf -fi && mkdir -p build && cd build && (../configure || (cat config.log; false)) && make -j CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Werror"'
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-spelling.pl
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} ./travis/run-tests.sh
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/debian-build.sh deb/debian-amd64/DIST

View File

@ -100,11 +100,12 @@ use constant TYPE_GET_VERSION => 7;
use constant TYPE_GET_BINDING_MODES => 8;
use constant TYPE_GET_CONFIG => 9;
use constant TYPE_SEND_TICK => 10;
use constant TYPE_SYNC => 11;
our %EXPORT_TAGS = ( 'all' => [
qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS
TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION
TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK)
TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK TYPE_SYNC)
] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
@ -534,6 +535,19 @@ sub send_tick {
$self->message(TYPE_SEND_TICK, $payload);
}
=head2 sync
Sends an i3 sync event. Requires i3 >= 4.16
=cut
sub sync {
my ($self, $payload) = @_;
$self->_ensure_connection;
$self->message(TYPE_SYNC, $payload);
}
=head2 command($content)
Makes i3 execute the given command

46
DEPENDS
View File

@ -4,29 +4,29 @@
"min" means minimum required version
"lkgv" means last known good version
┌──────────────┬────────┬────────┬───────────────────────────────────────────────────────────┐
│ dependency │ min. │ lkgv │ URL │
├──────────────┼────────┼────────┼───────────────────────────────────────────────────────────┤
│ pkg-config │ 0.25 │ 0.29 │ https://pkgconfig.freedesktop.org/ │
│ libxcb │ 1.1.93 │ 1.12 │ https://xcb.freedesktop.org/dist/ │
│ xcb-util │ 0.3.3 │ 0.4.1 │ https://xcb.freedesktop.org/dist/ │
│ xkbcommon │ 0.4.0 │ 0.6.1 │ https://xkbcommon.org/ │
│ xkbcommon-x11│ 0.4.0 │ 0.6.1 │ https://xkbcommon.org/ │
│ util-cursor³⁴│ 0.0.99 │ 0.1.3 │ https://xcb.freedesktop.org/dist/ │
│ util-wm⁴ │ 0.3.8 │ 0.3.8 │ https://xcb.freedesktop.org/dist/ │
│ util-keysyms⁴│ 0.3.8 │ 0.4.0 │ https://xcb.freedesktop.org/dist/ │
│ util-xrm⁴ │ 1.0.0 │ 1.0.0 │ https://github.com/Airblader/xcb-util-xrm │
│ libev │ 4.0 │ 4.19 │ http://libev.schmorp.de/ │
│ yajl │ 2.0.1 │ 2.1.0 │ https://lloyd.github.com/yajl/ │
│ asciidoc │ 8.3.0 │ 8.6.9 │ http://www.methods.co.nz/asciidoc/ │
│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │
│ Pod::Simple² │ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/
│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │
│ PCRE │ 8.12 │ 8.38 │ https://www.pcre.org/ │
│ libsn¹ │ 0.10 │ 0.12 │ https://freedesktop.org/wiki/Software/startup-notification │
│ pango │ 1.30.0 | 1.40.1 │ http://www.pango.org/
│ cairo │ 1.14.4 │ 1.14.6 │ https://cairographics.org/ │
└──────────────┴────────┴────────┴───────────────────────────────────────────────────────────┘
┌──────────────┬────────┬────────┬─────────────────────────────────────────────────────────────
│ dependency │ min. │ lkgv │ URL
├──────────────┼────────┼────────┼─────────────────────────────────────────────────────────────
│ pkg-config │ 0.25 │ 0.29 │ https://pkgconfig.freedesktop.org/
│ libxcb │ 1.1.93 │ 1.12 │ https://xcb.freedesktop.org/dist/
│ xcb-util │ 0.3.3 │ 0.4.1 │ https://xcb.freedesktop.org/dist/
│ xkbcommon │ 0.4.0 │ 0.6.1 │ https://xkbcommon.org/
│ xkbcommon-x11│ 0.4.0 │ 0.6.1 │ https://xkbcommon.org/
│ util-cursor³⁴│ 0.0.99 │ 0.1.3 │ https://xcb.freedesktop.org/dist/
│ util-wm⁴ │ 0.3.8 │ 0.3.8 │ https://xcb.freedesktop.org/dist/
│ util-keysyms⁴│ 0.3.8 │ 0.4.0 │ https://xcb.freedesktop.org/dist/
│ util-xrm⁴ │ 1.0.0 │ 1.0.0 │ https://github.com/Airblader/xcb-util-xrm/
│ libev │ 4.0 │ 4.19 │ http://libev.schmorp.de/
│ yajl │ 2.0.1 │ 2.1.0 │ https://lloyd.github.com/yajl/
│ asciidoc │ 8.3.0 │ 8.6.9 │ http://www.methods.co.nz/asciidoc/
│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/
│ Pod::Simple² │ 3.22 │ 3.22 │ http://search.cpan.org/dist/Pod-Simple/
│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/
│ PCRE │ 8.12 │ 8.38 │ https://www.pcre.org/
│ libsn¹ │ 0.10 │ 0.12 │ https://freedesktop.org/wiki/Software/startup-notification/
│ pango │ 1.30.0 │ 1.40.1 │ http://www.pango.org/
│ cairo │ 1.14.4 │ 1.14.6 │ https://cairographics.org/
└──────────────┴────────┴────────┴─────────────────────────────────────────────────────────────
¹ libsn = libstartup-notification
² Pod::Simple is a Perl module required for converting the testsuite
documentation to HTML. See https://michael.stapelberg.de/cpan/#Pod::Simple

View File

@ -1 +1 @@
4.15-non-git
4.16-non-git

View File

@ -118,7 +118,7 @@ EXTRA_DIST = \
I3_VERSION \
LICENSE \
PACKAGE-MAINTAINER \
RELEASE-NOTES-4.15 \
RELEASE-NOTES-4.16 \
generate-command-parser.pl \
parser-specs/commands.spec \
parser-specs/config.spec \
@ -301,6 +301,7 @@ libi3_a_SOURCES = \
libi3/fake_configure_notify.c \
libi3/font.c \
libi3/format_placeholders.c \
libi3/g_utf8_make_valid.c \
libi3/get_colorpixel.c \
libi3/get_config_path.c \
libi3/get_exe_path.c \
@ -357,10 +358,12 @@ i3_msg_i3_msg_SOURCES = \
i3_nagbar_i3_nagbar_CFLAGS = \
$(AM_CFLAGS) \
$(LIBSN_CFLAGS) \
$(libi3_CFLAGS)
i3_nagbar_i3_nagbar_LDADD = \
$(libi3_LIBS) \
$(LIBSN_LIBS) \
$(XCB_UTIL_CURSOR_LIBS)
i3_nagbar_i3_nagbar_SOURCES = \
@ -414,10 +417,12 @@ i3bar_i3bar_SOURCES = \
i3_config_wizard_i3_config_wizard_CFLAGS = \
$(AM_CFLAGS) \
$(libi3_CFLAGS) \
$(LIBSN_CFLAGS) \
$(XKBCOMMON_CFLAGS)
i3_config_wizard_i3_config_wizard_LDADD = \
$(libi3_LIBS) \
$(LIBSN_LIBS) \
$(XCB_UTIL_KEYSYMS_LIBS) \
$(XKBCOMMON_LIBS)
@ -521,6 +526,7 @@ i3_SOURCES = \
include/shmlog.h \
include/sighandler.h \
include/startup.h \
include/sync.h \
include/tree.h \
include/util.h \
include/window.h \
@ -562,6 +568,7 @@ i3_SOURCES = \
src/sd-daemon.c \
src/sighandler.c \
src/startup.c \
src/sync.c \
src/tree.c \
src/util.c \
src/version.c \

View File

@ -2,8 +2,8 @@
=====================================================
[![Build Status](https://travis-ci.org/i3/i3.svg?branch=next)](https://travis-ci.org/i3/i3)
[![Issue Stats](http://www.issuestats.com/github/i3/i3/badge/issue?style=flat)](http://www.issuestats.com/github/i3/i3)
[![Pull Request Stats](http://www.issuestats.com/github/i3/i3/badge/pr?style=flat)](http://www.issuestats.com/github/i3/i3)
[![Issue Stats](https://img.shields.io/github/issues/i3/i3.svg)](https://github.com/i3/i3/issues)
[![Pull Request Stats](https://img.shields.io/github/issues-pr/i3/i3.svg)](https://github.com/i3/i3/pulls)
i3 is a tiling window manager for X11.

View File

@ -1,113 +0,0 @@
┌────────────────────────────┐
│ Release notes for i3 v4.15 │
└────────────────────────────┘
This is i3 v4.15. This version is considered stable. All users of i3 are
strongly encouraged to upgrade.
Aside from a number of fixes and documentation improvements, a number of
commands have been extended to be more complete (e.g. “assign”, “resize”).
┌────────────────────────────┐
│ Changes in i3 v4.15 │
└────────────────────────────┘
• build: AnyEvent::I3 moved to the i3 repository, so that its main consumer,
the i3 testsuite, can use new features immediately (such as the tick event,
in this case).
• docs/hacking-howto: promote “using git / sending patches” and “how to
build?” sections
• docs/i3bar-protocol: document that pango markup only works with pango fonts
• docs/ipc: document focus, nodes, floating_nodes
• docs/ipc: urgent: complete the list of container types
• docs/ipc: document how to detect i3s byte order in memory-safe languages
• docs/ipc: document the GET_CONFIG request
• docs/userguide: fix formatting issue
• docs/userguide: explain why Mod4 is usually preferred as a modifier
• docs/userguide: use more idiomatic english (full-size, so-called)
• docs/userguide: switch from removed goto command to focus
• docs/userguide: mention <criteria> in focus
• docs/userguide: remove outdated 2013 last-modified date
• dump-asy: add prerequisite checks
• dump-asy: fix warnings about empty container names
• i3-dump-log: enable shmlog on demand
• i3-sensible-terminal: add “kitty”, “guake”, “tilda”
• i3-sensible-editor: add “gvim”
• i3bar: add --release flag for bindsym in bar blocks
• i3bar: add relative coordinates in JSON for click events
• ipc: rename COMMAND to RUN_COMMAND for consistency
• ipc: implement tick event for less flaky tests
• ipc: add error reply to “focus <window_mode>”
• ipc: send success response for nop
• default config: add $mod+r to toggle resize mode
• default config: use variables for workspace names to avoid repetition
• introduce “assign <criteria> [→] [workspace] [number] <workspace>”
• introduce “assign <criteria> [→] output left|right|up|down|primary|<output>”
• introduce a “focus_wrapping” option (subsumes “force_focus_wrapping”)
• introduce percentage point resizing for floating containers:
“resize set <width> [px | ppt] <height> [px | ppt]”
• introduce “resize set <width> ppt <height> ppt” for tiling windows
• rename “new_window” and “new_float” to “default_border” and
“default_floating_border” (the old names keep working)
• output names (e.g. “DP2”) can now be used as synonyms for monitor names
(e.g. “Dell UP2414Q”).
• the “swap” command now works with fullscreen windows
• raise floating windows to top when they are focused programmatically
• _NET_ACTIVE_WINDOW: invalidate focus to force SetInputFocus call
• make focus handling consistent when changing focus between outputs
• round non-integer Xft.dpi values
• tiling resize: remove minimum size
┌────────────────────────────┐
│ Bugfixes │
└────────────────────────────┘
• i3bar: fix various memory leaks
• i3bar: fix crash when no status_command is provided
• fix uninitialized variables in init_dpi_end, tree_restore
• fix incorrectly set up signal handling
• fix “swap” debug log message
• fix crash when specifying invalid con_id for “swap”
• fix crash upon restart with window marks
• fix crash when config file does not end in a newline
• fix crash in append_layout
• fix crash in layout toggle command
• fix crash when switching monitors
• fix use-after-free in randr_init error path
• fix move accidentally moving windows across outputs
• fix crash when floating window is tiled while being resized
• fix out-of-bounds memory read
• fix memory leak when config conversion fails
• fix layout toggle split, which didnt work until enabling tabbed/stack mode
once
• move XCB event handling into xcb_prepare_cb
• avert endless loop on unexpected EOF in ipc messages
• perform proper cleanup for signals with Term action
• dont match containers in the scratchpad with criteria
• fix “workspace show” related issues
• fix config file conversion with long variable names
• fix config file conversion memory initialization
• prevent access of freed workspace in _workspace_show
• disable fullscreen when required when programmatically focusing windows
• free last_motion_notify
• dont raise floating windows when focused because of focus_follows_mouse
• correctly set EWMH atoms when closing a workspace
• dont raise floating windows when workspace is shown
• keep focus order when encapsulating workspaces
• validate layout files before loading
┌────────────────────────────┐
│ Thanks! │
└────────────────────────────┘
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
Alex Lu, Ben Creasy, Bennett Piater, Cast, chressie, clonejo, Dan Elkouby,
Daniel Mueller, DebianWall, Diki Ananta, Edward Betts, hwangcc23, Ingo Bürk,
Jan Alexander Steffens, Johannes Lange, Kent Fredric, livanh, Martin
T. H. Sandsmark, Michael Siegel, Orestis Floros, Pallav Agarwal, Pawel
S. Veselov, Pietro Cerutti, Theo Buehler, Thomas Praxl, Tyler Brazier,
Vladimir Panteleev, walker0643, Wes Roberts, xzfc
-- Michael Stapelberg, 2018-03-10

145
RELEASE-NOTES-4.16 Normal file
View File

@ -0,0 +1,145 @@
┌────────────────────────────┐
│ Release notes for i3 v4.16 │
└────────────────────────────┘
This is i3 v4.16. This version is considered stable. All users of i3 are
strongly encouraged to upgrade.
This release contains a number of assorted fixes and improvements across pretty
much all individual components of i3.
┌────────────────────────────┐
│ Changes in i3 v4.16 │
└────────────────────────────┘
• build: add conditionals for building docs/mans
• docs/i3bar-protocol: mention skipping blocks with empty full_text
• docs/ipc: add window_properties to tree node
• docs/layout-saving: clarify JSON non-compliance
• docs/userguide: clarify X resource value format
• docs/userguide: fix move_to_outputs link
• docs/userguide: link workspace_auto_back_and_forth from workspace
command
• docs/userguide: mention known issues for assign
• docs/userguide: use anchor for list_of_commands
• docs/userguide: add the default keybinding for focus parent
• man/*: fix title markers (for asciidoctor)
• man/i3-msg.man: add get_config and send_tick
• ipc: kill misbehaving subscribed clients instead of hanging
• ipc: introduce the sync IPC command for synchronization with i3bar
• ipc: scratchpad show now returns correct success
• ipc: send_tick now sets the already-documented “first” field
• i3bar-protocol: add modifiers to events sent by i3bar
• dump-asy: use Pod::Usage for --help and perldoc
• dump-asy: introduce -gv flag to disable opening ghostview
• dump-asy: introduce -save flag to store the rendered tree in a file
• dump-asy: add marks
• dump-asy: include floating containers
• i3bar: add --verbose flag
• i3bar: make modifier accept combinations (like floating_modifier)
• i3-config-wizard: add --modifier flag to allow for headless config
• i3-config-wizard: support startup notifications
• i3-msg: only print input + error position if they are set
• i3-msg: check replies also in quiet mode (-q)
• i3-msg: add support for the SUBSCRIBE message type
• i3-nagbar: support startup notifications
• i3-nagbar: add option for button that runs commands without a terminal
• i3-save-tree: exclude unsupported transient_for property
• i3-sensible-terminal: add alacritty
• i3-sensible-terminal: add hyper
• introduce strip_workspace_name alongside strip_workspace_numbers
• introduce title_align config directive
• “border toggle” now accepts an optional pixel argument
• “resize set” now interprets 0 as “no change”
• “resize set” now accepts the “width” and “height” keywords
• “resize” with pixel values now works for tiling containers
• the optional “absolute” method is now silently ignored in “move position”
commands, where it did not cause a visible difference anyway
• the _NET_WM_STATE_FOCUSED atom is now supported, resulting in e.g.
GTK applications displaying the correct window decoration
• moving fullscreen containers now moves them across outputs
• floating windows can now be used with a geometry of e.g. +1+1, i.e.
their top-left corner can be outside any output as long as the window
is contained partially by one
• prefer floating fullscreen containers when switching focus
• moving containers to an active workspace no longer changes focus
• the rename workspace command no longer confuses directions (e.g. “left”)
with output names
• prefer $XDG_CONFIG_HOME/i3/config over ~/.i3/config
• allow multiple assignments of workspaces to output
• respect maximum size in WM_NORMAL_HINTS
• reject requests for WM_STATE_ICONIC, which avoids e.g. wine
applications being stuck in paused state
• a number of code refactorings and cleanups, some of which tool-assisted
┌────────────────────────────┐
│ Bugfixes │
└────────────────────────────┘
• build: fix static linking
• i3bar: fix various memory leaks
• i3bar: fix crash when no status_command is provided
• i3bar: fix chopping the first character on the very left when using the
full width of the output
• i3bar: fix relative_x and width properties of click events
• i3bar: fix the tray disappearing in some cases when using "tray_output"
• fix various memory leaks and memory correctness issues
• refocus focused window on FOCUS_IN events for the root window. This
fixes incorrect behavior with steam and some tk apps
• fix focus bugs when moving unfocused containers
• fix incorrect urgent window state edge case
• moving an unfocused container from inside a split container to another
workspace doesnt focus siblings
• toggling and killing floating windows now maintains focus order
• dont incorrectly focus siblings when scrolling on window decorations
• fix crash when moving a container to a marked workspace
• fix swap when first is behind a fullscreen window
• fix crash when renaming an existing workspace to a name assigned to the
focused output
• reframe swallowed windows if depth doesnt match
• use detectable autorepeat so that --release bindings are run only when
the key is actually released (and not when it is repeated)
• fix border artifacts when moving windows
• correctly handle bindings for the same mod key with and without --release
• reset B_UPON_KEYRELEASE_IGNORE_MODS bindings when switching modes
• fix height offset calculation in pango text drawing
• fix detection of libiconv on OpenBSD
• free workspace assignments on reload
• fix mouse position at startup with multiple outputs
• no longer allow dragging global fullscreen floating containers
• fix rendering artifacts with global fullscreen containers
• fix disabling floating for scratchpad windows
• fix a crash when renaming an unfocused empty workspace matching an
assignment
• ensure containers have a size of at least 1px after resize
• permit invalid UTF-8 in layout JSON files (e.g. for window titles)
• correct invalid UTF-8 characters in window and container titles
• fix a crash when moving to a child of a floating container
• fix a crash when matching __focused__ with no window open
• fix no_focus when only using floating windows
• fix max_aspect calculation
• moving an unfocused container from another output now maintains
the correct focus order
• dont change focus order when swapping containers
• correctly update _NET_CURRENT_DESKTOP when moving containers between outputs
using the directional move command
• dont produce move events after attempting to directionally move a container
towards a direction it cant go
• fix sticky focus when switching to workspace on different output
┌────────────────────────────┐
│ Thanks! │
└────────────────────────────┘
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
Adrian Cybulski, Aestek, Alan Barr, Andriy Yablonskyy, Cassandra Fox,
Christian Duerr, Dan Elkouby, downzer0, Elouan Martinet, Felix Buehler,
Gravemind, Harry Lawrence, Hritik Vijay, hwangcc23, Ingo Bürk, Joona, Klorax,
lasers, Łukasz Adamczak, Martin, Michael Stapelberg, Oliver Graff,
Orestis Floros, Soumya, Takashi Iwai, Thomas Fischer, Todd Walton, Tony
Crisci, Uli Schlachter, Vivien Didelot
-- Michael Stapelberg, 2018-11-04

View File

@ -2,7 +2,7 @@
# Run autoreconf -fi to generate a configure script from this file.
AC_PREREQ([2.69])
AC_INIT([i3], [4.15], [https://github.com/i3/i3/issues])
AC_INIT([i3], [4.16], [https://github.com/i3/i3/issues])
# For AX_EXTEND_SRCDIR
AX_ENABLE_BUILDDIR
AM_INIT_AUTOMAKE([foreign subdir-objects -Wall no-dist-gzip dist-bzip2])
@ -81,9 +81,10 @@ AC_SEARCH_LIBS([floor], [m], , [AC_MSG_FAILURE([cannot find the required floor()
# libev does not ship with a pkg-config file :(.
AC_SEARCH_LIBS([ev_run], [ev], , [AC_MSG_FAILURE([cannot find the required ev_run() function despite trying to link with -lev])])
AC_SEARCH_LIBS([shm_open], [rt])
AC_SEARCH_LIBS([shm_open], [rt], [], [], [-pthread])
AC_SEARCH_LIBS([iconv_open], [iconv], , [AC_MSG_FAILURE([cannot find the required iconv_open() function despite trying to link with -liconv])])
AC_SEARCH_LIBS([iconv_open], [iconv], ,
AC_SEARCH_LIBS([libiconv_open], [iconv], , [AC_MSG_FAILURE([cannot find the required iconv_open() function despite trying to link with -liconv])]))
AX_PTHREAD
@ -109,12 +110,27 @@ AC_PROG_MAKE_SET
AC_PROG_RANLIB
AC_PROG_LN_S
AC_PATH_PROG([PATH_ASCIIDOC], [asciidoc])
AC_PATH_PROG([PATH_XMLTO], [xmlto])
AC_PATH_PROG([PATH_POD2MAN], [pod2man])
AM_CONDITIONAL([BUILD_MANS], [test x$PATH_ASCIIDOC != x && test x$PATH_XMLTO != x && test x$PATH_POD2MAN != x])
AM_CONDITIONAL([BUILD_DOCS], [test x$PATH_ASCIIDOC != x])
AC_ARG_ENABLE(docs,
AS_HELP_STRING(
[--disable-docs],
[disable building documentation]),
[ax_docs=$enableval],
[ax_docs=yes])
AC_ARG_ENABLE(mans,
AS_HELP_STRING(
[--disable-mans],
[disable building manual pages]),
[ax_mans=$enableval],
[ax_mans=yes])
AS_IF([test x$ax_docs = xyes || test x$ax_mans = xyes], [
AC_PATH_PROG([PATH_ASCIIDOC], [asciidoc])
])
AS_IF([test x$ax_mans = xyes], [
AC_PATH_PROG([PATH_XMLTO], [xmlto])
AC_PATH_PROG([PATH_POD2MAN], [pod2man])
])
AM_CONDITIONAL([BUILD_MANS], [test x$ax_mans = xyes && test x$PATH_ASCIIDOC != x && test x$PATH_XMLTO != x && test x$PATH_POD2MAN != x])
AM_CONDITIONAL([BUILD_DOCS], [test x$ax_docs = xyes && test x$PATH_ASCIIDOC != x])
AM_PROG_AR

View File

@ -1,26 +1,35 @@
#!/usr/bin/env perl
# vim:ts=4:sw=4:expandtab
# renders the layout tree using asymptote
#
# ./dump-asy.pl
# will render the entire tree
# ./dump-asy.pl 'name'
# will render the tree starting from the node with the specified name,
# e.g. ./dump-asy.pl 2 will render workspace 2 and below
use strict;
use warnings;
use Data::Dumper;
use Getopt::Long;
use Pod::Usage;
use AnyEvent::I3;
use File::Temp;
use File::Spec;
use File::Basename;
use v5.10;
use IPC::Cmd qw[can_run];
my %options = (
gv => 1,
save => undef,
help => 0,
);
my $result = GetOptions(
"gv!" => \$options{gv},
"save=s" => \$options{save},
"help|?" => \$options{help},
);
pod2usage(-verbose => 2, -exitcode => 0) if $options{help};
# prerequisites check so we can be specific about failures caused
# by not having these tools in the path
can_run('asy') or die 'Please install asymptote';
can_run('gv') or die 'Please install gv';
can_run('gv') or die 'Please install gv' unless !$options{gv};
my $i3 = i3();
@ -37,7 +46,7 @@ sub dump_node {
my $o = ($n->{orientation} eq 'none' ? "u" : ($n->{orientation} eq 'horizontal' ? "h" : "v"));
my $w = (defined($n->{window}) ? $n->{window} : "N");
my $na = ($n->{name} or "[Empty]");
my $na = ($n->{name} or ($n->{type} eq "floating_con" ? "[Floating con]" : "[Empty]"));
$na =~ s/#/\\#/g;
$na =~ s/\$/\\\$/g;
$na =~ s/&/\\&/g;
@ -47,14 +56,16 @@ sub dump_node {
if (!defined($n->{window})) {
$type = $n->{layout};
}
my $name = qq|``$na'' ($type)|;
my $marks = $n->{marks} ? ' [' . join('][', @{$n->{marks}}) . ']' : '';
my $name = qq|``$na'' ($type)$marks|;
print $tmp "TreeNode n" . $n->{id} . " = makeNode(";
print $tmp "n" . $parent->{id} . ", " if defined($parent);
print $tmp "\"" . $name . "\");\n";
dump_node($_, $n) for @{$n->{nodes}};
dump_node($_, $n) for @{$n->{nodes}};
dump_node($_, $n) for @{$n->{floating_nodes}};
}
sub find_node_with_name {
@ -82,5 +93,60 @@ say $tmp "draw(n" . $root->{id} . ", (0, 0));";
close($tmp);
my $rep = "$tmp";
$rep =~ s/asy$/eps/;
my $tmp_dir = dirname($rep);
system("cd $tmp_dir && asy $tmp && gv --scale=-1000 --noresize --widgetless $rep && rm $rep");
if ($options{gv}) {
my $tmp_dir = dirname($rep);
$options{save} = File::Spec->rel2abs($options{save}) if $options{save};
chdir($tmp_dir);
} else {
$rep = basename($rep); # Output in current dir.
}
system("asy $tmp"); # Create the .eps file.
system("gv --scale=-1000 --noresize --widgetless $rep") if $options{gv};
if ($options{save}) {
system("mv $rep ${options{save}}");
} elsif ($options{gv}) {
system("rm $rep");
}
system("rm $tmp");
__END__
=head1 NAME
dump-asy.pl - Render the layout tree using asymptote
=head1 SYNOPSIS
dump-asy.pl [workspace]
=head1 EXAMPLE
Render the entire tree, run:
./dump-asy.pl
Render the tree starting from the node with the specified name, run:
./dump-asy.pl 'name'
Render the entire tree, save in file 'file.eps' and show using gv, run:
./dump-asy.pl --save file.eps
=head1 OPTIONS
=over 8
=item B<--gv>
=item B<--no-gv>
Enable or disable showing the result through gv. If disabled, an .eps file will
be saved in the current working directory. Enabled by default.
=item B<--save>
Save result using the specified file-name. If omitted, no file will be saved
when using '--gv' or a random name will be used when using '--no-gv'.
=back

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
i3-wm (4.15.1-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Sat, 10 Mar 2018 17:27:26 +0100
i3-wm (4.15-1) unstable; urgency=medium
* New upstream release.

2
debian/control vendored
View File

@ -40,7 +40,7 @@ Description: metapackage (i3 window manager, screen locker, menu, statusbar)
Package: i3-wm
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, x11-utils
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

View File

@ -160,7 +160,8 @@ flood kicks.
== Debugging i3bar
To debug i3bar problems, add +verbose yes+ to all +bar {}+ blocks in your i3
To debug i3bar problems, use the +--verbose+ commandline parameter,
or add +verbose yes+ to all +bar {}+ blocks in your i3
config, reload your config and then restart all i3bar instances like this:
---------------------------------------------------------------------

View File

@ -107,7 +107,7 @@ stop_signal::
processing.
The default value (if none is specified) is SIGSTOP.
cont_signal::
Specify to i3bar the signal (as an integer)to send to continue your
Specify to i3bar the signal (as an integer) to send to continue your
processing.
The default value (if none is specified) is SIGCONT.
click_events::
@ -118,7 +118,8 @@ click_events::
full_text::
The +full_text+ will be displayed by i3bar on the status line. This is the
only required key.
only required key. If +full_text+ is an empty string, the block will be
skipped.
short_text::
Where appropriate, the +short_text+ (string) entry should also be
provided. It will be used in case the status line needs to be shortened
@ -242,6 +243,9 @@ relative_x, relative_y::
of the block
width, height::
Width and height (in px) of the block
modifiers::
An array of the modifiers active when the click occurred. The order in which
modifiers are listed is not guaranteed.
*Example*:
------------------------------------------
@ -249,6 +253,7 @@ width, height::
"name": "ethernet",
"instance": "eth0",
"button": 1,
"modifiers": ["Shift", "Mod1"],
"x": 1320,
"y": 1400,
"relative_x": 12,

View File

@ -65,6 +65,7 @@ to do that).
| 8 | +GET_BINDING_MODES+ | <<_binding_modes_reply,BINDING_MODES>> | Gets the names of all currently configured binding modes.
| 9 | +GET_CONFIG+ | <<_config_reply,CONFIG>> | Returns the last loaded i3 config.
| 10 | +SEND_TICK+ | <<_tick_reply,TICK>> | Sends a tick event with the specified payload.
| 11 | +SYNC+ | <<_sync_reply,SYNC>> | Sends an i3 sync event with the specified random value to the specified window.
|======================================================
So, a typical message could look like this:
@ -327,6 +328,8 @@ window (integer)::
This field is set to null for split containers or otherwise empty
containers. This ID corresponds to what xwininfo(1) and other
X11-related tools display (usually in hex).
window_properties (map)::
X11 window properties title, instance, class, window_role and transient_for.
urgent (bool)::
Whether this container (window, split container, floating container or
workspace) has the urgency hint set, directly or indirectly. All parent
@ -422,6 +425,12 @@ JSON dump:
"width": 1280,
"height": 782
},
"window_properties": {
"class": "Evince",
"instance": "evince",
"title": "Properties",
"transient_for": 52428808
},
"floating_nodes": [],
"nodes": [
@ -654,6 +663,18 @@ events generated prior to the +SEND_TICK+ message (happened-before relation).
{ "success": true }
-------------------
[[_sync_reply]]
=== SYNC reply
The reply is a map containing the "success" member. After the reply was
received, the https://i3wm.org/docs/testsuite.html#i3_sync[i3 sync message] was
responded to.
*Example:*
-------------------
{ "success": true }
-------------------
== Events
[[events]]
@ -672,6 +693,14 @@ program does not want to cope which such kinds of race conditions (an
event based library may not have a problem here), I suggest you create a
separate connection to receive events.
If an event message needs to be sent and the socket is not writeable (write
returns EAGAIN, happens when the socket doesn't have enough buffer space for
writing new data) then i3 uses a queue system to store outgoing messages for
each client. This is combined with a timer: if the message queue for a client is
not empty and no data where successfully written in the past 10 seconds, the
connection is killed. Practically, this means that your client should try to
always read events from the socket to avoid having its connection closed.
=== Subscribing to events
By sending a message of type SUBSCRIBE with a JSON-encoded array as payload

View File

@ -219,13 +219,15 @@ the window which matches any of the criteria. As an example:
A layout file as generated by +i3-save-tree(1)+ is not strictly valid JSON:
1. Layout files contain multiple “JSON documents” on the top level, whereas the
JSON standard only allows precisely one “document” (array or hash).
1. Layout files contain multiple “JSON texts” at the top level. The JSON
standard doesn't prohibit this, but in practice most JSON parsers only
allow precisely one “text” per document/file, and will mark multiple texts
as invalid JSON.
2. Layout files contain comments which are not standardized, but understood by
many parsers.
2. Layout files contain comments which are not allowed by the JSON standard,
but are understood by many parsers.
Both deviations from the JSON standard are to make manual editing by humans
Both of these deviations from the norm are to make manual editing by humans
easier. In case you are writing a more elaborate tool for manipulating these
layouts, you can either use a JSON parser that supports these deviations (for
example libyajl), transform the layout file to a JSON-conforming file, or

View File

@ -245,9 +245,11 @@ you open a new terminal, it will open below the current one.
So, how can you open a new terminal window to the *right* of the current one?
The solution is to use +focus parent+, which will focus the +Parent Container+ of
the current +Container+. In this case, you would focus the +Vertical Split
Container+ which is *inside* the horizontally oriented workspace. Thus, now new
windows will be opened to the right of the +Vertical Split Container+:
the current +Container+. In default configuration, use +$mod+a+ to navigate one
+Container+ up the tree (you can repeat this multiple times until you get to the
+Workspace Container+). In this case, you would focus the +Vertical Split Container+
which is *inside* the horizontally oriented workspace. Thus, now new windows will be
opened to the right of the +Vertical Split Container+:
image::tree-shot3.png["shot3",title="Focus parent, then open new terminal"]
@ -585,6 +587,16 @@ workspace_layout default|stacking|tabbed
workspace_layout tabbed
---------------------
=== Window title alignment
This option determines the window title's text alignment.
Default is +left+
*Syntax*:
---------------------------------------------
title_align left|center|right
---------------------------------------------
=== Default border style for new windows
This option determines which border style new windows will have. The default is
@ -730,7 +742,8 @@ resource database to achieve an easily maintainable, consistent color theme
across many X applications.
Defining a resource will load this resource from the resource database and
assign its value to the specified variable. A fallback must be specified in
assign its value to the specified variable. This is done verbatim and the value
must therefore be in the format that i3 uses. A fallback must be specified in
case the resource cannot be loaded from the database.
*Syntax*:
@ -754,14 +767,23 @@ set_from_resource $black i3wm.color0 #000000
To automatically make a specific window show up on a specific workspace, you
can use an *assignment*. You can match windows by using any criteria,
see <<command_criteria>>. It is recommended that you match on window classes
(and instances, when appropriate) instead of window titles whenever possible
because some applications first create their window, and then worry about
setting the correct title. Firefox with Vimperator comes to mind. The window
starts up being named Firefox, and only when Vimperator is loaded does the
title change. As i3 will get the title as soon as the application maps the
window (mapping means actually displaying it on the screen), youd need to have
to match on 'Firefox' in this case.
see <<command_criteria>>. The difference between +assign+ and
+for_window <criteria> move to workspace+ is that the former will only be
executed when the application maps the window (mapping means actually displaying
it on the screen) but the latter will be executed whenever a window changes its
properties to something that matches the specified criteria.
Thus, it is recommended that you match on window classes (and instances, when
appropriate) instead of window titles whenever possible because some
applications first create their window, and then worry about setting the correct
title. Firefox with Vimperator comes to mind. The window starts up being named
Firefox, and only when Vimperator is loaded does the title change. As i3 will
get the title as soon as the application maps the window, youd need to have to
match on 'Firefox' in this case.
Another known issue is with Spotify, which doesn't set the class hints when
mapping the window, meaning you'll have to use a +for_window+ rule to assign
Spotify to a specific workspace.
Finally, using +assign [tiling]+ and +assign [floating]+ is not supported.
You can also assign a window to show up on a specific output. You can use RandR
names such as +VGA1+ or names relative to the output with the currently focused
@ -887,7 +909,7 @@ the second screen and so on).
*Syntax*:
-------------------------------------
workspace <workspace> output <output>
workspace <workspace> output <output1> [output2]…
-------------------------------------
The 'output' is the name of the RandR output you attach your screen to. On a
@ -906,12 +928,15 @@ monitor name is “Dell UP2414Q”.
entire monitor, i3 will still use the entire area of the containing monitor
rather than that of just the output's.)
You can specify multiple outputs. The first available will be used.
If you use named workspaces, they must be quoted:
*Examples*:
---------------------------
workspace 1 output LVDS1
workspace 5 output VGA1
workspace 2 output primary
workspace 5 output VGA1 LVDS1
workspace "2: vim" output VGA1
---------------------------
@ -991,7 +1016,7 @@ ipc-socket ~/.i3/i3-ipc.sock
----------------------------
You can then use the +i3-msg+ application to perform any command listed in
the next section.
<<list_of_commands>>.
=== Focus follows mouse
@ -1117,6 +1142,7 @@ force_xinerama yes
Also note that your output names are not descriptive (like +HDMI1+) when using
Xinerama, instead they are counted up, starting at 0: +xinerama-0+, +xinerama-1+, …
[[workspace_auto_back_and_forth]]
=== Automatic back-and-forth when switching to the current workspace
This configuration directive enables automatic +workspace back_and_forth+ (see
@ -1594,7 +1620,7 @@ bar {
}
------------------------
=== Strip workspace numbers
=== Strip workspace numbers/name
Specifies whether workspace numbers should be displayed within the workspace
buttons. This is useful if you want to have a named workspace that stays in
@ -1605,11 +1631,15 @@ the form "[n]:[NAME]" will display only the name. You could use this, for
instance, to display Roman numerals rather than digits by naming your
workspaces to "1:I", "2:II", "3:III", "4:IV", ...
When +strip_workspace_name+ is set to +yes+, any workspace that has a name of
the form "[n]:[NAME]" will display only the number.
The default is to display the full name within the workspace button.
*Syntax*:
------------------------------
strip_workspace_numbers yes|no
strip_workspace_name yes|no
------------------------------
*Example*:
@ -1707,6 +1737,7 @@ bar {
}
--------------------------------------
[[list_of_commands]]
== List of commands
Commands are what you bind to specific keypresses. You can also issue commands
@ -2025,10 +2056,13 @@ Use the +move+ command to move a container.
# defaults to 10 pixels.
move <left|right|down|up> [<px> px]
# Moves the container either to a specific location
# or to the center of the screen. If 'absolute' is
# used, it is moved to the center of all outputs.
move [absolute] position <pos_x> [px] <pos_y> [px]
# Moves the container to the specified pos_x and pos_y
# coordinates on the screen.
move position <pos_x> [px] <pos_y> [px]
# Moves the container to the center of the screen.
# If 'absolute' is used, it is moved to the center of
# all outputs.
move [absolute] position center
# Moves the container to the current position of the
@ -2113,8 +2147,8 @@ for_window [instance=notepad] sticky enable
To change to a specific workspace, use the +workspace+ command, followed by the
number or name of the workspace. Pass the optional flag
+--no-auto-back-and-forth+ to disable <<back_and_forth>> for this specific call
only.
+--no-auto-back-and-forth+ to disable <<workspace_auto_back_and_forth>> for this
specific call only.
To move containers to specific workspaces, use +move container to workspace+.
@ -2253,8 +2287,7 @@ See <<move_to_outputs>> for how to move a container/workspace to a different
RandR output.
[[move_to_outputs]]
[[_moving_containers_workspaces_to_randr_outputs]]
=== Moving containers/workspaces to RandR outputs
=== [[_moving_containers_workspaces_to_randr_outputs]]Moving containers/workspaces to RandR outputs
To move a container to another RandR output (addressed by names like +LVDS1+ or
+VGA1+) or to a RandR output identified by a specific direction (like +left+,
@ -2313,20 +2346,21 @@ If you want to resize containers/windows using your keyboard, you can use the
*Syntax*:
-------------------------------------------------------
resize grow|shrink <direction> [<px> px [or <ppt> ppt]]
resize set <width> [px | ppt] <height> [px | ppt]
resize set [width] <width> [px | ppt]
resize set height <height> [px | ppt]
resize set [width] <width> [px | ppt] [height] <height> [px | ppt]
-------------------------------------------------------
Direction can either be one of +up+, +down+, +left+ or +right+. Or you can be
less specific and use +width+ or +height+, in which case i3 will take/give
space from all the other containers. The optional pixel argument specifies by
how many pixels a *floating container* should be grown or shrunk (the default
is 10 pixels). The ppt argument means percentage points and specifies by how
many percentage points a *tiling container* should be grown or shrunk (the
default is 10 percentage points).
less specific and use +width+ or +height+, in which case i3 will take/give space
from all the other containers. The optional pixel argument specifies by how many
pixels a container should be grown or shrunk (the default is 10 pixels). The
optional ppt argument means "percentage points", and if specified it indicates
that a *tiling container* should be grown or shrunk by that many points, instead
of by the +px+ value.
Notes about +resize set+: a value of 0 for <width> or <height> means "do
not resize in this direction", and resizing a tiling container by +px+ is not
implemented.
Note about +resize set+: a value of 0 for <width> or <height> means "do not
resize in this direction".
It is recommended to define bindings for resizing in a dedicated binding mode.
See <<binding_modes>> and the example in the i3
@ -2469,7 +2503,9 @@ To change the border of the current client, you can use +border normal+ to use t
border (including window title), +border pixel 1+ to use a 1-pixel border (no window title)
and +border none+ to make the client borderless.
There is also +border toggle+ which will toggle the different border styles.
There is also +border toggle+ which will toggle the different border styles. The
optional pixel argument can be used to specify the border width when switching
to the normal and pixel styles.
Note that "pixel" refers to logical pixel. On HiDPI displays, a logical pixel
may be represented by multiple physical pixels, so +pixel 1+ might not
@ -2477,8 +2513,8 @@ necessarily translate into a single pixel row wide border.
*Syntax*:
-----------------------------------------------
border normal|pixel [<n>]
border none|toggle
border normal|pixel|toggle [<n>]
border none
# legacy syntax, equivalent to "border pixel 1"
border 1pixel

View File

@ -147,7 +147,7 @@ bindsym Mod1+Shift+c reload
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
bindsym Mod1+Shift+r restart
# exit i3 (logs you out of your X session)
bindsym Mod1+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'"
bindsym Mod1+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"
# resize window (you can also use the mouse for that)
mode "resize" {

View File

@ -133,7 +133,7 @@ bindcode $mod+Shift+54 reload
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
bindcode $mod+Shift+27 restart
# exit i3 (logs you out of your X session)
bindcode $mod+Shift+26 exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'"
bindcode $mod+Shift+26 exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"
# resize window (you can also use the mouse for that)
mode "resize" {

View File

@ -77,7 +77,7 @@ for my $line (@lines) {
($line =~ /
^\s* # skip leading whitespace
([a-z_]+ \s* = \s*|) # optional identifier
(.*?) -> \s* # token
(.*?) -> \s* # token
(.*) # optional action
/x);

View File

@ -48,6 +48,9 @@
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
#define SN_API_NOT_YET_FROZEN 1
#include <libsn/sn-launchee.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
@ -101,7 +104,7 @@ static struct xkb_keymap *xkb_keymap;
static uint8_t xkb_base_event;
static uint8_t xkb_base_error;
static void finish();
static void finish(void);
#include "GENERATED_config_enums.h"
@ -213,7 +216,7 @@ static const char *get_string(const char *identifier) {
static void clear_stack(void) {
for (int c = 0; c < 10; c++) {
if (stack[c].type == STACK_STR && stack[c].val.str != NULL)
if (stack[c].type == STACK_STR)
free(stack[c].val.str);
stack[c].identifier = NULL;
stack[c].val.str = NULL;
@ -293,6 +296,7 @@ static char *next_state(const cmdp_token *token) {
}
sasprintf(&res, "bindsym %s%s%s %s%s\n", (modifiers == NULL ? "" : modrep), (modifiers == NULL ? "" : "+"), str, (release == NULL ? "" : release), get_string("command"));
clear_stack();
free(modrep);
return res;
}
@ -478,7 +482,7 @@ static void txt(int col, int row, char *text, color_t fg, color_t bg) {
* Handles expose events, that is, draws the window contents.
*
*/
static int handle_expose() {
static int handle_expose(void) {
const color_t black = draw_util_hex_to_color("#000000");
const color_t white = draw_util_hex_to_color("#FFFFFF");
const color_t green = draw_util_hex_to_color("#00FF00");
@ -631,15 +635,13 @@ static void handle_button_press(xcb_button_press_event_t *event) {
modifier = MOD_Mod1;
handle_expose();
}
return;
}
/*
* Creates the config file and tells i3 to reload.
*
*/
static void finish() {
static void finish(void) {
printf("creating \"%s\"...\n", config_path);
struct xkb_context *xkb_context;
@ -745,10 +747,12 @@ int main(int argc, char *argv[]) {
char *pattern = "pango:monospace 8";
char *patternbold = "pango:monospace bold 8";
int o, option_index = 0;
bool headless_run = false;
static struct option long_options[] = {
{"socket", required_argument, 0, 's'},
{"version", no_argument, 0, 'v'},
{"modifier", required_argument, 0, 'm'},
{"limit", required_argument, 0, 'l'},
{"prompt", required_argument, 0, 'P'},
{"prefix", required_argument, 0, 'p'},
@ -756,7 +760,7 @@ int main(int argc, char *argv[]) {
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}};
char *options_string = "s:vh";
char *options_string = "sm:vh";
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
switch (o) {
@ -767,9 +771,18 @@ int main(int argc, char *argv[]) {
case 'v':
printf("i3-config-wizard " I3_VERSION "\n");
return 0;
case 'm':
headless_run = true;
if (strcmp(optarg, "alt") == 0)
modifier = MOD_Mod1;
else if (strcmp(optarg, "win") == 0)
modifier = MOD_Mod4;
else
err(EXIT_FAILURE, "Invalid modifier key %s", optarg);
break;
case 'h':
printf("i3-config-wizard " I3_VERSION "\n");
printf("i3-config-wizard [-s <socket>] [-v]\n");
printf("i3-config-wizard [-s <socket>] [-m win|alt] [-v] [-h]\n");
return 0;
}
}
@ -826,12 +839,22 @@ int main(int argc, char *argv[]) {
modmap_cookie = xcb_get_modifier_mapping(conn);
symbols = xcb_key_symbols_alloc(conn);
if (headless_run) {
finish();
return 0;
}
/* Place requests for the atoms we need as soon as possible */
#define xmacro(atom) \
xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
#include "atoms.xmacro"
#undef xmacro
/* Init startup notification. */
SnDisplay *sndisplay = sn_xcb_display_new(conn, NULL, NULL);
SnLauncheeContext *sncontext = sn_launchee_context_new_from_environment(sndisplay, screen);
sn_display_unref(sndisplay);
root_screen = xcb_aux_get_screen(conn, screen);
root = root_screen->root;
@ -864,6 +887,9 @@ int main(int argc, char *argv[]) {
0, /* back pixel: black */
XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_BUTTON_PRESS});
if (sncontext) {
sn_launchee_context_setup_window(sncontext, win);
}
/* Map the window (make it visible) */
xcb_map_window(conn, win);
@ -925,6 +951,12 @@ int main(int argc, char *argv[]) {
exit(-1);
}
/* Startup complete. */
if (sncontext) {
sn_launchee_context_complete(sncontext);
sn_launchee_context_unref(sncontext);
}
xcb_flush(conn);
xcb_generic_event_t *event;
@ -937,13 +969,12 @@ int main(int argc, char *argv[]) {
/* Strip off the highest bit (set if the event is generated) */
int type = (event->response_type & 0x7F);
/* TODO: handle mappingnotify */
switch (type) {
case XCB_KEY_PRESS:
handle_key_press(NULL, conn, (xcb_key_press_event_t *)event);
break;
/* TODO: handle mappingnotify */
case XCB_BUTTON_PRESS:
handle_button_press((xcb_button_press_event_t *)event);
break;

View File

@ -156,7 +156,7 @@ static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_rel
return 1;
}
static void finish_input() {
static void finish_input(void) {
char *command = (char *)concat_strings(glyphs_utf8, input_position);
/* count the occurrences of %s in the string */

View File

@ -95,8 +95,10 @@ static int reply_start_map_cb(void *params) {
static int reply_end_map_cb(void *params) {
if (!last_reply.success) {
fprintf(stderr, "ERROR: Your command: %s\n", last_reply.input);
fprintf(stderr, "ERROR: %s\n", last_reply.errorposition);
if (last_reply.input) {
fprintf(stderr, "ERROR: Your command: %s\n", last_reply.input);
fprintf(stderr, "ERROR: %s\n", last_reply.errorposition);
}
fprintf(stderr, "ERROR: %s\n", last_reply.error);
}
return 1;
@ -164,16 +166,18 @@ int main(int argc, char *argv[]) {
uint32_t message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND;
char *payload = NULL;
bool quiet = false;
bool monitor = false;
static struct option long_options[] = {
{"socket", required_argument, 0, 's'},
{"type", required_argument, 0, 't'},
{"version", no_argument, 0, 'v'},
{"quiet", no_argument, 0, 'q'},
{"monitor", no_argument, 0, 'm'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}};
char *options_string = "s:t:vhq";
char *options_string = "s:t:vhqm";
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
if (o == 's') {
@ -202,25 +206,34 @@ int main(int argc, char *argv[]) {
message_type = I3_IPC_MESSAGE_TYPE_GET_CONFIG;
} else if (strcasecmp(optarg, "send_tick") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_SEND_TICK;
} else if (strcasecmp(optarg, "subscribe") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_SUBSCRIBE;
} else {
printf("Unknown message type\n");
printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config, send_tick\n");
printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config, send_tick, subscribe\n");
exit(EXIT_FAILURE);
}
} else if (o == 'q') {
quiet = true;
} else if (o == 'm') {
monitor = true;
} else if (o == 'v') {
printf("i3-msg " I3_VERSION "\n");
return 0;
} else if (o == 'h') {
printf("i3-msg " I3_VERSION "\n");
printf("i3-msg [-s <socket>] [-t <type>] <message>\n");
printf("i3-msg [-s <socket>] [-t <type>] [-m] <message>\n");
return 0;
} else if (o == '?') {
exit(EXIT_FAILURE);
}
}
if (monitor && message_type != I3_IPC_MESSAGE_TYPE_SUBSCRIBE) {
fprintf(stderr, "The monitor option -m is used with -t SUBSCRIBE exclusively.\n");
exit(EXIT_FAILURE);
}
/* Use all arguments, separated by whitespace, as payload.
* This way, you dont have to do i3-msg 'mark foo', you can use
* i3-msg mark foo */
@ -244,9 +257,6 @@ int main(int argc, char *argv[]) {
err(EXIT_FAILURE, "IPC: write()");
free(payload);
if (quiet)
return 0;
uint32_t reply_length;
uint32_t reply_type;
uint8_t *reply;
@ -273,8 +283,9 @@ int main(int argc, char *argv[]) {
errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
}
/* NB: We still fall-through and print the reply, because even if one
* command failed, that doesnt mean that all commands failed. */
if (!quiet) {
printf("%.*s\n", reply_length, reply);
}
} else if (reply_type == I3_IPC_REPLY_TYPE_CONFIG) {
yajl_handle handle = yajl_alloc(&config_callbacks, NULL, NULL);
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
@ -287,12 +298,30 @@ int main(int argc, char *argv[]) {
case yajl_status_error:
errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
}
} else if (reply_type == I3_IPC_REPLY_TYPE_SUBSCRIBE) {
do {
free(reply);
if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
if (ret == -1)
err(EXIT_FAILURE, "IPC: read()");
exit(1);
}
goto exit;
if (!(reply_type & I3_IPC_EVENT_MASK)) {
errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected an event", reply_type);
}
if (!quiet) {
fprintf(stdout, "%.*s\n", reply_length, reply);
fflush(stdout);
}
} while (monitor);
} else {
if (!quiet) {
printf("%.*s\n", reply_length, reply);
}
}
printf("%.*s\n", reply_length, reply);
exit:
free(reply);
close(sockfd);

View File

@ -32,6 +32,9 @@
#include <xcb/randr.h>
#include <xcb/xcb_cursor.h>
#define SN_API_NOT_YET_FROZEN 1
#include <libsn/sn-launchee.h>
#include "i3-nagbar.h"
/** This is the equivalent of XC_left_ptr. Im not sure why xcb doesnt have a
@ -52,6 +55,7 @@ typedef struct {
char *action;
int16_t x;
uint16_t width;
bool terminal;
} button_t;
static xcb_window_t win;
@ -99,10 +103,10 @@ void debuglog(char *fmt, ...) {
}
/*
* Starts the given application by passing it through a shell. We use double fork
* to avoid zombie processes. As the started applications parent exits (immediately),
* the application is reparented to init (process-id 1), which correctly handles
* childs, so we dont have to do it :-).
* Starts the given application by passing it through a shell. We use double
* fork to avoid zombie processes. As the started applications parent exits
* (immediately), the application is reparented to init (process-id 1), which
* correctly handles children, so we dont have to do it :-).
*
* The shell is determined by looking for the SHELL environment variable. If it
* does not exist, /bin/sh is used.
@ -115,7 +119,7 @@ static void start_application(const char *command) {
setsid();
if (fork() == 0) {
/* This is the child */
execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, (void *)NULL);
execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, NULL);
/* not reached */
}
exit(0);
@ -184,7 +188,11 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
}
char *terminal_cmd;
sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", link_path);
if (button->terminal) {
sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", link_path);
} else {
terminal_cmd = sstrdup(link_path);
}
printf("argv0 = %s\n", argv0);
printf("terminal_cmd = %s\n", terminal_cmd);
@ -358,12 +366,13 @@ int main(int argc, char *argv[]) {
{"version", no_argument, 0, 'v'},
{"font", required_argument, 0, 'f'},
{"button", required_argument, 0, 'b'},
{"button-no-terminal", required_argument, 0, 'B'},
{"help", no_argument, 0, 'h'},
{"message", required_argument, 0, 'm'},
{"type", required_argument, 0, 't'},
{0, 0, 0, 0}};
char *options_string = "b:f:m:t:vh";
char *options_string = "b:B:f:m:t:vh";
prompt = i3string_from_utf8("Please do not run this program.");
@ -385,12 +394,14 @@ int main(int argc, char *argv[]) {
break;
case 'h':
printf("i3-nagbar " I3_VERSION "\n");
printf("i3-nagbar [-m <message>] [-b <button> <action>] [-t warning|error] [-f <font>] [-v]\n");
printf("i3-nagbar [-m <message>] [-b <button> <action>] [-B <button> <action>] [-t warning|error] [-f <font>] [-v]\n");
return 0;
case 'b':
case 'B':
buttons = srealloc(buttons, sizeof(button_t) * (buttoncnt + 1));
buttons[buttoncnt].label = i3string_from_utf8(optarg);
buttons[buttoncnt].action = argv[optind];
buttons[buttoncnt].terminal = (o == 'b');
printf("button with label *%s* and action *%s*\n",
i3string_as_utf8(buttons[buttoncnt].label),
buttons[buttoncnt].action);
@ -415,6 +426,11 @@ int main(int argc, char *argv[]) {
#include "atoms.xmacro"
#undef xmacro
/* Init startup notification. */
SnDisplay *sndisplay = sn_xcb_display_new(conn, NULL, NULL);
SnLauncheeContext *sncontext = sn_launchee_context_new_from_environment(sndisplay, screens);
sn_display_unref(sndisplay);
root_screen = xcb_aux_get_screen(conn, screens);
root = root_screen->root;
@ -484,6 +500,9 @@ int main(int argc, char *argv[]) {
XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE,
cursor});
if (sncontext) {
sn_launchee_context_setup_window(sncontext, win);
}
/* Map the window (make it visible) */
xcb_map_window(conn, win);
@ -544,6 +563,12 @@ int main(int argc, char *argv[]) {
/* Initialize the drawable bar */
draw_util_surface_init(conn, &bar, win, get_visualtype(root_screen), win_pos.width, win_pos.height);
/* Startup complete. */
if (sncontext) {
sn_launchee_context_complete(sncontext);
sn_launchee_context_unref(sncontext);
}
/* Grab the keyboard to get all input */
xcb_flush(conn);

View File

@ -123,9 +123,7 @@ sub strip_containers {
delete $tree->{current_border_width} if $tree->{current_border_width} == -1;
for my $key (keys %$tree) {
next if exists($allowed_keys{$key});
delete $tree->{$key};
delete $tree->{$key} unless exists($allowed_keys{$key});
}
for my $key (qw(nodes floating_nodes)) {
@ -169,7 +167,8 @@ sub dump_containers {
if (leaf_node($tree)) {
my $swallows = {};
for my $property (keys %{$tree->{window_properties}}) {
$swallows->{$property} = '^' . quotemeta($tree->{window_properties}->{$property}) . '$';
$swallows->{$property} = '^' . quotemeta($tree->{window_properties}->{$property}) . '$'
if $property ne 'transient_for';
}
$tree->{swallows} = [ $swallows ];
}

View File

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

View File

@ -31,7 +31,7 @@ typedef struct {
*/
int stop_signal;
/**
* The signal requested by the client to inform it of theun hidden state of i3bar
* The signal requested by the client to inform it of the unhidden state of i3bar
*/
int cont_signal;
@ -85,4 +85,4 @@ bool child_want_click_events(void);
* Generates a click event, if enabled.
*
*/
void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int width, int height);
void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int width, int height, int mods);

View File

@ -41,17 +41,18 @@ typedef struct tray_output_t {
} tray_output_t;
typedef struct config_t {
int modifier;
uint32_t modifier;
TAILQ_HEAD(bindings_head, binding_t)
bindings;
position_t position;
int verbose;
bool verbose;
struct xcb_color_strings_t colors;
bool disable_binding_mode_indicator;
bool disable_ws;
bool strip_ws_numbers;
bool strip_ws_name;
char *bar_id;
char *command;
char *fontname;

View File

@ -60,7 +60,7 @@ int separator_symbol_width;
* depend on 'config'.
*
*/
char *init_xcb_early();
char *init_xcb_early(void);
/**
* Initialization which depends on 'config' being usable. Called after the

View File

@ -8,6 +8,7 @@
*
*/
#include "common.h"
#include "yajl_utils.h"
#include <stdlib.h>
#include <unistd.h>
@ -27,6 +28,8 @@
#include <yajl/yajl_gen.h>
#include <paths.h>
#include <xcb/xcb_keysyms.h>
/* Global variables for child_*() */
i3bar_child child;
@ -133,7 +136,7 @@ finish:
* Stop and free() the stdin- and SIGCHLD-watchers
*
*/
void cleanup(void) {
static void cleanup(void) {
if (stdin_io != NULL) {
ev_io_stop(main_loop, stdin_io);
FREE(stdin_io);
@ -400,7 +403,7 @@ static bool read_json_input(unsigned char *input, int length) {
* in statusline
*
*/
void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
static void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
int rec;
unsigned char *buffer = get_buffer(watcher, &rec);
if (buffer == NULL)
@ -420,7 +423,7 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
* whether this is JSON or plain text
*
*/
void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
static void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
int rec;
unsigned char *buffer = get_buffer(watcher, &rec);
if (buffer == NULL)
@ -457,7 +460,7 @@ void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
* anymore
*
*/
void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
static void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
int exit_status = WEXITSTATUS(watcher->rstatus);
ELOG("Child (pid: %d) unexpectedly exited with status %d\n",
@ -477,7 +480,7 @@ void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
draw_bars(false);
}
void child_write_output(void) {
static void child_write_output(void) {
if (child.click_events) {
const unsigned char *output;
size_t size;
@ -580,7 +583,7 @@ void start_child(char *command) {
atexit(kill_child_at_exit);
}
void child_click_events_initialize(void) {
static void child_click_events_initialize(void) {
if (!child.click_events_init) {
yajl_gen_array_open(gen);
child_write_output();
@ -588,15 +591,11 @@ void child_click_events_initialize(void) {
}
}
void child_click_events_key(const char *key) {
yajl_gen_string(gen, (const unsigned char *)key, strlen(key));
}
/*
* Generates a click event, if enabled.
*
*/
void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int width, int height) {
void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int width, int height, int mods) {
if (!child.click_events) {
return;
}
@ -606,34 +605,52 @@ void send_block_clicked(int button, const char *name, const char *instance, int
yajl_gen_map_open(gen);
if (name) {
child_click_events_key("name");
yajl_gen_string(gen, (const unsigned char *)name, strlen(name));
ystr("name");
ystr(name);
}
if (instance) {
child_click_events_key("instance");
yajl_gen_string(gen, (const unsigned char *)instance, strlen(instance));
ystr("instance");
ystr(instance);
}
child_click_events_key("button");
ystr("button");
yajl_gen_integer(gen, button);
child_click_events_key("x");
ystr("modifiers");
yajl_gen_array_open(gen);
if (mods & XCB_MOD_MASK_SHIFT)
ystr("Shift");
if (mods & XCB_MOD_MASK_CONTROL)
ystr("Control");
if (mods & XCB_MOD_MASK_1)
ystr("Mod1");
if (mods & XCB_MOD_MASK_2)
ystr("Mod2");
if (mods & XCB_MOD_MASK_3)
ystr("Mod3");
if (mods & XCB_MOD_MASK_4)
ystr("Mod4");
if (mods & XCB_MOD_MASK_5)
ystr("Mod5");
yajl_gen_array_close(gen);
ystr("x");
yajl_gen_integer(gen, x);
child_click_events_key("y");
ystr("y");
yajl_gen_integer(gen, y);
child_click_events_key("relative_x");
ystr("relative_x");
yajl_gen_integer(gen, x_rel);
child_click_events_key("relative_y");
ystr("relative_y");
yajl_gen_integer(gen, y_rel);
child_click_events_key("width");
ystr("width");
yajl_gen_integer(gen, width);
child_click_events_key("height");
ystr("height");
yajl_gen_integer(gen, height);
yajl_gen_map_close(gen);

View File

@ -119,6 +119,7 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
return 1;
}
/* Kept for backwards compatibility. */
if (!strcmp(cur_key, "modifier")) {
DLOG("modifier = %.*s\n", len, val);
if (len == strlen("none") && !strncmp((const char *)val, "none", strlen("none"))) {
@ -297,9 +298,17 @@ static int config_boolean_cb(void *params_, int val) {
return 1;
}
if (!strcmp(cur_key, "strip_workspace_name")) {
DLOG("strip_workspace_name = %d\n", val);
config.strip_ws_name = val;
return 1;
}
if (!strcmp(cur_key, "verbose")) {
DLOG("verbose = %d\n", val);
config.verbose = val;
if (!config.verbose) {
DLOG("verbose = %d\n", val);
config.verbose = val;
}
return 1;
}
@ -330,6 +339,12 @@ static int config_integer_cb(void *params_, long long val) {
return 1;
}
if (!strcmp(cur_key, "modifier")) {
DLOG("modifier = %lld\n", val);
config.modifier = (uint32_t)val;
return 1;
}
return 0;
}

View File

@ -34,7 +34,7 @@ typedef void (*handler_t)(char *);
* Since i3 does not give us much feedback on commands, we do not much
*
*/
void got_command_reply(char *reply) {
static void got_command_reply(char *reply) {
/* TODO: Error handling for command replies */
}
@ -42,7 +42,7 @@ void got_command_reply(char *reply) {
* Called, when we get a reply with workspaces data
*
*/
void got_workspace_reply(char *reply) {
static void got_workspace_reply(char *reply) {
DLOG("Got workspace data!\n");
parse_workspaces_json(reply);
draw_bars(false);
@ -53,7 +53,7 @@ void got_workspace_reply(char *reply) {
* Since i3 does not give us much feedback on commands, we do not much
*
*/
void got_subscribe_reply(char *reply) {
static void got_subscribe_reply(char *reply) {
DLOG("Got subscribe reply: %s\n", reply);
/* TODO: Error handling for subscribe commands */
}
@ -62,7 +62,7 @@ void got_subscribe_reply(char *reply) {
* Called, when we get a reply with outputs data
*
*/
void got_output_reply(char *reply) {
static void got_output_reply(char *reply) {
DLOG("Clearing old output configuration...\n");
free_outputs();
@ -87,7 +87,7 @@ void got_output_reply(char *reply) {
* Called when we get the configuration for our bar instance
*
*/
void got_bar_config(char *reply) {
static void got_bar_config(char *reply) {
DLOG("Received bar config \"%s\"\n", reply);
/* We initiate the main function by requesting infos about the outputs and
* workspaces. Everything else (creating the bars, showing the right workspace-
@ -114,20 +114,25 @@ void got_bar_config(char *reply) {
/* Data structure to easily call the reply handlers later */
handler_t reply_handlers[] = {
&got_command_reply,
&got_workspace_reply,
&got_subscribe_reply,
&got_output_reply,
NULL,
NULL,
&got_bar_config,
&got_command_reply, /* I3_IPC_REPLY_TYPE_COMMAND */
&got_workspace_reply, /* I3_IPC_REPLY_TYPE_WORKSPACES */
&got_subscribe_reply, /* I3_IPC_REPLY_TYPE_SUBSCRIBE */
&got_output_reply, /* I3_IPC_REPLY_TYPE_OUTPUTS */
NULL, /* I3_IPC_REPLY_TYPE_TREE */
NULL, /* I3_IPC_REPLY_TYPE_MARKS */
&got_bar_config, /* I3_IPC_REPLY_TYPE_BAR_CONFIG */
NULL, /* I3_IPC_REPLY_TYPE_VERSION */
NULL, /* I3_IPC_REPLY_TYPE_BINDING_MODES */
NULL, /* I3_IPC_REPLY_TYPE_CONFIG */
NULL, /* I3_IPC_REPLY_TYPE_TICK */
NULL, /* I3_IPC_REPLY_TYPE_SYNC */
};
/*
* Called, when a workspace event arrives (i.e. the user changed the workspace)
*
*/
void got_workspace_event(char *event) {
static void got_workspace_event(char *event) {
DLOG("Got workspace event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
}
@ -136,7 +141,7 @@ void got_workspace_event(char *event) {
* Called, when an output event arrives (i.e. the screen configuration changed)
*
*/
void got_output_event(char *event) {
static void got_output_event(char *event) {
DLOG("Got output event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
if (!config.disable_ws) {
@ -148,7 +153,7 @@ void got_output_event(char *event) {
* Called, when a mode event arrives (i3 changed binding mode).
*
*/
void got_mode_event(char *event) {
static void got_mode_event(char *event) {
DLOG("Got mode event!\n");
parse_mode_json(event);
draw_bars(false);
@ -158,7 +163,7 @@ void got_mode_event(char *event) {
* Called, when a barconfig_update event arrives (i.e. i3 changed the bar hidden_state or mode)
*
*/
void got_bar_config_update(char *event) {
static void got_bar_config_update(char *event) {
/* check whether this affect this bar instance by checking the bar_id */
char *expected_id;
sasprintf(&expected_id, "\"id\":\"%s\"", config.bar_id);
@ -208,7 +213,7 @@ handler_t event_handlers[] = {
* Called, when we get a message from i3
*
*/
void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
static void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
DLOG("Got data!\n");
int fd = watcher->fd;
@ -273,8 +278,8 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
buffer[size] = '\0';
/* And call the callback (indexed by the type) */
if (type & (1 << 31)) {
type ^= 1 << 31;
if (type & (1UL << 31)) {
type ^= 1UL << 31;
event_handlers[type](buffer);
} else {
if (reply_handlers[type])
@ -304,7 +309,7 @@ int i3_send_msg(uint32_t type, const char *payload) {
char *buffer = smalloc(to_write);
char *walk = buffer;
strncpy(buffer, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC));
memcpy(buffer, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC));
walk += strlen(I3_IPC_MAGIC);
memcpy(walk, &len, sizeof(uint32_t));
walk += sizeof(uint32_t);

View File

@ -44,7 +44,7 @@ void debuglog(char *fmt, ...) {
* Glob path, i.e. expand ~
*
*/
char *expand_path(char *path) {
static char *expand_path(char *path) {
static glob_t globbuf;
if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) {
ELOG("glob() failed\n");
@ -55,13 +55,14 @@ char *expand_path(char *path) {
return result;
}
void print_usage(char *elf_name) {
static void print_usage(char *elf_name) {
printf("Usage: %s -b bar_id [-s sock_path] [-h] [-v]\n", elf_name);
printf("\n");
printf("-b, --bar_id <bar_id>\tBar ID for which to get the configuration\n");
printf("-s, --socket <sock_path>\tConnect to i3 via <sock_path>\n");
printf("-h, --help Display this help message and exit\n");
printf("-v, --version Display version number and exit\n");
printf("-V, --verbose Enable verbose mode\n");
printf("\n");
printf(" PLEASE NOTE that i3bar will be automatically started by i3\n"
" as soon as there is a 'bar' configuration block in your\n"
@ -75,7 +76,7 @@ void print_usage(char *elf_name) {
* in main() with that
*
*/
void sig_cb(struct ev_loop *loop, ev_signal *watcher, int revents) {
static void sig_cb(struct ev_loop *loop, ev_signal *watcher, int revents) {
switch (watcher->signum) {
case SIGTERM:
DLOG("Got a SIGTERM, stopping\n");
@ -106,9 +107,10 @@ int main(int argc, char **argv) {
{"bar_id", required_argument, 0, 'b'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{"verbose", no_argument, 0, 'V'},
{NULL, 0, 0, 0}};
while ((opt = getopt_long(argc, argv, "b:s:hv", long_opt, &option_index)) != -1) {
while ((opt = getopt_long(argc, argv, "b:s:hvV", long_opt, &option_index)) != -1) {
switch (opt) {
case 's':
socket_path = expand_path(optarg);
@ -120,6 +122,9 @@ int main(int argc, char **argv) {
case 'b':
config.bar_id = sstrdup(optarg);
break;
case 'V':
config.verbose = true;
break;
default:
print_usage(argv[0]);
exit(EXIT_SUCCESS);

View File

@ -106,8 +106,8 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
const char *ws_name = (const char *)val;
params->workspaces_walk->canonical_name = sstrndup(ws_name, len);
if (config.strip_ws_numbers && params->workspaces_walk->num >= 0) {
/* Special case: strip off the workspace number */
if ((config.strip_ws_numbers || config.strip_ws_name) && params->workspaces_walk->num >= 0) {
/* Special case: strip off the workspace number/name */
static char ws_num[10];
snprintf(ws_num, sizeof(ws_num), "%d", params->workspaces_walk->num);
@ -119,11 +119,14 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
if (offset && ws_name[offset] == ':')
offset += 1;
/* 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));
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));
} else {
params->workspaces_walk->name = i3string_from_markup(ws_num);
}
} else {
/* Default case: just save the name */
params->workspaces_walk->name = i3string_from_markup_with_length(ws_name, len);

View File

@ -79,7 +79,7 @@ int bar_height;
/* These are only relevant for XKB, which we only need for grabbing modifiers */
int xkb_base;
int mod_pressed = 0;
bool mod_pressed = 0;
/* Event watchers, to interact with the user */
ev_prepare *xcb_prep;
@ -92,6 +92,9 @@ static mode binding;
/* Indicates whether a new binding mode was recently activated */
bool activated_mode = false;
/* The output in which the tray should be displayed. */
static i3_output *output_for_tray;
/* The parsed colors */
struct xcb_colors_t {
color_t bar_fg;
@ -146,13 +149,13 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
return 0;
}
uint32_t get_sep_offset(struct status_block *block) {
static uint32_t get_sep_offset(struct status_block *block) {
if (!block->no_separator && block->sep_block_width > 0)
return block->sep_block_width / 2 + block->sep_block_width % 2;
return 0;
}
int get_tray_width(struct tc_head *trayclients) {
static int get_tray_width(struct tc_head *trayclients) {
trayclient *trayclient;
int tray_width = 0;
TAILQ_FOREACH_REVERSE(trayclient, trayclients, tc_head, tailq) {
@ -193,7 +196,7 @@ static void draw_separator(i3_output *output, uint32_t x, struct status_block *b
}
}
uint32_t predict_statusline_length(bool use_short_text) {
static uint32_t predict_statusline_length(bool use_short_text) {
uint32_t width = 0;
struct status_block *block;
@ -245,7 +248,7 @@ uint32_t predict_statusline_length(bool use_short_text) {
/*
* Redraws the statusline to the output's statusline_buffer
*/
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, bool use_short_text) {
struct status_block *block;
color_t bar_color = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
@ -330,7 +333,7 @@ void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_color
* Hides all bars (unmaps them)
*
*/
void hide_bars(void) {
static void hide_bars(void) {
if ((config.hide_on_modifier == M_DOCK) || (config.hidden_state == S_SHOW && config.hide_on_modifier == M_HIDE)) {
return;
}
@ -349,7 +352,7 @@ void hide_bars(void) {
* Unhides all bars (maps them)
*
*/
void unhide_bars(void) {
static void unhide_bars(void) {
if (config.hide_on_modifier != M_HIDE) {
return;
}
@ -457,7 +460,7 @@ static bool execute_custom_command(xcb_keycode_t input_code, bool event_is_relea
* wheel was used and change the workspace appropriately
*
*/
void handle_button(xcb_button_press_event_t *event) {
static void handle_button(xcb_button_press_event_t *event) {
/* Determine, which bar was clicked */
i3_output *walk;
xcb_window_t bar = event->event;
@ -500,13 +503,12 @@ void handle_button(xcb_button_press_event_t *event) {
/* If the child asked for click events,
* check if a status block has been clicked. */
int tray_width = get_tray_width(walk->trayclients);
int block_x = 0, last_block_x;
int offset = walk->rect.w - walk->statusline_width - tray_width - logical_px(sb_hoff_px);
int last_block_x = 0;
int offset = walk->rect.w - walk->statusline_width - tray_width - logical_px((tray_width > 0) * sb_hoff_px);
int32_t statusline_x = x - offset;
if (statusline_x >= 0 && statusline_x < walk->statusline_width) {
struct status_block *block;
int sep_offset_remainder = 0;
TAILQ_FOREACH(block, &statusline_head, blocks) {
i3String *text = block->full_text;
@ -519,16 +521,15 @@ void handle_button(xcb_button_press_event_t *event) {
if (i3string_get_num_bytes(text) == 0)
continue;
last_block_x = block_x;
block_x += render->width + render->x_offset + render->x_append + get_sep_offset(block) + sep_offset_remainder;
if (statusline_x <= block_x && statusline_x >= last_block_x) {
const int relative_x = statusline_x - last_block_x;
if (relative_x >= 0 && (uint32_t)relative_x <= render->width) {
send_block_clicked(event->detail, block->name, block->instance,
event->root_x, event->root_y, statusline_x - last_block_x, event->event_y, block_x - last_block_x, bar_height);
event->root_x, event->root_y, relative_x, event->event_y, render->width, bar_height,
event->state);
return;
}
sep_offset_remainder = block->sep_block_width - get_sep_offset(block);
last_block_x += render->width + render->x_append + render->x_offset + block->sep_block_width;
}
}
}
@ -604,7 +605,7 @@ void handle_button(xcb_button_press_event_t *event) {
const size_t len = namelen + strlen("workspace \"\"") + 1;
char *buffer = scalloc(len + num_quotes, 1);
strncpy(buffer, "workspace \"", strlen("workspace \""));
memcpy(buffer, "workspace \"", strlen("workspace \""));
size_t inpos, outpos;
for (inpos = 0, outpos = strlen("workspace \"");
inpos < namelen;
@ -694,21 +695,12 @@ static void handle_client_message(xcb_client_message_event_t *event) {
if (event->type == atoms[I3_SYNC]) {
xcb_window_t window = event->data.data32[0];
uint32_t rnd = event->data.data32[1];
DLOG("[i3 sync protocol] Forwarding random value %d, X11 window 0x%08x to i3\n", rnd, window);
void *reply = scalloc(32, 1);
xcb_client_message_event_t *ev = reply;
ev->response_type = XCB_CLIENT_MESSAGE;
ev->window = window;
ev->type = atoms[I3_SYNC];
ev->format = 32;
ev->data.data32[0] = window;
ev->data.data32[1] = rnd;
xcb_send_event(conn, false, xcb_root, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char *)ev);
xcb_flush(conn);
free(reply);
/* Forward the request to i3 via the IPC interface so that all pending
* IPC messages are guaranteed to be handled. */
char *payload = NULL;
sasprintf(&payload, "{\"rnd\":%d, \"window\":%d}", rnd, window);
i3_send_msg(I3_IPC_MESSAGE_TYPE_SYNC, payload);
free(payload);
} else if (event->type == atoms[_NET_SYSTEM_TRAY_OPCODE] &&
event->format == 32) {
DLOG("_NET_SYSTEM_TRAY_OPCODE received\n");
@ -770,58 +762,16 @@ static void handle_client_message(xcb_client_message_event_t *event) {
}
DLOG("X window %08x requested docking\n", client);
i3_output *output = NULL;
i3_output *walk = NULL;
tray_output_t *tray_output = NULL;
/* We need to iterate through the tray_output assignments first in
* order to prioritize them. Otherwise, if this bar manages two
* outputs and both are assigned as tray_output as well, the first
* output in our list would receive the tray rather than the first
* one defined via tray_output. */
TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
if (strcasecmp(walk->name, tray_output->output) == 0) {
DLOG("Found tray_output assignment for output %s.\n", walk->name);
output = walk;
break;
}
if (walk->primary && strcasecmp("primary", tray_output->output) == 0) {
DLOG("Found tray_output assignment on primary output %s.\n", walk->name);
output = walk;
break;
}
}
/* If we found an output, we're done. */
if (output != NULL)
break;
}
/* If no tray_output has been specified, we fall back to the first
* available output. */
if (output == NULL && TAILQ_EMPTY(&(config.tray_outputs))) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("Falling back to output %s because no primary output is configured\n", walk->name);
output = walk;
break;
}
}
if (output == NULL) {
ELOG("No output found\n");
if (output_for_tray == NULL) {
ELOG("No output found for tray\n");
return;
}
xcb_void_cookie_t rcookie = xcb_reparent_window(xcb_connection,
client,
output->bar.id,
output->rect.w - icon_size - logical_px(config.tray_padding),
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?"))
return;
@ -848,7 +798,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
ev->format = 32;
ev->data.data32[0] = XCB_CURRENT_TIME;
ev->data.data32[1] = XEMBED_EMBEDDED_NOTIFY;
ev->data.data32[2] = output->bar.id;
ev->data.data32[2] = output_for_tray->bar.id;
ev->data.data32[3] = xe_version;
xcb_send_event(xcb_connection,
0,
@ -868,7 +818,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
tc->win = client;
tc->xe_version = xe_version;
tc->mapped = false;
TAILQ_INSERT_TAIL(output->trayclients, tc, tailq);
TAILQ_INSERT_TAIL(output_for_tray->trayclients, tc, tailq);
if (map_it) {
DLOG("Mapping dock client\n");
@ -1092,7 +1042,7 @@ static void handle_resize_request(xcb_resize_request_event_t *event) {
* events from X11, handle them, then flush our outgoing queue.
*
*/
void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
static void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
xcb_generic_event_t *event;
if (xcb_connection_has_error(xcb_connection)) {
@ -1117,49 +1067,18 @@ void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
DLOG("received an xkb event\n");
xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
if (state->xkbType == XCB_XKB_STATE_NOTIFY && config.modifier != XCB_NONE) {
int modstate = state->mods & config.modifier;
#define DLOGMOD(modmask, status) \
do { \
switch (modmask) { \
case ShiftMask: \
DLOG("ShiftMask got " #status "!\n"); \
break; \
case ControlMask: \
DLOG("ControlMask got " #status "!\n"); \
break; \
case Mod1Mask: \
DLOG("Mod1Mask got " #status "!\n"); \
break; \
case Mod2Mask: \
DLOG("Mod2Mask got " #status "!\n"); \
break; \
case Mod3Mask: \
DLOG("Mod3Mask got " #status "!\n"); \
break; \
case Mod4Mask: \
DLOG("Mod4Mask got " #status "!\n"); \
break; \
case Mod5Mask: \
DLOG("Mod5Mask got " #status "!\n"); \
break; \
} \
} while (0)
if (modstate != mod_pressed) {
if (modstate == 0) {
DLOGMOD(config.modifier, released);
if (!activated_mode)
hide_bars();
} else {
DLOGMOD(config.modifier, pressed);
const uint32_t mod = (config.modifier & 0xFFFF);
const bool new_mod_pressed = (mod != 0 && (state->mods & mod) == mod);
if (new_mod_pressed != mod_pressed) {
mod_pressed = new_mod_pressed;
if (state->xkbType == XCB_XKB_STATE_NOTIFY && config.modifier != XCB_NONE) {
if (mod_pressed) {
activated_mode = false;
unhide_bars();
} else if (!activated_mode) {
hide_bars();
}
mod_pressed = modstate;
}
#undef DLOGMOD
}
free(event);
@ -1223,7 +1142,7 @@ void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
* are triggered
*
*/
void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
static void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
}
/*
@ -1231,7 +1150,7 @@ void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
* depend on 'config'.
*
*/
char *init_xcb_early() {
char *init_xcb_early(void) {
/* FIXME: xcb_connect leaks memory */
xcb_connection = xcb_connect(NULL, &screen);
if (xcb_connection_has_error(xcb_connection)) {
@ -1294,7 +1213,7 @@ char *init_xcb_early() {
* in xcb.
*
*/
void register_xkb_keyevents() {
static void register_xkb_keyevents(void) {
const xcb_query_extension_reply_t *extreply;
extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
if (!extreply->present) {
@ -1318,7 +1237,7 @@ void register_xkb_keyevents() {
* Deregister from xkb keyevents.
*
*/
void deregister_xkb_keyevents() {
static void deregister_xkb_keyevents(void) {
xcb_xkb_select_events(conn,
XCB_XKB_ID_USE_CORE_KBD,
0,
@ -1384,7 +1303,7 @@ static void send_tray_clientmessage(void) {
* atom. Afterwards, tray clients will send ClientMessages to our window.
*
*/
void init_tray(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];
@ -1613,7 +1532,7 @@ void destroy_window(i3_output *output) {
/* Strut partial tells i3 where to reserve space for i3bar. This is determined
* by the `position` bar config directive. */
xcb_void_cookie_t config_strut_partial(i3_output *output) {
static xcb_void_cookie_t config_strut_partial(i3_output *output) {
/* A local struct to save the strut_partial property */
struct {
uint32_t left;
@ -1655,6 +1574,56 @@ xcb_void_cookie_t config_strut_partial(i3_output *output) {
&strut_partial);
}
/*
* Returns the output which should hold the tray, if one exists.
*
* An output is returned in these scenarios:
* 1. A specific output was listed in tray_outputs which is also in the list
* of outputs managed by this bar.
* 2. No tray_output directive was specified. In this case, we use the first
* available output.
* 3. 'tray_output primary' was specified. In this case we use the primary
* output.
*
* Three scenarios in which we specifically don't want to use a tray:
* 1. 'tray_output none' was specified.
* 2. A specific output was listed as a tray_output, but is not one of the
* outputs managed by this bar. For example, consider tray_outputs == [VGA-1],
* but outputs == [HDMI-1].
* 3. 'tray_output primary' was specified and no output in the list is
* primary.
*/
static i3_output *get_tray_output(void) {
i3_output *output = NULL;
if (TAILQ_EMPTY(&(config.tray_outputs))) {
/* No tray_output specified, use first active output. */
SLIST_FOREACH(output, outputs, slist) {
if (output->active) {
return output;
}
}
return NULL;
} else if (strcasecmp(TAILQ_FIRST(&(config.tray_outputs))->output, "none") == 0) {
/* Check for "tray_output none" */
return NULL;
}
/* If one or more tray_output assignments were specified, we ensure that at
* least one of them is actually an output managed by this instance. */
tray_output_t *tray_output;
TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
SLIST_FOREACH(output, outputs, slist) {
if (output->active &&
(strcasecmp(output->name, tray_output->output) == 0 ||
(strcasecmp(tray_output->output, "primary") == 0 && output->primary))) {
return output;
}
}
}
return NULL;
}
/*
* Reconfigure all bars and create new bars for recently activated outputs
*
@ -1662,7 +1631,6 @@ xcb_void_cookie_t config_strut_partial(i3_output *output) {
void reconfig_windows(bool redraw_bars) {
uint32_t mask;
uint32_t values[6];
static bool tray_configured = false;
i3_output *walk;
SLIST_FOREACH(walk, outputs, slist) {
@ -1790,58 +1758,6 @@ void reconfig_windows(bool redraw_bars) {
exit(EXIT_FAILURE);
}
/* Unless "tray_output none" was specified, we need to initialize the tray. */
bool no_tray = false;
if (!(TAILQ_EMPTY(&(config.tray_outputs)))) {
no_tray = strcasecmp(TAILQ_FIRST(&(config.tray_outputs))->output, "none") == 0;
}
/*
* There are three scenarios in which we need to initialize the tray:
* 1. A specific output was listed in tray_outputs which is also
* in the list of outputs managed by this bar.
* 2. No tray_output directive was specified. In this case, we
* use the first available output.
* 3. 'tray_output primary' was specified. In this case we use the
* primary output.
*
* Three scenarios in which we specifically don't want to
* initialize the tray are:
* 1. 'tray_output none' was specified.
* 2. A specific output was listed as a tray_output, but is not
* one of the outputs managed by this bar. For example, consider
* tray_outputs == [VGA-1], but outputs == [HDMI-1].
* 3. 'tray_output primary' was specified and no output in the list
* is primary.
*/
if (!tray_configured && !no_tray) {
/* If no tray_output was specified, we go ahead and initialize the tray as
* we will be using the first available output. */
if (TAILQ_EMPTY(&(config.tray_outputs))) {
init_tray();
}
/* If one or more tray_output assignments were specified, we ensure that at least one of
* them is actually an output managed by this instance. */
tray_output_t *tray_output;
TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
i3_output *output;
bool found = false;
SLIST_FOREACH(output, outputs, slist) {
if (strcasecmp(output->name, tray_output->output) == 0 ||
(strcasecmp(tray_output->output, "primary") == 0 && output->primary)) {
found = true;
init_tray();
break;
}
}
if (found)
break;
}
tray_configured = true;
}
} else {
/* We already have a bar, so we just reconfigure it */
mask = XCB_CONFIG_WINDOW_X |
@ -1935,6 +1851,19 @@ void reconfig_windows(bool redraw_bars) {
}
}
}
/* Finally, check if we want to initialize the tray or destroy the selection
* window. The result of get_tray_output() is cached. */
output_for_tray = get_tray_output();
if (output_for_tray) {
if (selwin == XCB_NONE) {
init_tray();
}
} else if (selwin != XCB_NONE) {
DLOG("Destroying tray selection window\n");
xcb_destroy_window(xcb_connection, selwin);
selwin = XCB_NONE;
}
}
/*
@ -2048,7 +1977,8 @@ void draw_bars(bool unhide) {
DLOG("Printing statusline!\n");
int tray_width = get_tray_width(outputs_walk->trayclients);
uint32_t max_statusline_width = outputs_walk->rect.w - workspace_width - tray_width - 2 * logical_px(sb_hoff_px);
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;
@ -2062,7 +1992,7 @@ void draw_bars(bool unhide) {
}
int16_t visible_statusline_width = MIN(statusline_width, max_statusline_width);
int x_dest = outputs_walk->rect.w - tray_width - logical_px(sb_hoff_px) - visible_statusline_width;
int x_dest = outputs_walk->rect.w - tray_width - logical_px((tray_width > 0) * sb_hoff_px) - visible_statusline_width;
draw_statusline(outputs_walk, clip_left, use_focus_colors, use_short_text);
draw_util_copy_surface(&outputs_walk->statusline_buffer, &outputs_walk->buffer, 0, 0,
@ -2110,5 +2040,4 @@ void set_current_mode(struct mode *current) {
I3STRING_FREE(binding.name);
binding = *current;
activated_mode = binding.name != NULL;
return;
}

View File

@ -82,4 +82,5 @@
#include "fake_outputs.h"
#include "display_version.h"
#include "restore_layout.h"
#include "sync.h"
#include "main.h"

View File

@ -8,6 +8,7 @@ xmacro(_NET_WM_STATE_FULLSCREEN)
xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
xmacro(_NET_WM_STATE_MODAL)
xmacro(_NET_WM_STATE_HIDDEN)
xmacro(_NET_WM_STATE_FOCUSED)
xmacro(_NET_WM_STATE)
xmacro(_NET_WM_WINDOW_TYPE)
xmacro(_NET_WM_WINDOW_TYPE_NORMAL)

View File

@ -17,3 +17,4 @@ xmacro(I3_FLOATING_WINDOW)
xmacro(_NET_REQUEST_FRAME_EXTENTS)
xmacro(_NET_FRAME_EXTENTS)
xmacro(_MOTIF_WM_HINTS)
xmacro(WM_CHANGE_STATE)

View File

@ -216,7 +216,7 @@ void cmd_sticky(I3_CMD, const char *action);
* Implementation of 'move <direction> [<pixels> [px]]'.
*
*/
void cmd_move_direction(I3_CMD, const char *direction, long move_px);
void cmd_move_direction(I3_CMD, const char *direction_str, long move_px);
/**
* Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
@ -264,7 +264,7 @@ void cmd_focus_output(I3_CMD, const char *name);
* Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
*
*/
void cmd_move_window_to_position(I3_CMD, const char *method, long x, long y);
void cmd_move_window_to_position(I3_CMD, long x, long y);
/**
* Implementation of 'move [window|container] [to] [absolute] position center
@ -314,13 +314,13 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name);
*/
void cmd_bar(I3_CMD, const char *bar_type, const char *bar_value, const char *bar_id);
/*
/**
* Implementation of 'shmlog <size>|toggle|on|off'
*
*/
void cmd_shmlog(I3_CMD, const char *argument);
/*
/**
* Implementation of 'debuglog toggle|on|off'
*
*/

View File

@ -13,7 +13,7 @@
#include <yajl/yajl_gen.h>
/*
/**
* Holds an intermediate represenation of the result of a call to any command.
* When calling parse_command("floating enable, border none"), the parser will
* internally use this struct when calling cmd_floating and cmd_border.

View File

@ -20,7 +20,8 @@
*/
Con *con_new_skeleton(Con *parent, i3Window *window);
/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
/**
* A wrapper for con_new_skeleton, to retain the old con_new behaviour
*
*/
Con *con_new(Con *parent, i3Window *window);
@ -120,6 +121,14 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation);
*/
Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode);
/**
* Returns the fullscreen node that covers the given workspace if it exists.
* This is either a CF_GLOBAL fullscreen container anywhere or a CF_OUTPUT
* fullscreen container in the workspace.
*
*/
Con *con_get_fullscreen_covering_ws(Con *ws);
/**
* Returns true if the container is internal, such as __i3_scratch
*
@ -212,7 +221,7 @@ void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode);
*/
void con_mark(Con *con, const char *mark, mark_mode_t mode);
/*
/**
* Removes marks from containers.
* If con is NULL, all containers are considered.
* If name is NULL, this removes all existing marks.
@ -394,7 +403,7 @@ Con *con_descend_focused(Con *con);
*/
Con *con_descend_tiling_focused(Con *con);
/*
/**
* Returns the leftmost, rightmost, etc. container in sub-tree. For example, if
* direction is D_LEFT, then we return the rightmost container and if direction
* is D_RIGHT, we return the leftmost container. This is because if we are

View File

@ -56,12 +56,14 @@ CFGFUN(disable_randr15, const char *value);
CFGFUN(fake_outputs, const char *outputs);
CFGFUN(force_display_urgency_hint, const long duration_ms);
CFGFUN(focus_on_window_activation, const char *mode);
CFGFUN(title_align, const char *alignment);
CFGFUN(show_marks, const char *value);
CFGFUN(hide_edge_borders, const char *borders);
CFGFUN(assign_output, const char *output);
CFGFUN(assign, const char *workspace, bool is_number);
CFGFUN(no_focus);
CFGFUN(ipc_socket, const char *path);
CFGFUN(ipc_kill_timeout, const long timeout_ms);
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);
@ -81,7 +83,7 @@ CFGFUN(bar_hidden_state, const char *hidden_state);
CFGFUN(bar_id, const char *bar_id);
CFGFUN(bar_output, const char *output);
CFGFUN(bar_verbose, const char *verbose);
CFGFUN(bar_modifier, const char *modifier);
CFGFUN(bar_modifier, const char *modifiers);
CFGFUN(bar_wheel_up_cmd, const char *command);
CFGFUN(bar_wheel_down_cmd, const char *command);
CFGFUN(bar_bindsym, const char *button, const char *release, const char *command);
@ -96,5 +98,6 @@ CFGFUN(bar_status_command, const char *command);
CFGFUN(bar_binding_mode_indicator, const char *value);
CFGFUN(bar_workspace_buttons, const char *value);
CFGFUN(bar_strip_workspace_numbers, const char *value);
CFGFUN(bar_strip_workspace_name, const char *value);
CFGFUN(bar_start);
CFGFUN(bar_finish);

View File

@ -16,7 +16,7 @@
SLIST_HEAD(variables_head, Variable);
extern pid_t config_error_nagbar_pid;
/*
/**
* An intermediate reprsentation of the result of a parse_config call.
* Currently unused, but the JSON output will be useful in the future when we
* implement a config parsing IPC command.

View File

@ -201,6 +201,13 @@ struct Config {
* decoration. Marks starting with a "_" will be ignored either way. */
bool show_marks;
/** Title alignment options. */
enum {
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT
} title_align;
/** The default border style for new windows. */
border_style_t default_border;
@ -289,16 +296,7 @@ struct Barconfig {
S_SHOW = 1 } hidden_state;
/** Bar modifier (to show bar when in hide mode). */
enum {
M_NONE = 0,
M_CONTROL = 1,
M_SHIFT = 2,
M_MOD1 = 3,
M_MOD2 = 4,
M_MOD3 = 5,
M_MOD4 = 6,
M_MOD5 = 7
} modifier;
uint32_t modifier;
TAILQ_HEAD(bar_bindings_head, Barbinding)
bar_bindings;
@ -331,6 +329,10 @@ struct Barconfig {
* 'strip_workspace_numbers yes'. */
bool strip_workspace_numbers;
/** Strip workspace name? Configuration option is
* 'strip_workspace_name yes'. */
bool strip_workspace_name;
/** Hide mode button? Configuration option is 'binding_mode_indicator no'
* but we invert the bool for the same reason as hide_workspace_buttons.*/
bool hide_binding_mode_indicator;
@ -432,7 +434,7 @@ void ungrab_all_keys(xcb_connection_t *conn);
* Sends the current bar configuration as an event to all barconfig_update listeners.
*
*/
void update_barconfig();
void update_barconfig(void);
/**
* Kills the configerror i3-nagbar process, if any.

View File

@ -477,6 +477,10 @@ struct Window {
int min_width;
int min_height;
/* Maximum size specified for the window. */
int max_width;
int max_height;
/* aspect ratio from WM_NORMAL_HINTS (MPlayer uses this for example) */
double aspect_ratio;
};
@ -573,7 +577,7 @@ struct Assignment {
/** the criteria to check if a window matches */
Match match;
/** destination workspace/command, depending on the type */
/** destination workspace/command/output, depending on the type */
union {
char *command;
char *workspace;

View File

@ -83,6 +83,12 @@ void ewmh_update_client_list_stacking(xcb_window_t *stack, int num_windows);
*/
void ewmh_update_sticky(xcb_window_t window, bool sticky);
/**
* Set or remove _NEW_WM_STATE_FOCUSED on the window.
*
*/
void ewmh_update_focused(xcb_window_t window, bool is_focused);
/**
* Set up the EWMH hints on the root window.
*

View File

@ -143,7 +143,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event,
* outputs.
*
*/
void floating_reposition(Con *con, Rect newrect);
bool floating_reposition(Con *con, Rect newrect);
/**
* Sets size of the CT_FLOATING_CON to specified dimensions. Might limit the

View File

@ -63,6 +63,9 @@ typedef struct i3_ipc_header {
/** Send a tick event to all subscribers. */
#define I3_IPC_MESSAGE_TYPE_SEND_TICK 10
/** Trigger an i3 sync protocol message via IPC. */
#define I3_IPC_MESSAGE_TYPE_SYNC 11
/*
* Messages from i3 to clients
*
@ -78,12 +81,13 @@ typedef struct i3_ipc_header {
#define I3_IPC_REPLY_TYPE_BINDING_MODES 8
#define I3_IPC_REPLY_TYPE_CONFIG 9
#define I3_IPC_REPLY_TYPE_TICK 10
#define I3_IPC_REPLY_TYPE_SYNC 11
/*
* Events from i3 to clients. Events have the first bit set high.
*
*/
#define I3_IPC_EVENT_MASK (1 << 31)
#define I3_IPC_EVENT_MASK (1UL << 31)
/* The workspace event will be triggered upon changes in the workspace list */
#define I3_IPC_EVENT_WORKSPACE (I3_IPC_EVENT_MASK | 0)

View File

@ -35,6 +35,11 @@ typedef struct ipc_client {
* event has been sent by i3. */
bool first_tick_sent;
struct ev_io *callback;
struct ev_timer *timeout;
uint8_t *buffer;
size_t buffer_size;
TAILQ_ENTRY(ipc_client)
clients;
} ipc_client;
@ -124,3 +129,9 @@ void ipc_send_barconfig_update_event(Barconfig *barconfig);
* For the binding events, we send the serialized binding struct.
*/
void ipc_send_binding_event(const char *event_type, Binding *bind);
/**
* Set the maximum duration that we allow for a connection with an unwriteable
* socket.
*/
void ipc_set_kill_timeout(ev_tstamp new);

View File

@ -166,6 +166,14 @@ int sasprintf(char **strp, const char *fmt, ...);
*/
ssize_t writeall(int fd, const void *buf, size_t count);
/**
* Like writeall, but instead of retrying upon EAGAIN (returned when a write
* would block), the function stops and returns the total number of bytes
* written so far.
*
*/
ssize_t writeall_nonblock(int fd, const void *buf, size_t count);
/**
* Safe-wrapper around writeall which exits if it returns -1 (meaning that
* write failed)
@ -188,11 +196,11 @@ i3String *i3string_from_markup(const char *from_markup);
/**
* Build an i3String from an UTF-8 encoded string with fixed length.
* To be used when no proper NUL-terminaison is available.
* To be used when no proper NULL-termination is available.
* Returns the newly-allocated i3String.
*
*/
i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes);
i3String *i3string_from_utf8_with_length(const char *from_utf8, ssize_t num_bytes);
/**
* Build an i3String from an UTF-8 encoded string in Pango markup with fixed
@ -312,6 +320,11 @@ int ipc_recv_message(int sockfd, uint32_t *message_type,
*/
void fake_configure_notify(xcb_connection_t *conn, xcb_rectangle_t r, xcb_window_t window, int border_width);
#define HAS_G_UTF8_MAKE_VALID GLIB_CHECK_VERSION(2, 52, 0)
#if !HAS_G_UTF8_MAKE_VALID
gchar *g_utf8_make_valid(const gchar *str, gssize len);
#endif
/**
* Returns the colorpixel to use for the given hex color (think of HTML). Only
* works for true-color (vast majority of cases) at the moment, avoiding a
@ -330,7 +343,7 @@ uint32_t get_colorpixel(const char *hex) __attribute__((const));
#if defined(__APPLE__)
/*
/**
* Taken from FreeBSD
* Returns a pointer to a new string which is a duplicate of the
* string, but only copies at most n characters.
@ -459,7 +472,7 @@ xcb_visualtype_t *get_visualtype(xcb_screen_t *screen);
* release version), based on the git version number.
*
*/
bool is_debug_build() __attribute__((const));
bool is_debug_build(void) __attribute__((const));
/**
* Returns the name of a temporary file with the specified prefix.
@ -506,11 +519,11 @@ int logical_px(const int logical);
char *resolve_tilde(const char *path);
/**
* Get the path of the first configuration file found. If override_configpath
* is specified, that path is returned and saved for further calls. Otherwise,
* checks the home directory first, then the system directory first, always
* taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
* $XDG_CONFIG_DIRS)
* Get the path of the first configuration file found. If override_configpath is
* specified, that path is returned and saved for further calls. Otherwise,
* checks the home directory first, then the system directory, always taking
* into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
* $XDG_CONFIG_DIRS).
*
*/
char *get_config_path(const char *override_configpath, bool use_system_paths);

View File

@ -15,7 +15,7 @@
#include <config.h>
/*
/**
* Initializes the Match data structure. This function is necessary because the
* members representing boolean values (like dock) need to be initialized with
* -1 instead of 0.

View File

@ -17,3 +17,13 @@
*
*/
void tree_move(Con *con, int direction);
typedef enum { BEFORE,
AFTER } position_t;
/**
* This function detaches 'con' from its parent and inserts it either before or
* after 'target'.
*
*/
void insert_con_into(Con *con, Con *target, position_t position);

View File

@ -40,5 +40,12 @@ Output *get_output_for_con(Con *con);
* Iterates over all outputs and pushes sticky windows to the currently visible
* workspace on that output.
*
* old_focus is used to determine if a sticky window is going to be focused.
* old_focus might be different than the currently focused container because the
* caller might need to temporarily change the focus and then call
* output_push_sticky_windows. For example, workspace_show needs to set focus to
* one of its descendants first, then call output_push_sticky_windows that
* should focus a sticky window if it was the focused in the previous workspace.
*
*/
void output_push_sticky_windows(Con *to_focus);
void output_push_sticky_windows(Con *old_focus);

View File

@ -88,6 +88,14 @@ Output *get_output_by_name(const char *name, const bool require_active);
*/
Output *get_output_containing(unsigned int x, unsigned int y);
/**
* Returns the active output which contains the midpoint of the given rect. If
* such an output doesn't exist, returns the output which contains most of the
* rectangle or NULL if there is no output which intersects with it.
*
*/
Output *get_output_from_rect(Rect rect);
/**
* Returns the active output which spans exactly the area specified by
* rect or NULL if there is no output like this.
@ -95,15 +103,14 @@ Output *get_output_containing(unsigned int x, unsigned int y);
*/
Output *get_output_with_dimensions(Rect rect);
/*
* In contained_by_output, we check if any active output contains part of the container.
/**
* In output_containing_rect, we check if any active output contains part of the container.
* We do this by checking if the output rect is intersected by the Rect.
* This is the 2-dimensional counterpart of get_output_containing.
* Since we don't actually need the outputs intersected by the given Rect (There could
* be many), we just return true or false for convenience.
* Returns the output with the maximum intersecting area.
*
*/
bool contained_by_output(Rect rect);
Output *output_containing_rect(Rect rect);
/**
* Gets the output which is the next one in the given direction.
@ -130,7 +137,7 @@ Output *get_output_next(direction_t direction, Output *current, output_close_far
*/
Output *get_output_next_wrap(direction_t direction, Output *current);
/*
/**
* Creates an output covering the root window.
*
*/

View File

@ -12,7 +12,10 @@
#include <config.h>
/* This is used to keep a state to pass around when rendering a con in render_con(). */
/**
* This is used to keep a state to pass around when rendering a con in render_con().
*
*/
typedef struct render_params {
/* A copy of the coordinates of the container which is being rendered. */
int x;
@ -39,7 +42,8 @@ typedef struct render_params {
*/
void render_con(Con *con, bool render_fullscreen);
/*
/**
* Returns the height for the decorations
*
*/
int render_deco_height(void);

View File

@ -13,4 +13,27 @@
bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction, bool both_sides);
int resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event);
void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event);
/**
* Resize the two given containers using the given amount of pixels or
* percentage points. One of the two needs to be 0. A positive amount means
* growing the first container while a negative means shrinking it.
* Returns false when the resize would result in one of the two containers
* having less than 1 pixel of size.
*
*/
bool resize_neighboring_cons(Con *first, Con *second, int px, int ppt);
/**
* Calculate the minimum percent needed for the given container to be at least 1
* pixel.
*
*/
double percent_for_1px(Con *con);
/**
* Calculate the given container's new percent given a change in pixels.
*
*/
double px_resize_to_percent(Con *con, int px_diff);

View File

@ -29,7 +29,7 @@ void scratchpad_move(Con *con);
* can press the same key to quickly look something up).
*
*/
void scratchpad_show(Con *con);
bool scratchpad_show(Con *con);
/**
* When starting i3 initially (and after each change to the connected outputs),

View File

@ -20,7 +20,7 @@
/* Default shmlog size if not set by user. */
extern const int default_shmlog_size;
/*
/**
* Header of the shmlog file. Used by i3/src/log.c and i3/i3-dump-log/main.c.
*
*/

View File

@ -21,7 +21,7 @@
* Starts the given application by passing it through a shell. We use double
* fork to avoid zombie processes. As the started applications parent exits
* (immediately), the application is reparented to init (process-id 1), which
* correctly handles childs, so we dont have to do it :-).
* correctly handles children, so we dont have to do it :-).
*
* The shell used to start applications is the system's bourne shell (i.e.,
* /bin/sh).

14
include/sync.h Normal file
View File

@ -0,0 +1,14 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* sync.c: i3 sync protocol: https://i3wm.org/docs/testsuite.html#i3_sync
*
*/
#pragma once
#include <xcb/xcb.h>
void sync_respond(xcb_window_t window, uint32_t rnd);

View File

@ -78,7 +78,7 @@ void tree_next(char way, orientation_t orientation);
* container) and focus should be set there.
*
*/
bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus);
bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent);
/**
* Loads tree from ~/.i3/_restart.json (used for in-place restarts).

View File

@ -25,9 +25,6 @@
#define STARTS_WITH(string, needle) (strncasecmp((string), (needle), strlen((needle))) == 0)
#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_NEXT(elm, field) : NULL)
#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_PREV(elm, field) : NULL)
#define FOR_TABLE(workspace) \
for (int cols = 0; cols < (workspace)->cols; cols++) \
for (int rows = 0; rows < (workspace)->rows; rows++)
#define NODES_FOREACH(head) \
for (Con *child = (Con *)-1; (child == (Con *)-1) && ((child = 0), true);) \
@ -128,7 +125,7 @@ void i3_restart(bool forget_layout);
#if defined(__OpenBSD__) || defined(__APPLE__)
/*
/**
* Taken from FreeBSD
* Find the first occurrence of the byte string s in byte string l.
*
@ -177,3 +174,9 @@ bool parse_long(const char *str, long *out, int base);
*
*/
ssize_t slurp(const char *path, char **buf);
/**
* Convert a direction to its corresponding orientation.
*
*/
orientation_t orientation_from_direction(direction_t direction);

View File

@ -24,6 +24,27 @@
#define NET_WM_DESKTOP_NONE 0xFFFFFFF0
#define NET_WM_DESKTOP_ALL 0xFFFFFFFF
/**
* Returns the workspace with the given name or NULL if such a workspace does
* not exist.
*
*/
Con *get_existing_workspace_by_name(const char *name);
/**
* Returns the workspace with the given number or NULL if such a workspace does
* not exist.
*
*/
Con *get_existing_workspace_by_num(int num);
/**
* Returns true if the first output assigned to a workspace with the given
* workspace assignment is the same as the given output.
*
*/
bool output_triggers_assignment(Output *output, struct Workspace_Assignment *assignment);
/**
* Returns a pointer to the workspace with the given number (starting at 0),
* creating the workspace if necessary (by allocating the necessary amount of
@ -193,4 +214,4 @@ Con *workspace_encapsulate(Con *ws);
* This returns true if and only if moving the workspace was successful.
*
*/
bool workspace_move_to_output(Con *ws, const char *output);
bool workspace_move_to_output(Con *ws, Output *output);

View File

@ -49,6 +49,12 @@ void x_reinit(Con *con);
*/
void x_con_kill(Con *con);
/*
* Completely reinitializes the container's frame, without destroying the old window.
*
*/
void x_con_reframe(Con *con);
/**
* Returns true if the client supports the given protocol atom (like WM_DELETE_WINDOW)
*

View File

@ -53,6 +53,7 @@
ConfigureNotify */ \
XCB_EVENT_MASK_POINTER_MOTION | \
XCB_EVENT_MASK_PROPERTY_CHANGE | \
XCB_EVENT_MASK_FOCUS_CHANGE | \
XCB_EVENT_MASK_ENTER_WINDOW)
#define xmacro(atom) xcb_atom_t A_##atom;

View File

@ -49,14 +49,12 @@ void init_dpi(void) {
dpi = 0;
goto init_dpi_end;
}
dpi = (long)round(in_dpi);
dpi = lround(in_dpi);
DLOG("Found Xft.dpi = %ld.\n", dpi);
init_dpi_end:
if (resource != NULL) {
free(resource);
}
free(resource);
if (database != NULL) {
xcb_xrm_database_free(database);

View File

@ -121,7 +121,7 @@ static void draw_util_set_source_color(surface_t *surface, color_t color) {
cairo_set_source_rgba(surface->cr, color.red, color.green, color.blue, color.alpha);
}
/**
/*
* Draw the given text using libi3.
* This function also marks the surface dirty which is needed if other means of
* drawing are used. This will be the case when using XCB to draw text.
@ -140,7 +140,7 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_
cairo_surface_mark_dirty(surface->surface);
}
/**
/*
* Draws a filled rectangle.
* This function is a convenience wrapper and takes care of flushing the
* surface as well as restoring the cairo state.
@ -167,7 +167,7 @@ void draw_util_rectangle(surface_t *surface, color_t color, double x, double y,
cairo_restore(surface->cr);
}
/**
/*
* Clears a surface with the given color.
*
*/
@ -191,7 +191,7 @@ void draw_util_clear_surface(surface_t *surface, color_t color) {
cairo_restore(surface->cr);
}
/**
/*
* Copies a surface onto another surface.
*
*/

View File

@ -109,9 +109,8 @@ static void draw_text_pango(const char *text, size_t text_len,
cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue);
pango_cairo_update_layout(cr, layout);
pango_layout_get_pixel_size(layout, NULL, &height);
/* Center the piece of text vertically if its height is smaller than the
* cached font height, and just let "high" symbols fall out otherwise. */
int yoffset = (height < savedFont->height ? 0.5 : 1) * (height - savedFont->height);
/* Center the piece of text vertically. */
int yoffset = (height - savedFont->height) / 2;
cairo_move_to(cr, x, y - yoffset);
pango_cairo_show_layout(cr, layout);
@ -224,9 +223,7 @@ i3Font load_font(const char *pattern, const bool fallback) {
error->error_code);
}
}
if (error != NULL) {
free(error);
}
free(error);
font.pattern = sstrdup(pattern);
LOG("Using X font %s\n", pattern);
@ -275,17 +272,13 @@ void free_font(void) {
case FONT_TYPE_XCB: {
/* Close the font and free the info */
xcb_close_font(conn, savedFont->specific.xcb.id);
if (savedFont->specific.xcb.info)
free(savedFont->specific.xcb.info);
free(savedFont->specific.xcb.info);
break;
}
case FONT_TYPE_PANGO:
/* Free the font description */
pango_font_description_free(savedFont->specific.pango_desc);
break;
default:
assert(false);
break;
}
savedFont = NULL;
@ -315,9 +308,6 @@ void set_font_colors(xcb_gcontext_t gc, color_t foreground, color_t background)
pango_font_green = foreground.green;
pango_font_blue = foreground.blue;
break;
default:
assert(false);
break;
}
}
@ -388,8 +378,6 @@ void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc,
draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
drawable, visual, x, y, max_width, i3string_is_markup(text));
return;
default:
assert(false);
}
}
@ -425,8 +413,6 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable,
draw_text_pango(text, strlen(text),
drawable, root_visual_type, x, y, max_width, false);
return;
default:
assert(false);
}
}
@ -519,8 +505,6 @@ int predict_text_width(i3String *text) {
/* Calculate extents using Pango */
return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
i3string_is_markup(text));
default:
assert(false);
return 0;
}
assert(false);
}

View File

@ -11,8 +11,8 @@
#include <stdint.h>
#include <string.h>
#ifndef STARTS_WITH
#define STARTS_WITH(string, needle) (strncasecmp((string), (needle), strlen((needle))) == 0)
#ifndef CS_STARTS_WITH
#define CS_STARTS_WITH(string, needle) (strncmp((string), (needle), strlen((needle))) == 0)
#endif
/*
@ -28,7 +28,7 @@ char *format_placeholders(char *format, placeholder_t *placeholders, int num) {
int buffer_len = strlen(format) + 1;
for (char *walk = format; *walk != '\0'; walk++) {
for (int i = 0; i < num; i++) {
if (!STARTS_WITH(walk, placeholders[i].name))
if (!CS_STARTS_WITH(walk, placeholders[i].name))
continue;
buffer_len = buffer_len - strlen(placeholders[i].name) + strlen(placeholders[i].value);
@ -48,7 +48,7 @@ char *format_placeholders(char *format, placeholder_t *placeholders, int num) {
bool matched = false;
for (int i = 0; i < num; i++) {
if (!STARTS_WITH(walk, placeholders[i].name)) {
if (!CS_STARTS_WITH(walk, placeholders[i].name)) {
continue;
}

93
libi3/g_utf8_make_valid.c Normal file
View File

@ -0,0 +1,93 @@
/* g_utf8_make_valid.c - Coerce string into UTF-8
*
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "libi3.h"
#include <string.h>
#include <glib.h>
/* Copied from:
* https://gitlab.gnome.org/GNOME/glib/blob/f928dfdf57bf92c883b53b16d7a9d49add504f52/glib/gutf8.c#L1752-1815 */
/* clang-format off */
#if !HAS_G_UTF8_MAKE_VALID
/**
* g_utf8_make_valid:
* @str: string to coerce into UTF-8
* @len: the maximum length of @str to use, in bytes. If @len < 0,
* then the string is nul-terminated.
*
* If the provided string is valid UTF-8, return a copy of it. If not,
* return a copy in which bytes that could not be interpreted as valid Unicode
* are replaced with the Unicode replacement character (U+FFFD).
*
* For example, this is an appropriate function to use if you have received
* a string that was incorrectly declared to be UTF-8, and you need a valid
* UTF-8 version of it that can be logged or displayed to the user, with the
* assumption that it is close enough to ASCII or UTF-8 to be mostly
* readable as-is.
*
* Returns: (transfer full): a valid UTF-8 string whose content resembles @str
*
* Since: 2.52
*/
gchar *
g_utf8_make_valid (const gchar *str,
gssize len)
{
GString *string;
const gchar *remainder, *invalid;
gsize remaining_bytes, valid_bytes;
g_return_val_if_fail (str != NULL, NULL);
if (len < 0)
len = strlen (str);
string = NULL;
remainder = str;
remaining_bytes = len;
while (remaining_bytes != 0)
{
if (g_utf8_validate (remainder, remaining_bytes, &invalid))
break;
valid_bytes = invalid - remainder;
if (string == NULL)
string = g_string_sized_new (remaining_bytes);
g_string_append_len (string, remainder, valid_bytes);
/* append U+FFFD REPLACEMENT CHARACTER */
g_string_append (string, "\357\277\275");
remaining_bytes -= valid_bytes + 1;
remainder = invalid + 1;
}
if (string == NULL)
return g_strndup (str, len);
g_string_append_len (string, remainder, remaining_bytes);
g_string_append_c (string, '\0');
g_assert (g_utf8_validate (string->str, -1, NULL));
return g_string_free (string, FALSE);
}
#endif

View File

@ -43,7 +43,7 @@ uint32_t get_colorpixel(const char *hex) {
/* Shortcut: if our screen is true color, no need to do a roundtrip to X11 */
if (root_screen == NULL || root_screen->root_depth == 24 || root_screen->root_depth == 32) {
return (0xFF << 24) | (r << 16 | g << 8 | b);
return (0xFFUL << 24) | (r << 16 | g << 8 | b);
}
/* Lookup this colorpixel in the cache */
@ -60,8 +60,7 @@ uint32_t get_colorpixel(const char *hex) {
xcb_alloc_color_reply_t *reply;
reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, root_screen->default_colormap,
r16, g16, b16),
reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, root_screen->default_colormap, r16, g16, b16),
NULL);
if (!reply) {

View File

@ -21,11 +21,11 @@ static bool path_exists(const char *path) {
}
/*
* Get the path of the first configuration file found. If override_configpath
* is specified, that path is returned and saved for further calls. Otherwise,
* checks the home directory first, then the system directory first, always
* taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
* $XDG_CONFIG_DIRS)
* Get the path of the first configuration file found. If override_configpath is
* specified, that path is returned and saved for further calls. Otherwise,
* checks the home directory first, then the system directory, always taking
* into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
* $XDG_CONFIG_DIRS).
*
*/
char *get_config_path(const char *override_configpath, bool use_system_paths) {
@ -38,40 +38,41 @@ char *get_config_path(const char *override_configpath, bool use_system_paths) {
return sstrdup(saved_configpath);
}
if (saved_configpath != NULL)
if (saved_configpath != NULL) {
return sstrdup(saved_configpath);
}
/* 1: check the traditional path under the home directory */
config_path = resolve_tilde("~/.i3/config");
if (path_exists(config_path))
return config_path;
free(config_path);
/* 2: check for $XDG_CONFIG_HOME/i3/config */
if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
/* 1: check for $XDG_CONFIG_HOME/i3/config */
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);
free(xdg_config_home);
if (path_exists(config_path))
if (path_exists(config_path)) {
return config_path;
}
free(config_path);
/* 2: check the traditional path under the home directory */
config_path = resolve_tilde("~/.i3/config");
if (path_exists(config_path)) {
return config_path;
}
free(config_path);
/* The below paths are considered system-level, and can be skipped if the
* caller only wants user-level configs. */
if (!use_system_paths)
if (!use_system_paths) {
return NULL;
}
/* 3: check the traditional path under /etc */
config_path = SYSCONFDIR "/i3/config";
if (path_exists(config_path))
return sstrdup(config_path);
/* 4: check for $XDG_CONFIG_DIRS/i3/config */
if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL)
/* 3: check for $XDG_CONFIG_DIRS/i3/config */
if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL) {
xdg_config_dirs = SYSCONFDIR "/xdg";
}
char *buf = sstrdup(xdg_config_dirs);
char *tok = strtok(buf, ":");
@ -88,5 +89,11 @@ char *get_config_path(const char *override_configpath, bool use_system_paths) {
}
free(buf);
/* 4: check the traditional path under /etc */
config_path = SYSCONFDIR "/i3/config";
if (path_exists(config_path)) {
return sstrdup(config_path);
}
return NULL;
}

View File

@ -15,7 +15,7 @@
* release version), based on the git version number.
*
*/
bool is_debug_build() {
bool is_debug_build(void) {
/* i3_version contains either something like this:
* "4.0.2 (2011-11-11, branch "release")".
* or: "4.0.2-123-gCOFFEEBABE (2011-11-11, branch "next")".

View File

@ -44,10 +44,7 @@ int mkdirp(const char *path, mode_t mode) {
char *sep = strrchr(copy, '/');
if (sep == NULL) {
if (copy != NULL) {
free(copy);
copy = NULL;
}
free(copy);
return -1;
}
*sep = '\0';

View File

@ -35,9 +35,10 @@ char *resolve_tilde(const char *path) {
} else {
head = globbuf.gl_pathv[0];
result = scalloc(strlen(head) + (tail ? strlen(tail) : 0) + 1, 1);
strncpy(result, head, strlen(head));
if (tail)
strncat(result, tail, strlen(tail));
strcpy(result, head);
if (tail) {
strcat(result, tail);
}
}
globfree(&globbuf);

View File

@ -68,10 +68,9 @@ int sasprintf(char **strp, const char *fmt, ...) {
ssize_t writeall(int fd, const void *buf, size_t count) {
size_t written = 0;
ssize_t n = 0;
while (written < count) {
n = write(fd, buf + written, count - written);
const ssize_t n = write(fd, ((char *)buf) + written, count - written);
if (n == -1) {
if (errno == EINTR || errno == EAGAIN)
continue;
@ -83,6 +82,25 @@ ssize_t writeall(int fd, const void *buf, size_t count) {
return written;
}
ssize_t writeall_nonblock(int fd, const void *buf, size_t count) {
size_t written = 0;
while (written < count) {
const ssize_t n = write(fd, ((char *)buf) + written, count - written);
if (n == -1) {
if (errno == EAGAIN) {
return written;
} else if (errno == EINTR) {
continue;
} else {
return n;
}
}
written += (size_t)n;
}
return written;
}
ssize_t swrite(int fd, const void *buf, size_t count) {
ssize_t n;

View File

@ -30,15 +30,7 @@ struct _i3String {
*
*/
i3String *i3string_from_utf8(const char *from_utf8) {
i3String *str = scalloc(1, sizeof(i3String));
/* Get the text */
str->utf8 = sstrdup(from_utf8);
/* Compute and store the length */
str->num_bytes = strlen(str->utf8);
return str;
return i3string_from_utf8_with_length(from_utf8, -1);
}
/*
@ -56,20 +48,18 @@ i3String *i3string_from_markup(const char *from_markup) {
/*
* Build an i3String from an UTF-8 encoded string with fixed length.
* To be used when no proper NUL-terminaison is available.
* To be used when no proper NULL-termination is available.
* Returns the newly-allocated i3String.
*
*/
i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes) {
i3String *i3string_from_utf8_with_length(const char *from_utf8, ssize_t num_bytes) {
i3String *str = scalloc(1, sizeof(i3String));
/* Copy the actual text to our i3String */
str->utf8 = scalloc(num_bytes + 1, 1);
strncpy(str->utf8, from_utf8, num_bytes);
str->utf8[num_bytes] = '\0';
/* g_utf8_make_valid NULL-terminates the string. */
str->utf8 = g_utf8_make_valid(from_utf8, num_bytes);
/* Store the length */
str->num_bytes = num_bytes;
/* num_bytes < 0 means NULL-terminated string, need to calculate length */
str->num_bytes = num_bytes < 0 ? strlen(str->utf8) : (size_t)num_bytes;
return str;
}
@ -109,7 +99,7 @@ i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs) {
return str;
}
/**
/*
* Copies the given i3string.
* Note that this will not free the source string.
*/

View File

@ -83,8 +83,7 @@ xcb_char2b_t *convert_utf8_to_ucs2(char *input, size_t *real_strlen) {
}
/* Do the conversion */
size_t rc = iconv(ucs2_conversion_descriptor, (char **)&input,
&input_size, (char **)&output, &output_size);
size_t rc = iconv(ucs2_conversion_descriptor, &input, &input_size, (char **)&output, &output_size);
if (rc == (size_t)-1) {
perror("Converting to UCS-2 failed");
free(buffer);

View File

@ -9,7 +9,21 @@ i3-config-wizard - creates a keysym based config based on your layout
== SYNOPSIS
i3-config-wizard
i3-config-wizard [*-s* 'socket'] [*-m* 'modifier'] [*-v*] [*-h*]
== OPTIONS
*-s, --socket* 'socket'::
Overwrites the path to the i3 IPC socket.
*-m, --modifier* 'modifier'::
Generates the configuration file headlessly. Accepts win or alt.
*-v, --version*::
Display version number and exit.
*-h, --help*::
Display a short help message and exit.
== FILES

View File

@ -1,5 +1,5 @@
i3-input(1)
=========
===========
Michael Stapelberg <michael+i3@stapelberg.de>
v4.1.2, April 2012

View File

@ -31,6 +31,11 @@ with an error.
*-t* 'type'::
Send ipc message, see below. This option defaults to "command".
*-m*, *--monitor*::
Instead of exiting right after receiving the first subscribed event,
wait indefinitely for all of them. Can only be used with "-t subscribe".
See the "subscribe" IPC message type below for details.
*message*::
Send ipc message, see below.
@ -69,6 +74,17 @@ get_version::
Gets the version of i3. The reply will be a JSON-encoded dictionary with the
major, minor, patch and human-readable version.
get_config::
Gets the currently loaded i3 configuration.
send_tick::
Sends a tick to all IPC connections which subscribe to tick events.
subscribe::
The payload of the message describes the events to subscribe to.
Upon reception, each event will be dumped as a JSON-encoded object.
See the -m option for continuous monitoring.
== DESCRIPTION
i3-msg is a sample implementation for a client using the unix socket IPC
@ -85,6 +101,9 @@ i3-msg border normal
# Dump the layout tree
i3-msg -t get_tree
# Monitor window changes
i3-msg -t subscribe -m '[ "window" ]'
------------------------------------------------
== ENVIRONMENT

View File

@ -9,7 +9,7 @@ i3-nagbar - displays an error bar on top of your screen
== SYNOPSIS
i3-nagbar [-m <message>] [-b <button> <action>] [-t warning|error] [-f <font>] [-v]
i3-nagbar [-m <message>] [-b <button> <action>] [-B <button> <action>] [-t warning|error] [-f <font>] [-v]
== OPTIONS
@ -32,6 +32,12 @@ Select font that is being used.
*-b, --button* 'button' 'action'::
Create a button with text 'button'. The 'action' are the shell commands that
will be executed by this button. Multiple buttons can be defined.
Will launch the shell commands inside a terminal emulator, using
i3-sensible-terminal.
*-B, --button-no-terminal* 'button' 'action'::
Same as above, but will execute the shell commands directly, without launching a
terminal emulator.
== DESCRIPTION

View File

@ -1,5 +1,5 @@
i3-sensible-editor(1)
===================
=====================
Michael Stapelberg <michael+i3@stapelberg.de>
v4.1, November 2011

View File

@ -1,5 +1,5 @@
i3-sensible-pager(1)
===================
====================
Michael Stapelberg <michael+i3@stapelberg.de>
v4.1, November 2011

View File

@ -47,6 +47,8 @@ It tries to start one of the following (in that order):
* kitty
* guake
* tilda
* alacritty
* hyper
Please dont complain about the order: If the user has any preference, they will
have $TERMINAL set or modified their i3 configuration file.

View File

@ -170,10 +170,10 @@ Exits i3.
When starting, i3 looks for configuration files in the following order:
1. ~/.i3/config
2. ~/.config/i3/config (or $XDG_CONFIG_HOME/i3/config if set)
3. /etc/i3/config
4. /etc/xdg/i3/config (or $XDG_CONFIG_DIRS/i3/config if set)
1. ~/.config/i3/config (or $XDG_CONFIG_HOME/i3/config if set)
2. ~/.i3/config
3. /etc/xdg/i3/config (or $XDG_CONFIG_DIRS/i3/config if set)
4. /etc/i3/config
You can specify a custom path using the -c option.

View File

@ -86,16 +86,16 @@ state DEBUGLOG:
# border normal|pixel [<n>]
# border none|1pixel|toggle
state BORDER:
border_style = 'normal', 'pixel'
border_style = 'normal', 'pixel', 'toggle'
-> BORDER_WIDTH
border_style = 'none', 'toggle'
border_style = 'none'
-> call cmd_border($border_style, 0)
border_style = '1pixel'
-> call cmd_border($border_style, 1)
'1pixel'
-> call cmd_border("pixel", 1)
state BORDER_WIDTH:
end
-> call cmd_border($border_style, 2)
-> call cmd_border($border_style, -1)
border_width = number
-> call cmd_border($border_style, &border_width)
@ -243,7 +243,7 @@ state RESIZE_TILING:
'or'
-> RESIZE_TILING_OR
end
-> call cmd_resize($way, $direction, &resize_px, 10)
-> call cmd_resize($way, $direction, &resize_px, 0)
state RESIZE_TILING_OR:
resize_ppt = number
@ -254,12 +254,24 @@ state RESIZE_TILING_FINAL:
-> call cmd_resize($way, $direction, &resize_px, &resize_ppt)
state RESIZE_SET:
'height'
-> RESIZE_HEIGHT_GET_NUMBER
'width'
->
width = number
-> RESIZE_WIDTH
state RESIZE_WIDTH:
mode_width = 'px', 'ppt'
->
end
-> call cmd_resize_set(&width, $mode_width, 0, 0)
'height'
-> RESIZE_HEIGHT_GET_NUMBER
height = number
-> RESIZE_HEIGHT
state RESIZE_HEIGHT_GET_NUMBER:
height = number
-> RESIZE_HEIGHT
@ -396,7 +408,7 @@ state MOVE_TO_POSITION_X:
state MOVE_TO_POSITION_Y:
'px', end
-> call cmd_move_window_to_position($method, &coord_x, &coord_y)
-> call cmd_move_window_to_position(&coord_x, &coord_y)
# mode <string>
state MODE:

View File

@ -45,9 +45,11 @@ state INITIAL:
'fake_outputs', 'fake-outputs' -> FAKE_OUTPUTS
'force_display_urgency_hint' -> FORCE_DISPLAY_URGENCY_HINT
'focus_on_window_activation' -> FOCUS_ON_WINDOW_ACTIVATION
'title_align' -> TITLE_ALIGN
'show_marks' -> SHOW_MARKS
'workspace' -> WORKSPACE
'ipc_socket', 'ipc-socket' -> IPC_SOCKET
'ipc_kill_timeout' -> IPC_KILL_TIMEOUT
'restart_state' -> RESTART_STATE
'popup_during_fullscreen' -> POPUP_DURING_FULLSCREEN
exectype = 'exec_always', 'exec' -> EXEC
@ -247,6 +249,11 @@ state FORCE_DISPLAY_URGENCY_HINT:
duration_ms = number
-> FORCE_DISPLAY_URGENCY_HINT_MS
# title_align [left|center|right]
state TITLE_ALIGN:
alignment = 'left', 'center', 'right'
-> call cfg_title_align($alignment)
# show_marks
state SHOW_MARKS:
value = word
@ -273,7 +280,7 @@ state WORKSPACE_OUTPUT:
-> WORKSPACE_OUTPUT_STR
state WORKSPACE_OUTPUT_STR:
output = word
output = string
-> call cfg_workspace($workspace, $output)
# ipc-socket <path>
@ -281,6 +288,11 @@ state IPC_SOCKET:
path = string
-> call cfg_ipc_socket($path)
# ipc_kill_timeout
state IPC_KILL_TIMEOUT:
timeout = number
-> call cfg_ipc_kill_timeout(&timeout)
# restart_state <path> (for testcases)
state RESTART_STATE:
path = string
@ -455,6 +467,7 @@ state BAR:
'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR
'workspace_buttons' -> BAR_WORKSPACE_BUTTONS
'strip_workspace_numbers' -> BAR_STRIP_WORKSPACE_NUMBERS
'strip_workspace_name' -> BAR_STRIP_WORKSPACE_NAME
'verbose' -> BAR_VERBOSE
'colors' -> BAR_COLORS_BRACE
'}'
@ -490,8 +503,14 @@ state BAR_ID:
-> call cfg_bar_id($bar_id); BAR
state BAR_MODIFIER:
modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift', 'none', 'off'
-> call cfg_bar_modifier($modifier); BAR
'off', 'none'
-> call cfg_bar_modifier(NULL); BAR
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl'
->
'+'
->
end
-> call cfg_bar_modifier($modifiers); BAR
state BAR_WHEEL_UP_CMD:
command = string
@ -555,6 +574,10 @@ state BAR_STRIP_WORKSPACE_NUMBERS:
value = word
-> call cfg_bar_strip_workspace_numbers($value); BAR
state BAR_STRIP_WORKSPACE_NAME:
value = word
-> call cfg_bar_strip_workspace_name($value); BAR
state BAR_VERBOSE:
value = word
-> call cfg_bar_verbose($value); BAR

View File

@ -1,9 +1,9 @@
#!/bin/zsh
# This script is used to prepare a new release of i3.
export RELEASE_VERSION="4.14.1"
export RELEASE_VERSION="4.15"
export PREVIOUS_VERSION="4.14"
export RELEASE_BRANCH="master"
export RELEASE_BRANCH="next"
if [ ! -e "../i3.github.io" ]
then
@ -85,12 +85,12 @@ if [ "${RELEASE_BRANCH}" = "master" ]; then
git checkout master
git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'"
git checkout next
git merge --no-ff -X ours master -m "Merge branch 'master' into next"
git merge --no-ff -s recursive -X ours -X no-renames master -m "Merge branch 'master' into next"
else
git checkout next
git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'"
git checkout master
git merge --no-ff -X theirs next -m "Merge branch 'next' into master"
git merge --no-ff -s recursive -X theirs -X no-renames next -m "Merge branch 'next' into master"
fi
git remote remove origin
@ -126,6 +126,7 @@ WORKDIR /usr/src
RUN mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' i3-${RELEASE_VERSION}/debian/control
WORKDIR /usr/src/i3-${RELEASE_VERSION}
RUN dpkg-buildpackage -sa -j8
RUN dpkg-buildpackage -S -sa -j8
EOT
CONTAINER_NAME=$(echo "i3-${TMPDIR}" | sed 's,/,,g')
@ -139,7 +140,7 @@ echo "Content of resulting packages .changes file:"
cat ${TMPDIR}/debian/*.changes
# debsign is in devscripts, which is available in fedora and debian
debsign -k4AC8EE1D ${TMPDIR}/debian/*.changes
debsign --no-re-sign -k4AC8EE1D ${TMPDIR}/debian/*.changes
# TODO: docker cleanup
@ -227,7 +228,7 @@ echo " cd ${TMPDIR}/i3.github.io"
echo " git push"
echo ""
echo " cd ${TMPDIR}/debian"
echo " dput *.changes"
echo " dput"
echo ""
echo " cd ${TMPDIR}"
echo " sendmail -t < email.txt"

View File

@ -22,7 +22,7 @@ void run_assignments(i3Window *window) {
/* Check if any assignments match */
Assignment *current;
TAILQ_FOREACH(current, &assignments, assignments) {
if (!match_matches_window(&(current->match), window))
if (current->type != A_COMMAND || !match_matches_window(&(current->match), window))
continue;
bool skip = false;
@ -45,19 +45,16 @@ void run_assignments(i3Window *window) {
window->ran_assignments = srealloc(window->ran_assignments, sizeof(Assignment *) * window->nr_assignments);
window->ran_assignments[window->nr_assignments - 1] = current;
DLOG("matching assignment, would do:\n");
if (current->type == A_COMMAND) {
DLOG("execute command %s\n", current->dest.command);
char *full_command;
sasprintf(&full_command, "[id=\"%d\"] %s", window->id, current->dest.command);
CommandResult *result = parse_command(full_command, NULL);
free(full_command);
DLOG("matching assignment, execute command %s\n", current->dest.command);
char *full_command;
sasprintf(&full_command, "[id=\"%d\"] %s", window->id, current->dest.command);
CommandResult *result = parse_command(full_command, NULL);
free(full_command);
if (result->needs_tree_render)
needs_tree_render = true;
if (result->needs_tree_render)
needs_tree_render = true;
command_result_free(result);
}
command_result_free(result);
}
/* If any of the commands required re-rendering, we will do that now. */

View File

@ -198,6 +198,7 @@ void regrab_all_buttons(xcb_connection_t *conn) {
*/
static Binding *get_binding(i3_event_state_mask_t state_filtered, bool is_release, uint16_t input_code, input_type_t input_type) {
Binding *bind;
Binding *result = NULL;
if (!is_release) {
/* On a press event, we first reset all B_UPON_KEYRELEASE_IGNORE_MODS
@ -227,30 +228,27 @@ static Binding *get_binding(i3_event_state_mask_t state_filtered, bool is_releas
/* For keyboard bindings where a symbol was specified by the user, we
* need to look in the array of translated keycodes for the events
* keycode */
bool found_keycode = false;
if (input_type == B_KEYBOARD && bind->symbol != NULL) {
xcb_keycode_t input_keycode = (xcb_keycode_t)input_code;
bool found_keycode = false;
struct Binding_Keycode *binding_keycode;
TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) {
const uint32_t modifiers_mask = (binding_keycode->modifiers & 0x0000FFFF);
const bool mods_match = (modifiers_mask == modifiers_state);
DLOG("binding_keycode->modifiers = %d, modifiers_mask = %d, modifiers_state = %d, mods_match = %s\n",
binding_keycode->modifiers, modifiers_mask, modifiers_state, (mods_match ? "yes" : "no"));
if (binding_keycode->keycode == input_keycode && mods_match) {
if (binding_keycode->keycode == input_keycode &&
(mods_match || (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS && is_release))) {
found_keycode = true;
break;
}
}
if (!found_keycode) {
continue;
}
} else {
/* This case is easier: The user specified a keycode */
if (bind->keycode != input_code) {
continue;
}
bool found_keycode = false;
struct Binding_Keycode *binding_keycode;
TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) {
const uint32_t modifiers_mask = (binding_keycode->modifiers & 0x0000FFFF);
@ -262,9 +260,9 @@ static Binding *get_binding(i3_event_state_mask_t state_filtered, bool is_releas
break;
}
}
if (!found_keycode) {
continue;
}
}
if (!found_keycode) {
continue;
}
/* If this binding is a release binding, it matches the key which the
@ -274,23 +272,26 @@ static Binding *get_binding(i3_event_state_mask_t state_filtered, bool is_releas
if (bind->release == B_UPON_KEYRELEASE && !is_release) {
bind->release = B_UPON_KEYRELEASE_IGNORE_MODS;
DLOG("marked bind %p as B_UPON_KEYRELEASE_IGNORE_MODS\n", bind);
/* The correct binding has been found, so abort the search, but
* also dont return this binding, since it should not be executed
* yet (only when the keys are released). */
bind = TAILQ_END(bindings);
break;
}
/* Check if the binding is for a press or a release event */
if ((bind->release == B_UPON_KEYPRESS && is_release) ||
(bind->release >= B_UPON_KEYRELEASE && !is_release)) {
if (result) {
break;
}
continue;
}
break;
/* Check if the binding is for a press or a release event */
if ((bind->release == B_UPON_KEYPRESS && is_release)) {
continue;
}
if (is_release) {
return bind;
} else if (!result) {
/* Continue looping to mark needed B_UPON_KEYRELEASE_IGNORE_MODS. */
result = bind;
}
}
return (bind == TAILQ_END(bindings) ? NULL : bind);
return result;
}
/*
@ -361,6 +362,14 @@ struct resolve {
struct xkb_state *xkb_state_numlock_no_shift;
};
#define ADD_TRANSLATED_KEY(code, mods) \
do { \
struct Binding_Keycode *binding_keycode = smalloc(sizeof(struct Binding_Keycode)); \
binding_keycode->modifiers = (mods); \
binding_keycode->keycode = (code); \
TAILQ_INSERT_TAIL(&(bind->keycodes_head), binding_keycode, keycodes); \
} while (0)
/*
* add_keycode_if_matches is called for each keycode in the keymap and will add
* the keycode to |data->bind| if the keycode can result in the keysym
@ -390,18 +399,10 @@ static void add_keycode_if_matches(struct xkb_keymap *keymap, xkb_keycode_t key,
}
Binding *bind = resolving->bind;
#define ADD_TRANSLATED_KEY(mods) \
do { \
struct Binding_Keycode *binding_keycode = smalloc(sizeof(struct Binding_Keycode)); \
binding_keycode->modifiers = (mods); \
binding_keycode->keycode = key; \
TAILQ_INSERT_TAIL(&(bind->keycodes_head), binding_keycode, keycodes); \
} while (0)
ADD_TRANSLATED_KEY(bind->event_state_mask);
ADD_TRANSLATED_KEY(key, bind->event_state_mask);
/* Also bind the key with active CapsLock */
ADD_TRANSLATED_KEY(bind->event_state_mask | XCB_MOD_MASK_LOCK);
ADD_TRANSLATED_KEY(key, bind->event_state_mask | XCB_MOD_MASK_LOCK);
/* If this binding is not explicitly for NumLock, check whether we need to
* add a fallback. */
@ -413,17 +414,15 @@ static void add_keycode_if_matches(struct xkb_keymap *keymap, xkb_keycode_t key,
xkb_keysym_t sym_numlock = xkb_state_key_get_one_sym(numlock_state, key);
if (sym_numlock == resolving->keysym) {
/* Also bind the key with active NumLock */
ADD_TRANSLATED_KEY(bind->event_state_mask | xcb_numlock_mask);
ADD_TRANSLATED_KEY(key, bind->event_state_mask | xcb_numlock_mask);
/* Also bind the key with active NumLock+CapsLock */
ADD_TRANSLATED_KEY(bind->event_state_mask | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
ADD_TRANSLATED_KEY(key, bind->event_state_mask | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
} else {
DLOG("Skipping automatic numlock fallback, key %d resolves to 0x%x with numlock\n",
key, sym_numlock);
}
}
#undef ADD_TRANSLATED_KEY
}
/*
@ -431,41 +430,22 @@ static void add_keycode_if_matches(struct xkb_keymap *keymap, xkb_keycode_t key,
*
*/
void translate_keysyms(void) {
struct xkb_state *dummy_state = xkb_state_new(xkb_keymap);
if (dummy_state == NULL) {
ELOG("Could not create XKB state, cannot translate keysyms.\n");
return;
}
struct xkb_state *dummy_state_no_shift = xkb_state_new(xkb_keymap);
if (dummy_state_no_shift == NULL) {
ELOG("Could not create XKB state, cannot translate keysyms.\n");
return;
}
struct xkb_state *dummy_state_numlock = xkb_state_new(xkb_keymap);
if (dummy_state_numlock == NULL) {
ELOG("Could not create XKB state, cannot translate keysyms.\n");
return;
}
struct xkb_state *dummy_state_numlock_no_shift = xkb_state_new(xkb_keymap);
if (dummy_state_numlock_no_shift == NULL) {
ELOG("Could not create XKB state, cannot translate keysyms.\n");
return;
}
struct xkb_state *dummy_state = NULL;
struct xkb_state *dummy_state_no_shift = NULL;
struct xkb_state *dummy_state_numlock = NULL;
struct xkb_state *dummy_state_numlock_no_shift = NULL;
bool has_errors = false;
if ((dummy_state = xkb_state_new(xkb_keymap)) == NULL ||
(dummy_state_no_shift = xkb_state_new(xkb_keymap)) == NULL ||
(dummy_state_numlock = xkb_state_new(xkb_keymap)) == NULL ||
(dummy_state_numlock_no_shift = xkb_state_new(xkb_keymap)) == NULL) {
ELOG("Could not create XKB state, cannot translate keysyms.\n");
goto out;
}
Binding *bind;
TAILQ_FOREACH(bind, bindings, bindings) {
#define ADD_TRANSLATED_KEY(code, mods) \
do { \
struct Binding_Keycode *binding_keycode = smalloc(sizeof(struct Binding_Keycode)); \
binding_keycode->modifiers = (mods); \
binding_keycode->keycode = (code); \
TAILQ_INSERT_TAIL(&(bind->keycodes_head), binding_keycode, keycodes); \
} while (0)
if (bind->input_type == B_MOUSE) {
long button;
if (!parse_long(bind->symbol + (sizeof("button") - 1), &button, 10)) {
@ -616,10 +596,9 @@ void translate_keysyms(void) {
DLOG("state=0x%x, cfg=\"%s\", sym=0x%x → keycodes%s (%d)\n",
bind->event_state_mask, bind->symbol, keysym, keycodes, num_keycodes);
free(keycodes);
#undef ADD_TRANSLATED_KEY
}
out:
xkb_state_unref(dummy_state);
xkb_state_unref(dummy_state_no_shift);
xkb_state_unref(dummy_state_numlock);
@ -630,6 +609,8 @@ void translate_keysyms(void) {
}
}
#undef ADD_TRANSLATED_KEY
/*
* Switches the key bindings to the given mode, if the mode exists
*
@ -648,6 +629,14 @@ void switch_mode(const char *new_mode) {
translate_keysyms();
grab_all_keys(conn);
/* Reset all B_UPON_KEYRELEASE_IGNORE_MODS bindings to avoid possibly
* activating one of them. */
Binding *bind;
TAILQ_FOREACH(bind, bindings, bindings) {
if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
bind->release = B_UPON_KEYRELEASE;
}
char *event_msg;
sasprintf(&event_msg, "{\"change\":\"%s\", \"pango_markup\":%s}",
mode->name, (mode->pango_markup ? "true" : "false"));
@ -658,7 +647,7 @@ void switch_mode(const char *new_mode) {
return;
}
ELOG("ERROR: Mode not found\n");
ELOG("Mode not found\n");
}
static int reorder_binding_cmp(const void *a, const void *b) {
@ -870,8 +859,6 @@ static int fill_rmlvo_from_root(struct xkb_rule_names *xkb_names) {
xcb_intern_atom_reply_t *atom_reply;
size_t content_max_words = 256;
xcb_window_t root = root_screen->root;
atom_reply = xcb_intern_atom_reply(
conn, xcb_intern_atom(conn, 0, strlen("_XKB_RULES_NAMES"), "_XKB_RULES_NAMES"), NULL);
if (atom_reply == NULL)
@ -968,10 +955,7 @@ bool load_keymap(void) {
.options = NULL};
if (fill_rmlvo_from_root(&names) == -1) {
ELOG("Could not get _XKB_RULES_NAMES atom from root window, falling back to defaults.\n");
if ((new_keymap = xkb_keymap_new_from_names(xkb_context, &names, 0)) == NULL) {
ELOG("xkb_keymap_new_from_names(NULL) failed\n");
return false;
}
/* Using NULL for the fields of xkb_rule_names. */
}
new_keymap = xkb_keymap_new_from_names(xkb_context, &names, 0);
free((char *)names.rules);
@ -980,7 +964,7 @@ bool load_keymap(void) {
free((char *)names.variant);
free((char *)names.options);
if (new_keymap == NULL) {
ELOG("xkb_keymap_new_from_names(RMLVO) failed\n");
ELOG("xkb_keymap_new_from_names failed\n");
return false;
}
}

View File

@ -44,9 +44,6 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press
case BORDER_BOTTOM:
search_direction = D_DOWN;
break;
default:
assert(false);
break;
}
bool res = resize_find_tiling_participants(&first, &second, search_direction, false);
@ -233,15 +230,12 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
event->detail == XCB_BUTTON_SCROLL_LEFT ||
event->detail == XCB_BUTTON_SCROLL_RIGHT)) {
DLOG("Scrolling on a window decoration\n");
orientation_t orientation = (con->parent->layout == L_STACKED ? VERT : HORIZ);
/* Focus the currently focused container on the same level that the
* user scrolled on. e.g. the tabbed decoration contains
* "urxvt | i3: V[xterm geeqie] | firefox",
* focus is on the xterm, but the user scrolled on urxvt.
* The splitv container will be focused. */
orientation_t orientation = con_orientation(con->parent);
/* Use the focused child of the tabbed / stacked container, not the
* container the user scrolled on. */
Con *focused = con->parent;
focused = TAILQ_FIRST(&(focused->focus_head));
con_activate(focused);
con_activate(con_descend_focused(focused));
/* To prevent scrolling from going outside the container (see ticket
* #557), we first check if scrolling is possible at all. */
bool scroll_prev_possible = (TAILQ_PREV(focused, nodes_head, nodes) != NULL);
@ -260,7 +254,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
/* 3: For floating containers, we also want to raise them on click.
* We will skip handling events on floating cons in fullscreen mode */
Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL);
Con *fs = con_get_fullscreen_covering_ws(ws);
if (floatingcon != NULL && fs != con) {
/* 4: floating_modifier plus left mouse button drags */
if (mod_pressed && event->detail == XCB_BUTTON_CLICK_LEFT) {

File diff suppressed because it is too large Load Diff

View File

@ -157,7 +157,7 @@ static long get_long(const char *identifier) {
// TODO move to a common util
static void clear_stack(void) {
for (int c = 0; c < 10; c++) {
if (stack[c].type == STACK_STR && stack[c].val.str != NULL)
if (stack[c].type == STACK_STR)
free(stack[c].val.str);
stack[c].identifier = NULL;
stack[c].val.str = NULL;

161
src/con.c
View File

@ -286,14 +286,14 @@ void con_close(Con *con, kill_window_t kill_window) {
for (child = TAILQ_FIRST(&(con->focus_head)); child;) {
nextchild = TAILQ_NEXT(child, focused);
DLOG("killing child = %p.\n", child);
tree_close_internal(child, kill_window, false, false);
tree_close_internal(child, kill_window, false);
child = nextchild;
}
return;
}
tree_close_internal(con, kill_window, false, false);
tree_close_internal(con, kill_window, false);
}
/*
@ -312,7 +312,7 @@ bool con_has_managed_window(Con *con) {
return (con != NULL && con->window != NULL && con->window->id != XCB_WINDOW_NONE && con_get_workspace(con) != NULL);
}
/**
/*
* Returns true if this node has regular or floating children.
*
*/
@ -509,7 +509,24 @@ Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode) {
return NULL;
}
/**
/*
* Returns the fullscreen node that covers the given workspace if it exists.
* This is either a CF_GLOBAL fullscreen container anywhere or a CF_OUTPUT
* fullscreen container in the workspace.
*
*/
Con *con_get_fullscreen_covering_ws(Con *ws) {
if (!ws) {
return NULL;
}
Con *fs = con_get_fullscreen_con(croot, CF_GLOBAL);
if (!fs) {
return con_get_fullscreen_con(ws, CF_OUTPUT);
}
return fs;
}
/*
* Returns true if the container is internal, such as __i3_scratch
*
*/
@ -879,7 +896,7 @@ int con_num_children(Con *con) {
return children;
}
/**
/*
* Returns the number of visible non-floating children of this container.
* For example, if the container contains a hsplit which has two children,
* this will return 2 instead of 1.
@ -919,6 +936,10 @@ int con_num_windows(Con *con) {
num += con_num_windows(current);
}
TAILQ_FOREACH(current, &(con->floating_head), floating_windows) {
num += con_num_windows(current);
}
return num;
}
@ -1097,7 +1118,7 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
/* Prevent moving if this would violate the fullscreen focus restrictions. */
Con *target_ws = con_get_workspace(target);
if (!con_fullscreen_permits_focusing(target_ws)) {
if (!ignore_focus && !con_fullscreen_permits_focusing(target_ws)) {
LOG("Cannot move out of a fullscreen container.\n");
return false;
}
@ -1140,7 +1161,13 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
/* 1: save the container which is going to be focused after the current
* container is moved away */
Con *focus_next = con_next_focused(con);
Con *focus_next = NULL;
if (!ignore_focus && source_ws == current_ws) {
focus_next = con_descend_focused(source_ws);
if (focus_next == con || con_has_parent(focus_next, con)) {
focus_next = con_next_focused(con);
}
}
/* 2: we go up one level, but only when target is a normal container */
if (target->type != CT_WORKSPACE) {
@ -1148,13 +1175,13 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
target = target->parent;
}
/* 3: if the target container is floating, we get the workspace instead.
* Only tiling windows need to get inserted next to the current container.
* */
Con *floatingcon = con_inside_floating(target);
if (floatingcon != NULL) {
/* 3: if the original target is the direct child of a floating container, we
* can't move con next to it - floating containers have only one child - so
* we get the workspace instead. */
if (target->type == CT_FLOATING_CON) {
DLOG("floatingcon, going up even further\n");
target = floatingcon->parent;
orig_target = target;
target = target->parent;
}
if (con->type == CT_FLOATING_CON) {
@ -1170,20 +1197,6 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
floating_fix_coordinates(con, &(source_output->rect), &(dest_output->rect));
} else
DLOG("Not fixing coordinates, fix_coordinates flag = %d\n", fix_coordinates);
/* If moving to a visible workspace, call show so it can be considered
* focused. Must do before attaching because workspace_show checks to see
* if focused container is in its area. */
if (!ignore_focus && workspace_is_visible(target_ws)) {
workspace_show(target_ws);
/* Dont warp if told so (when dragging floating windows with the
* mouse for example) */
if (dont_warp)
x_set_warp_to(NULL);
else
x_set_warp_to(&(con->rect));
}
}
/* If moving a fullscreen container and the destination already has a
@ -1217,20 +1230,21 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
/* We need to save the focused workspace on the output in case the
* new workspace is hidden and it's necessary to immediately switch
* back to the originally-focused workspace. */
Con *old_focus = TAILQ_FIRST(&(output_get_content(dest_output)->focus_head));
Con *old_focus_ws = TAILQ_FIRST(&(output_get_content(dest_output)->focus_head));
Con *old_focus = focused;
con_activate(con_descend_focused(con));
/* Restore focus if the output's focused workspace has changed. */
if (con_get_workspace(focused) != old_focus)
if (old_focus_ws == current_ws && old_focus->type != CT_WORKSPACE) {
/* Restore focus to the currently focused container. */
con_activate(old_focus);
} else if (con_get_workspace(focused) != old_focus_ws) {
/* Restore focus if the output's focused workspace has changed. */
con_focus(con_descend_focused(old_focus_ws));
}
}
/* 7: when moving to another workspace, we leave the focus on the current
* workspace. (see also #809) */
/* Descend focus stack in case focus_next is a workspace which can
* occur if we move to the same workspace. Also show current workspace
* to ensure it is focused. */
if (!ignore_focus) {
workspace_show(current_ws);
if (dont_warp) {
@ -1241,7 +1255,7 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
/* Set focus only if con was on current workspace before moving.
* Otherwise we would give focus to some window on different workspace. */
if (!ignore_focus && source_ws == current_ws)
if (focus_next)
con_activate(con_descend_focused(focus_next));
/* 8. If anything within the container is associated with a startup sequence,
@ -1310,7 +1324,7 @@ bool con_move_to_mark(Con *con, const char *mark) {
return true;
}
if (con->type == CT_WORKSPACE) {
if (target->type == CT_WORKSPACE) {
DLOG("target container is a workspace, simply moving the container there.\n");
con_move_to_workspace(con, target, true, false, false);
return true;
@ -1418,20 +1432,16 @@ orientation_t con_orientation(Con *con) {
return HORIZ;
case L_DEFAULT:
DLOG("Someone called con_orientation() on a con with L_DEFAULT, this is a bug in the code.\n");
ELOG("Someone called con_orientation() on a con with L_DEFAULT, this is a bug in the code.\n");
assert(false);
return HORIZ;
case L_DOCKAREA:
case L_OUTPUT:
DLOG("con_orientation() called on dockarea/output (%d) container %p\n", con->layout, con);
assert(false);
return HORIZ;
default:
DLOG("con_orientation() ran into default\n");
ELOG("con_orientation() called on dockarea/output (%d) container %p\n", con->layout, con);
assert(false);
}
/* should not be reached */
assert(false);
}
/*
@ -1441,52 +1451,20 @@ orientation_t con_orientation(Con *con) {
*
*/
Con *con_next_focused(Con *con) {
Con *next;
/* floating containers are attached to a workspace, so we focus either the
* next floating container (if any) or the workspace itself. */
if (con->type == CT_FLOATING_CON) {
DLOG("selecting next for CT_FLOATING_CON\n");
next = TAILQ_NEXT(con, floating_windows);
DLOG("next = %p\n", next);
if (!next) {
next = TAILQ_PREV(con, floating_head, floating_windows);
DLOG("using prev, next = %p\n", next);
}
if (!next) {
Con *ws = con_get_workspace(con);
next = ws;
DLOG("no more floating containers for next = %p, restoring workspace focus\n", next);
while (next != TAILQ_END(&(ws->focus_head)) && !TAILQ_EMPTY(&(next->focus_head))) {
next = TAILQ_FIRST(&(next->focus_head));
if (next == con) {
DLOG("skipping container itself, we want the next client\n");
next = TAILQ_NEXT(next, focused);
}
}
if (next == TAILQ_END(&(ws->focus_head))) {
DLOG("Focus list empty, returning ws\n");
next = ws;
}
} else {
/* Instead of returning the next CT_FLOATING_CON, we descend it to
* get an actual window to focus. */
next = con_descend_focused(next);
}
return next;
}
/* dock clients cannot be focused, so we focus the workspace instead */
if (con->parent->type == CT_DOCKAREA) {
DLOG("selecting workspace for dock client\n");
return con_descend_focused(output_get_content(con->parent->parent));
}
if (con_is_floating(con)) {
con = con->parent;
}
/* if 'con' is not the first entry in the focus stack, use the first one as
* its currently focused already */
Con *first = TAILQ_FIRST(&(con->parent->focus_head));
if (first != con) {
DLOG("Using first entry %p\n", first);
next = first;
Con *next = TAILQ_FIRST(&(con->parent->focus_head));
if (next != con) {
DLOG("Using first entry %p\n", next);
} else {
/* try to focus the next container on the same level as this one or fall
* back to its parent */
@ -1501,6 +1479,10 @@ Con *con_next_focused(Con *con) {
next = TAILQ_FIRST(&(next->focus_head));
}
if (con->type == CT_FLOATING_CON && next != con->parent) {
next = con_descend_focused(next);
}
return next;
}
@ -1734,8 +1716,7 @@ adjacent_t con_adjacent_borders(Con *con) {
*
*/
int con_border_style(Con *con) {
Con *fs = con_get_fullscreen_con(con->parent, CF_OUTPUT);
if (fs == con) {
if (con->fullscreen_mode == CF_OUTPUT || con->fullscreen_mode == CF_GLOBAL) {
DLOG("this one is fullscreen! overriding BS_NONE\n");
return BS_NONE;
}
@ -1935,7 +1916,6 @@ void con_toggle_layout(Con *con, const char *toggle_mode) {
* now let's activate the current layout (next in list) */
if (current_layout_found) {
new_layout = layout;
free(tm_dup);
break;
}
@ -1943,6 +1923,7 @@ void con_toggle_layout(Con *con, const char *toggle_mode) {
current_layout_found = true;
}
}
free(tm_dup);
if (new_layout != L_DEFAULT) {
con_set_layout(con, new_layout);
@ -1995,7 +1976,7 @@ static void con_on_remove_child(Con *con) {
if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL);
tree_close_internal(con, DONT_KILL_WINDOW, false, false);
tree_close_internal(con, DONT_KILL_WINDOW, false);
const unsigned char *payload;
ylength length;
@ -2016,7 +1997,7 @@ static void con_on_remove_child(Con *con) {
int children = con_num_children(con);
if (children == 0) {
DLOG("Container empty, closing\n");
tree_close_internal(con, DONT_KILL_WINDOW, false, false);
tree_close_internal(con, DONT_KILL_WINDOW, false);
return;
}
}
@ -2413,6 +2394,10 @@ bool con_swap(Con *first, Con *second) {
/* Move first to second. */
result &= _con_move_to_con(first, second, false, false, false, true, false);
/* If swapping the containers didn't work we don't need to mess with the focus. */
if (!result) {
goto swap_end;
}
/* If we moved the container holding the focused window to another
* workspace we need to ensure the visible workspace has the focused
@ -2425,8 +2410,6 @@ bool con_swap(Con *first, Con *second) {
/* Move second to where first has been originally. */
result &= _con_move_to_con(second, fake, false, false, false, true, false);
/* If swapping the containers didn't work we don't need to mess with the focus. */
if (!result) {
goto swap_end;
}

View File

@ -18,7 +18,7 @@ Config config;
struct modes_head modes;
struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
/**
/*
* Ungrabs all keys, to be called before re-grabbing the keys because of a
* mapping_notify event or a configuration file reload
*
@ -32,7 +32,7 @@ void ungrab_all_keys(xcb_connection_t *conn) {
* Sends the current bar configuration as an event to all barconfig_update listeners.
*
*/
void update_barconfig() {
void update_barconfig(void) {
Barconfig *current;
TAILQ_FOREACH(current, &barconfigs, configs) {
ipc_send_barconfig_update_event(current);
@ -49,7 +49,8 @@ bool parse_configuration(const char *override_configpath, bool use_nagbar) {
char *path = get_config_path(override_configpath, true);
if (path == NULL) {
die("Unable to find the configuration file (looked at "
"~/.i3/config, $XDG_CONFIG_HOME/i3/config, " SYSCONFDIR "/i3/config and $XDG_CONFIG_DIRS/i3/config)");
"$XDG_CONFIG_HOME/i3/config, ~/.i3/config, $XDG_CONFIG_DIRS/i3/config "
"and " SYSCONFDIR "/i3/config)");
}
LOG("Parsing configfile %s\n", path);
@ -96,18 +97,27 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
FREE(mode);
}
struct Assignment *assign;
while (!TAILQ_EMPTY(&assignments)) {
assign = TAILQ_FIRST(&assignments);
if (assign->type == A_TO_WORKSPACE)
struct Assignment *assign = TAILQ_FIRST(&assignments);
if (assign->type == A_TO_WORKSPACE || assign->type == A_TO_WORKSPACE_NUMBER)
FREE(assign->dest.workspace);
else if (assign->type == A_COMMAND)
FREE(assign->dest.command);
else if (assign->type == A_TO_OUTPUT)
FREE(assign->dest.output);
match_free(&(assign->match));
TAILQ_REMOVE(&assignments, assign, assignments);
FREE(assign);
}
while (!TAILQ_EMPTY(&ws_assignments)) {
struct Workspace_Assignment *assign = TAILQ_FIRST(&ws_assignments);
FREE(assign->name);
FREE(assign->output);
TAILQ_REMOVE(&ws_assignments, assign, ws_assignments);
FREE(assign);
}
/* Clear bar configs */
Barconfig *barconfig;
while (!TAILQ_EMPTY(&barconfigs)) {
@ -160,10 +170,16 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
FREE(barconfig);
}
/* Invalidate pixmap caches in case font or colors changed */
Con *con;
TAILQ_FOREACH(con, &all_cons, all_cons)
FREE(con->deco_render_params);
TAILQ_FOREACH(con, &all_cons, all_cons) {
/* Assignments changed, previously ran assignments are invalid. */
if (con->window) {
con->window->nr_assignments = 0;
FREE(con->window->ran_assignments);
}
/* Invalidate pixmap caches in case font or colors changed. */
FREE(con->deco_render_params);
}
/* Get rid of the current font */
free_font();
@ -183,10 +199,6 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
bindings = default_mode->bindings;
#define REQUIRED_OPTION(name) \
if (config.name == NULL) \
die("You did not specify required configuration option " #name "\n");
/* Clear the old config or initialize the data structure */
memset(&config, 0, sizeof(config));

View File

@ -318,31 +318,46 @@ CFGFUN(focus_on_window_activation, const char *mode) {
DLOG("Set new focus_on_window_activation mode = %i.\n", config.focus_on_window_activation);
}
CFGFUN(title_align, const char *alignment) {
if (strcmp(alignment, "left") == 0) {
config.title_align = ALIGN_LEFT;
} else if (strcmp(alignment, "center") == 0) {
config.title_align = ALIGN_CENTER;
} else if (strcmp(alignment, "right") == 0) {
config.title_align = ALIGN_RIGHT;
} else {
assert(false);
}
}
CFGFUN(show_marks, const char *value) {
config.show_marks = eval_boolstr(value);
}
CFGFUN(workspace, const char *workspace, const char *output) {
DLOG("Assigning workspace \"%s\" to output \"%s\"\n", workspace, output);
CFGFUN(workspace, const char *workspace, const char *outputs) {
DLOG("Assigning workspace \"%s\" to outputs \"%s\"\n", workspace, outputs);
/* Check for earlier assignments of the same workspace so that we
* dont have assignments of a single workspace to different
* outputs */
struct Workspace_Assignment *assignment;
bool duplicate = false;
TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcasecmp(assignment->name, workspace) == 0) {
ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n",
workspace);
assignment->output = sstrdup(output);
duplicate = true;
return;
}
}
if (!duplicate) {
char *buf = sstrdup(outputs);
char *output = strtok(buf, " ");
while (output != NULL) {
assignment = scalloc(1, sizeof(struct Workspace_Assignment));
assignment->name = sstrdup(workspace);
assignment->output = sstrdup(output);
TAILQ_INSERT_TAIL(&ws_assignments, assignment, ws_assignments);
output = strtok(NULL, " ");
}
free(buf);
}
CFGFUN(ipc_socket, const char *path) {
@ -403,6 +418,11 @@ CFGFUN(assign_output, const char *output) {
return;
}
if (current_match->window_mode != WM_ANY) {
ELOG("Assignments using window mode (floating/tiling) is not supported\n");
return;
}
DLOG("New assignment, using above criteria, to output \"%s\".\n", output);
Assignment *assignment = scalloc(1, sizeof(Assignment));
match_copy(&(assignment->match), current_match);
@ -417,6 +437,11 @@ CFGFUN(assign, const char *workspace, bool is_number) {
return;
}
if (current_match->window_mode != WM_ANY) {
ELOG("Assignments using window mode (floating/tiling) is not supported\n");
return;
}
if (is_number && ws_name_to_number(workspace) == -1) {
ELOG("Could not parse initial part of \"%s\" as a number.\n", workspace);
return;
@ -443,6 +468,10 @@ CFGFUN(no_focus) {
TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
}
CFGFUN(ipc_kill_timeout, const long timeout_ms) {
ipc_set_kill_timeout(timeout_ms / 1000.0);
}
/*******************************************************************************
* Bar configuration (i3bar)
******************************************************************************/
@ -482,25 +511,8 @@ CFGFUN(bar_verbose, const char *verbose) {
current_bar->verbose = eval_boolstr(verbose);
}
CFGFUN(bar_modifier, const char *modifier) {
if (strcmp(modifier, "Mod1") == 0)
current_bar->modifier = M_MOD1;
else if (strcmp(modifier, "Mod2") == 0)
current_bar->modifier = M_MOD2;
else if (strcmp(modifier, "Mod3") == 0)
current_bar->modifier = M_MOD3;
else if (strcmp(modifier, "Mod4") == 0)
current_bar->modifier = M_MOD4;
else if (strcmp(modifier, "Mod5") == 0)
current_bar->modifier = M_MOD5;
else if (strcmp(modifier, "Control") == 0 ||
strcmp(modifier, "Ctrl") == 0)
current_bar->modifier = M_CONTROL;
else if (strcmp(modifier, "Shift") == 0)
current_bar->modifier = M_SHIFT;
else if (strcmp(modifier, "none") == 0 ||
strcmp(modifier, "off") == 0)
current_bar->modifier = M_NONE;
CFGFUN(bar_modifier, const char *modifiers) {
current_bar->modifier = modifiers ? event_state_from_str(modifiers) : XCB_NONE;
}
static void bar_configure_binding(const char *button, const char *release, const char *command) {
@ -627,12 +639,16 @@ CFGFUN(bar_strip_workspace_numbers, const char *value) {
current_bar->strip_workspace_numbers = eval_boolstr(value);
}
CFGFUN(bar_strip_workspace_name, const char *value) {
current_bar->strip_workspace_name = eval_boolstr(value);
}
CFGFUN(bar_start) {
current_bar = scalloc(1, sizeof(struct Barconfig));
TAILQ_INIT(&(current_bar->bar_bindings));
TAILQ_INIT(&(current_bar->tray_outputs));
current_bar->tray_padding = 2;
current_bar->modifier = M_MOD4;
current_bar->modifier = XCB_KEY_BUT_MASK_MOD_4;
}
CFGFUN(bar_finish) {

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