1
0
forked from mirrors/i3

Compare commits

...

453 Commits
4.19.1 ... next

Author SHA1 Message Date
Michael Stapelberg
853b0d9161 i3bar: fix clang compilation error by using const size_t (#6349)
Thanks to Shouη (@Shoun2137 on GitHub) for the suggestion.
2024-12-30 08:29:29 +01:00
Emeric Planet
9dc5230000 Update meson setup command to remove warning (#6338)
This is a very easy fix, to remove the following warning:
```
WARNING: Running the setup command as `meson [options]` instead of `meson setup [options]` is ambiguous and deprecated.
```
The fix is to just run `meson setup` instead and it works like a charm.

Related PRs: i3/i3lock#365 and i3/i3status#538.
2024-12-29 19:50:09 +01:00
Michael Stapelberg
2746e0319b i3bar: work around weird clang compiler behavior
clang used to not warn (= error, with -Werror) about this line. Oh well.
2024-11-06 19:22:15 +01:00
Michael Stapelberg
e107254f1e GitHub Actions: update actions/upload-artifact to v4
The older versions are now deprecated and result in failing GitHub Actions runs.
2024-11-06 19:22:15 +01:00
Michael Stapelberg
2f9ffa3178 debian: update changelog 2024-11-06 18:34:21 +01:00
Michael Stapelberg
d64e5df5b4 Merge branch 'release-4.24' 2024-11-06 18:27:13 +01:00
Michael Stapelberg
454fb63392 Restore non-git version suffix 2024-11-06 18:27:13 +01:00
Michael Stapelberg
4a42eb085c release i3 4.24 2024-11-06 18:26:52 +01:00
Malix
4661e74b5e Fix: remove "dynamic" TWM (#6193)
related to https://github.com/i3/i3.github.io/issues/137
2024-08-05 18:23:55 +02:00
Orestis Floros
d05eed3c01 Consider fullscreen windows maximized (#6153)
Fixes #6148
2024-07-12 09:17:25 +02:00
Orestis Floros
45d8f98fd5 complete-run.pl: Un-set I3SOCK (#6139)
This causes test breakage when the developer/tester is running the tests
in i3, as the actual active instance socket is used instead of the test
instance.

Besides breaking tests, this is quite dangerous as tests like 319-gaps.t
will replace the **actual** config of the user (i.e.
~/.config/i3/config) instead of the current test config.

Broken after #5987
2024-07-10 08:40:50 +02:00
Orestis Floros
05feaecf8a Remove v3 to v4 automatic migration logic (#6144)
Closes https://github.com/i3/i3/issues/6131
2024-07-09 18:03:57 +00:00
Orestis Floros
5413c15e97 Fix crash when reloading config with invalid criteria (#6142)
Came up in https://github.com/i3/i3/discussions/6141
2024-07-09 17:41:11 +02:00
Orestis Floros
be840af45c tiling_drag: Allow swapping containers (#6084)
This was originally mentioned in #3085 but left for a future PR.

One of the noticeable limitations is that pressing the modifier while
the drag is already initiated, will not swap the containers but instead
cancel the drag. This is because of how `drag_pointer()` is written and
would be quite an involved case to handle it.
2024-07-04 21:44:41 +02:00
Orestis Floros
4215998929 GH action: Upload built htmls (#6070) 2024-06-20 07:05:50 +02:00
Orestis Floros
1ee963ede9 Fix crash with focus output and scratchpad (#6079)
The crash was brought up in a comment in
https://github.com/i3/i3/discussions/6076#discussioncomment-9536969

The cause is that the command criteria are matching a window in the
scratchpad. In that case, the assertion in get_output_for_con() fails.
That happens because there is no `Output` for the `Con` output of a
scratchpad window.

I've decided to *not* remove the offending assertion but rather rely on
the caller not using the function with internal containers. My reasoning
is:
1. If get_output_for_con can return NULL then the caller will either
segfault (which is worse) or needs to check the return for NULL.
2. The case where the return can be NULL is already known, it happens
for internal containers.
3. Therefore, the caller should already prevent the situation with a
call to con_is_internal(). Thus, the `assert`ion can remain.

There is also the potential fix of con_get_workspace returning some
arbitrary output (e.g. first in the list or currently focused one)
instead of NULL. This can lead to more tricky to catch bugs.
2024-06-03 17:00:47 +02:00
colona_
11c0a9567f docs/ipc: Add more elaborate description to workspace events (#6089)
This adds some detail to the workspace events documentation and is
written along the same lines as the window events documentation. This
was brought up in [#4392 (issue)](https://github.com/i3/i3/issues/4392).
2024-06-01 09:57:12 +00:00
Orestis Floros
822477cb35 Add popup_during_fullscreen all option (#6068)
Fixes #6062
2024-05-21 17:19:11 +02:00
Orestis Floros
cd6573493c docs/ipc: Fix enumerated list (#6069) 2024-05-21 17:16:23 +02:00
Orestis Floros
1993b7e318 Add popup_during_fullscreen all option 2024-05-20 21:26:27 +02:00
Orestis Floros
3b1747a107 Add tests for popup_during_fullscreen 2024-05-20 21:13:41 +02:00
Tasos Sahanidis
9a69c1eecf Fix size_t format specifiers on 32 bit systems (#6065)
This fixes the following warnings on 32 bit systems

```
[60/108] Compiling C object i3.p/src_regex.c.o
In file included from ../include/all.h:40,
                 from ../src/regex.c:10:
../src/regex.c: In function ‘regex_new’:
../include/log.h:29:33: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘size_t’ {aka ‘unsigned int’} [-Wformat=]
   29 | #define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__)
      |                                 ^~~~~~~~~
../src/regex.c:35:9: note: in expansion of macro ‘ELOG’
   35 |         ELOG("PCRE regular expression compilation failed at %lu: %s\n",
      |         ^~~~
[93/108] Compiling C object i3-input.p/i3-input_main.c.o
../i3-input/main.c: In function ‘finish_input’:
../i3-input/main.c:173:29: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 2 has type ‘size_t’ {aka ‘unsigned int’} [-Wformat=]
  173 |     printf("occurrences = %ld\n", cnt);
      |                           ~~^     ~~~
      |                             |     |
      |                             |     size_t {aka unsigned int}
      |                             long int
      |                           %d 
```
2024-05-20 13:13:26 +02:00
Orestis Floros
82a1c101fd i3bar: Fix clicks with horizontal padding (#6064)
Fixes #5476
2024-05-20 09:17:16 +02:00
Orestis Floros
093e3cf1f7 docs/ipc: Update information on IPC socket 2024-05-20 09:15:57 +02:00
Orestis Floros
00aaa84ab0 ipc_connect: Delete outdated path 2024-05-20 09:15:57 +02:00
Nikolay Nechaev
caf5b32d5c Reap zombie children on i3 start (#5909)
One case when this might be useful is when i3 is restarted and there are
children that terminate after the previous i3 instance shut down but
before the new one set things up.

Fixes #5756
2024-05-17 21:49:54 +02:00
Theo Buehler
854696cfb5 Remove pledge() support for OpenBSD (#6048)
While this initially worked fine, at some point these patches broke
because libcairo started calling shmget(2) - a syscall not covered by
any pledge promise - and a common pitfall when using pledge with
graphics-oriented applications.

Various attempts were made to fix them, but at some time they were
simply disabled in the OpenBSD port:


a4a9f41dd7
5a03c386ba

This seems pointless and creates needless friction both for the i3 team
who was willing to carry ugly code and for the OpenBSD ports maintainers
who had to disable that code again.

Let's abandon this experiment.
2024-05-17 21:41:45 +02:00
Bimba Laszlo
c06ac08aab docs: Fix "Focus the next output" example (#6054)
The "Focus the next output" example was misleading, fixed the code and
added another comment to the previous code.
2024-05-17 21:38:15 +02:00
Junicchi
1597ec27ee add WINDOW_TYPE_NOTIFICATION to floating list (#6017)
as explained in this discussion:
https://github.com/i3/i3/discussions/5966#discussioncomment-8961295
2024-05-16 07:50:06 +00:00
Orestis Floros
6094944345 testsuite docs: Update instructions (#6034)
- Only recommend local perl library installation
- Update build instructions to match hacking-howto
2024-05-05 10:47:17 +02:00
Orestis Floros
d54a10b200 i3bar-workspace-protocol: Make examples (more) POSIX compliant (#6029)
See https://unix.stackexchange.com/a/581410, `read` needs a variable
name.

Came up in #5939
2024-04-30 13:41:45 +02:00
Orestis Floros
e020701df1 errorlog: Check errorfile exists (#6028)
Fixes #6027
2024-04-30 13:39:54 +02:00
Wesley Schwengle
5834b7e824 Use I3SOCK environment variable for path in AnyEvent::I3 (#5987)
Commit 3ae5f31d0 introduced the I3SOCK environment variable. This
prevents us from having to call `i3 --get-socketpath'. In case the
variable doesn't exist, fall back to the old ways.

Signed-off-by: Wesley Schwengle <wesleys@opperschaap.net>
2024-04-19 19:12:11 +02:00
Michael Stapelberg
91ce3bdbd5 AnyEvent::I3: rip out taint mode compatibility (#5999)
I suspect nobody actually uses Perl’s taint mode with AnyEvent::I3.

See https://github.com/i3/i3/pull/5987 for discussion.
2024-04-19 08:45:34 +02:00
yuvallangerontheroad
ffbbbf3477 Add a newline at the end of the version option output. (#5980)
If there is no newline character at the end of the version option's
output, the next command line prompt is written left to the version,
rather than under it.
2024-04-16 11:38:23 +00:00
Michael Stapelberg
051d3537e3 AnyEvent-I3: bump to 0.19 (#5990)
fixes https://github.com/i3/i3/issues/5986
2024-04-09 17:43:02 +02:00
Wesley Schwengle
d91597b1c1 Check if subscribe event type is supported in AnyEvent::I3 (#5988)
Add simple `if exists' construct in the subscribe function. This
prevents a somewhat cryptic warnings such as these:

Use of uninitialized value $type in hash element at
/usr/share/perl5/AnyEvent/I3.pm line 309.

We still warn the user, but it is much clearer as to what the cause is.

It now shows something like this:

Could not subscribe to event type 'foo'. Supported events are _error
barconfig_update binding mode output shutdown tick window workspace

Signed-off-by: Wesley Schwengle <wesleys@opperschaap.net>
2024-04-09 08:28:40 +02:00
systec-awe
47cab33aa8 Fix missing SIGUSR2 posix signal handling (#5960)
Since there is no separate error handling the `SIGUSR2` signal is
registered to get the write return code after exiting the program.

Fixes #5958

---------

Signed-off-by: Andre Werner <andre.werner@systec-electronic.com>
2024-03-20 12:58:56 +00:00
Orestis Floros
910e58585f Support multiple _NET_WM_STATE changes in one ClientMessage (#5910) 2024-02-12 08:40:39 +01:00
rsgowman
6a530de220 Create new workspaces to the right of existing ones with the same number
i.e. creating workspaces named "1", "2:a", "2:b", "3" should result in
that same order rather than "1", "2:b", "2:a", "3".
2024-02-06 20:28:20 +01:00
rsgowman
0639167185 Don't skip identically numbered workspaces when moving to next/prev (#4578)
eg if you have workspaces: { 1, 2:a, 2:b, 3 } and are on workspace 1,
then 'workspace next' should traverse 1 -> 2:a -> 2:b -> 3 -> 1 instead
of 1 -> 2:a -> 3 -> 1.

Fixes #4452
2024-02-06 20:07:21 +01:00
Orestis Floros
60cc6ce174 Use new GitHub issue templates (#5900)
Biggest benefit is that the users are presented with a set of editable
fields instead of having to edit markdown directly in a text box, which
is less friendly. Links to userguide and IPC docs are clickable in the
description.

See
https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository
2024-02-05 08:27:31 +01:00
Harimbola Santatra
b9a796b24a doc: update meson build instruction (#5899)
The [official build instruction][1] are deprecated on Meson 1.3.1.
These command:

    mkdir -p build && cd build
    meson ..
    ninja

... work but will yield the following warning:

> WARNING: Running the setup command as `meson [options]` instead of `meson setup [options]` is ambiguous and deprecated.

Here's the correct way, according to the [meson documentation][2]:

    mkdir -p build
    meson setup build
    meson compile -C build
    meson install -C build


[1]: https://i3wm.org/docs/hacking-howto.html#_building_i3
[2]: https://mesonbuild.com/Quick-guide.html#compiling-a-meson-project
2024-02-04 15:50:36 +01:00
Orestis Floros
f8befe378a Avoid creating redundant containers when switching between tabbed/stacked and split layouts (#5469)
Fixes #3001
2024-01-31 08:14:32 +01:00
Orestis Floros
230147c815 smart_borders: Deprecate option (#5889)
This had pretty much identical behaviour to hide_edge_borders which made
it confusing. The `hide_edge_borders smart_no_gaps` implementation has an extra check
which fixes #5406.
2024-01-30 08:53:32 +01:00
Orestis Floros
c3173af2f1 Merge pull request #5787 from elebow/userguide-default_border-title-layout-note
docs: Add note to `default_border` about title bar in stacking/tabbed
2024-01-28 13:00:16 +01:00
Alessandro Vinciguerra
ca510e5e0f Shrinking on a per-block basis (#5818)
Co-authored-by: Orestis Floros <orestisflo@gmail.com>
2024-01-28 11:30:28 +01:00
Orestis Floros
f169624560 clang-format: enable InsertBraces (#5882)
Enforces a rule that we have followed for years now. Yes, the diff is
quite big but we get it over with once and we prevent having to nit-pick
future PRs.
2024-01-27 11:37:05 +01:00
Orestis Floros
5fdfb14530 con_is_maximized: Fix case where parent is workspace (#5880)
See added test for simple example.
2024-01-26 08:51:28 +01:00
Seth Pollen
a56670bca8 split up compound test assertions 2024-01-24 14:29:01 +01:00
sethpollen
b660d6a902 Add support for _NET_WM_STATE_MAXIMIZED_{HORZ, VERT} (#5840)
If a window occupies the entirety of its workspace vertically and/or horizontally, pass it the _NET_WM_STATE_MAXIMIZED_{HORZ, VERT} atoms. This helps applications like Google Chrome draw the tab bar correctly and handle tab clicks correctly (see https://crbug.com/1495853).

This change is based on work from @yshui in #2380.
2024-01-22 20:34:40 +01:00
Yonas Yanfa
9aba43119b Make raphamorim/rio one of i3's sensible terminals. 2023-12-13 12:45:14 +02:00
Eddie Lebow
96d3762712 docs: Add note to default_border about title bar in stacking/tabbed
See <https://github.com/i3/i3/issues/2664>.
2023-11-26 02:38:02 -05:00
Orestis Floros
69f68dcd74 focus workspace: consider workspace_auto_back_and_forth (#5754)
Additionally, adds some tests for the command.

Fixes #5744
2023-11-05 11:08:44 +01:00
Orestis Floros
a36618f96c Clean up old release notes (#5753) 2023-11-05 11:08:10 +01:00
Orestis Floros
b42dc21068 bindings: Do not grab pointer when executing bindings (#5755)
Grabing the pointer produces a `GrabFrozen` error in applications that
are run from key bindings. Since we don't need the pointer in such
cases, we can change the call to use ASYNC. This seems to be a
historical leftover.

I've tested locally that these still work:
- bindsym $mod+x ...
- bindsym --release $mod+x ...
- bindsym $mod+button1 ...
- bindsym --release $mod+button1 ...
- bindsym --release $mod+x exec program that grabs the keyboard
  now works (see original issue)

Even in the main branch, I actually couldn't get `import` and `xdotool`
to fail with the pointer being frozen, potentially because these
programs wait a bit for the pointer to be unfrozen like i3lock does.

This patch came up in
https://github.com/i3/i3/issues/5735#issuecomment-1781321011

I wonder why the pointer is actually grabbed.

The argument I change in `xcb_grab_key` there, is `pointer_mode`, from
https://www.x.org/releases/X11R7.7/doc/man/man3/xcb_grab_key.3.xhtml:
```
pointer_mode

One of the following values:
XCB_GRAB_MODE_SYNC

The state of the keyboard appears to freeze: No further keyboard events are generated by the server until the grabbing client issues a releasing AllowEvents request or until the keyboard grab is released.

XCB_GRAB_MODE_ASYNC

Keyboard event processing continues normally.
```

I traced via `git blame` the usage of `xcb_grab_key` throughout 14 years
of i3 development and it seems that `pointer_mode` was always set to
`XCB_GRAB_MODE_SYNC`, going all the way back to
b664456706.

Fixes #5735
2023-11-05 11:04:04 +01:00
a-kenji
f1f2282947 docs: fix typo in i3bar-workspace-protocol 2023-11-04 12:18:03 +01:00
Michael Stapelberg
1da50c4ae0 debian: update changelog 2023-10-29 15:42:25 +01:00
Michael Stapelberg
06b3137bd7 Merge branch 'release-4.23' 2023-10-29 09:58:05 +01:00
Michael Stapelberg
ee12c2d1e1 Restore non-git version suffix 2023-10-29 09:58:05 +01:00
Michael Stapelberg
f844de8e66 release i3 4.23 2023-10-29 09:57:51 +01:00
Sergey Zhmylove
26608b74d9 Fix failing testcase when the font exists (#5679)
When the font from testcase's config exists on the system,
load_configuration() does not fallback to a 'fixed' one resulting in a
fail of this case.
The fallback scenario is added as well.
2023-09-23 17:06:54 +02:00
Orestis Floros
5489db6bc8 motif hints: respect maximum border style in append_layout 2023-09-21 18:55:01 +02:00
Orestis Floros
f4959d5da4 Update to clang-format-15 2023-09-21 18:55:01 +02:00
Orestis Floros
bffa4a543c userguide: Add an example for negative lookeaheads (#5665) 2023-09-13 08:42:52 +02:00
Orestis Floros
908c86544b remanage_window: Refactor to make clearer when a swallowing happens 2023-09-06 17:24:53 +02:00
Orestis Floros
82b9821204 Remanage window after urgency flag change
Fixes #5658
2023-09-06 17:24:53 +02:00
Orestis Floros
c1c405f4fc Update to actions/checkout@v4 (#5653)
Update to [actions/checkout@v4](https://github.com/actions/checkout/releases/tag/v4.0.0)

According to https://github.com/actions/checkout/issues/1448 it might fix the issue we are encountering. Even if it doesn't, no apparent harm from using the latest version.
2023-09-05 17:18:38 +02:00
Wesley Schwengle
c6befec0fd Fix dead links for Modern Perl book
Closes: #5523

Signed-off-by: Wesley Schwengle <wesleys@opperschaap.net>
2023-09-04 16:09:05 +02:00
Orestis Floros
c6f62f4695 Fix crashes when using machine criterion (#5650)
Fixes #5616
2023-09-03 19:32:42 +02:00
Uli Schlachter
d6e2a38b5c Share graphics context globally (#4376)
Instead of creating a graphics context for every surface_t, this commit
adds a cache that allows to "remember" up to two GCs. Thus, the code
uses less GCs. When a GC from the cache can be used, this also gets rid
of a round-trip to the X11 server. Both of these are tiny, insignificant
savings, but so what?

Since GCs are per-depth, this code needs access to get_visual_depth().
To avoid a code duplication, this function is moved to libi3.

Fixes: https://github.com/i3/i3/issues/3478
Signed-off-by: Uli Schlachter <psychon@znc.in>
2023-07-22 10:24:13 +02:00
slyshot
6fe98f7847 Remove focus workaround 2023-07-21 13:52:44 +02:00
Orestis Floros
9947890472 Fix gcc false-positive warning 2023-07-15 21:27:11 +02:00
Orestis Floros
cf2ea348c7 debugging docs: Add note about ptrace
Also remove outdated section about IRC
2023-07-15 21:27:11 +02:00
Orestis Floros
e6b41172da Regrab buttons on mode change (#5554)
Unfortunately, grabbing / ungrabbing doesn't seem to work correctly in
xvfb so we can't really test this.

I also fixed the deduplication code in bindings_get_buttons_to_grab().
2023-06-30 08:57:19 +02:00
Harm te Hennepe
866e3dd909 Improve documentation regarding tiling drag (#5541) 2023-06-26 14:43:33 +02:00
Nikolay Nechaev
3ae5f31d04 Remove the double forking in start_application (#5510)
Having just a single fork is beneficial, as it preserves the approprate
parent information for the children of i3, which is useful in some
scenarious e.g. when a child wants to do something on the wm's exit
(possible via `prctl(PR_SET_PDEATHSIG, ...)`).

Moreover, this is a zero-cost benefit: i3 is already using libev with
the default loop, which automatically reaps all the zombie children even
when there is no corresponding event set.

Resolves #5506
2023-06-15 08:38:35 +02:00
Orestis Floros
a95870120c fix workspace not being focused on title bar scroll (#5518) 2023-05-29 14:08:46 +02:00
Orestis Floros
fde43a078b Merge pull request #5378 from slyshot/next
Fix top border resizing registered on tiled windows
2023-05-05 14:15:47 +02:00
slyshot
a715c03d30 Fix top border resizing registered on tiled windows 2023-04-27 20:20:41 -04:00
Orestis Floros
fae2b637ee i3-input: Avoid compiler warning (#5480)
Also updates the function to use proper types, const and style.

The warning:

../i3/i3-input/main.c: In function ‘finish_input’:
../i3/i3-input/main.c:187:13: warning: ‘__builtin_strncat’ specified bound depends on the length of the source argument [-Wstringop-overflow=]
  187 |             strncat(dest, command, inputlen);
      |             ^
../i3/i3-input/main.c:175:20: note: length computed here
  175 |     int inputlen = strlen(command);

Which is triggered because gcc thinks it's bad that `input_len` (the
length of the source in the copy) is used instead of a length that is
inside the limits of the allocated size for the destination. However, in
practice, `full_len` is always than `input_len`.
2023-04-22 18:59:03 +02:00
Orestis Floros
d06f97c4d4 GH Actions: Check for gcc before deploying (#5483)
Fixes #5482
2023-04-11 20:11:30 +02:00
Orestis Floros
13474ff787 man page: Update outdated information (#5478)
Instead of correcting some of them, I completely deleted them since the
userguide is always more up-to-date.

Fixes #5477
2023-04-10 18:36:02 +02:00
Orestis Floros
74785f4795 Allow switching workspaces when in global fullscreen mode (#5398)
Fixes #2974
2023-04-02 18:55:21 +02:00
Orestis Floros
d7583fbc17 Update some of the hacking docs (#5464) 2023-04-02 16:28:06 +02:00
Orestis Floros
fbb6bf666f hacking-howto: Update build instructions for meson (#5463) 2023-04-02 16:27:16 +02:00
Orestis Floros
892ae730cb Update ubuntu & clang-format in builds (#5467) 2023-04-02 16:24:15 +02:00
Orestis Floros
ecb9b895c7 docker: fix missing address sanitizer dependency (#5466) 2023-04-02 15:39:13 +02:00
Orestis Floros
a5da4d54f3 GitHub Actions: revert changes and use if on each step (#5393) 2023-01-23 19:50:28 +01:00
Orestis Floros
3702960a87 Actions: Fix BASENAME env vars (#5392) 2023-01-23 18:57:08 +01:00
Orestis Floros
6911c116e7 GitHub Actions: push artifacts only on next branch (#5388) 2023-01-23 17:39:55 +01:00
Orestis Floros
bfbe73f665 Merge pull request #5390 from nikitabobko/bobko/userguide
Fix "default binding" mistake in userguide
2023-01-23 13:20:31 +01:00
Nikita Bobko
0f64420281 Fix "default binding" mistake in userguide
Default binding to move window down is $mod+Shift+k, not $mod+Shift+j.
Proof: https://github.com/i3/i3/blob/next/etc/config#L45
This commit reverts this pull request https://github.com/i3/i3/pull/4146
2023-01-23 11:49:37 +01:00
Orestis Floros
26990d90f2 Merge pull request #4311 from i3/i3bar-ws-protocol
i3bar: Add protocol for workspace buttons
2023-01-22 19:05:01 +01:00
Orestis Floros
ba1f40f45f i3bar: Add protocol for workspace buttons
Closes #3818 (parent issue)
Fixes #1808
Fixes #2333
Fixes #2617
Fixes #3548
2023-01-22 18:59:58 +01:00
Orestis Floros
c52f13900d Add focus workspace command 2023-01-22 18:33:23 +01:00
Michael Stapelberg
8d64937054 Bump -D_FORTIFY_SOURCE=2 to -D_FORTIFY_SOURCE=3 (#5379)
Arch Linux is discussing increasing to 3, so we should probably keep up:

https://gitlab.archlinux.org/archlinux/rfcs/-/merge_requests/17
2023-01-19 08:37:46 +01:00
Orestis Floros
9c8746c00f Merge pull request #5355 from orestisfl/5346/do-not-canonicalize-nonprimary
Do not canonicalize "nonprimary" output for i3bar
2023-01-07 10:02:45 +01:00
Orestis Floros
fa25afedd2 Do not canonicalize "nonprimary" output for i3bar
Fixes #5346
2023-01-06 22:30:56 +01:00
Michael Stapelberg
46de32eedd GitHub Actions: remove i386 autobuild packages (#5345)
They are newly failing since the previous commit (upgrading to Ubuntu focal),
so instead of debugging what the issue is, let’s just remove them entirely.
Not many i386 users are left, as the world is on amd64 and arm64 these days.
2023-01-02 12:24:54 +01:00
Michael Stapelberg
944a262688 GitHub Actions: build Ubuntu packages using Ubuntu focal (#5344)
This is required to satisfy our meson.build minimal Meson version.
2023-01-02 12:10:36 +01:00
Michael Stapelberg
aaee2b3eae free some heap allocations to satisfy LeakSanitizer 2023-01-02 11:36:37 +01:00
Michael Stapelberg
dfb3850989 fix reload binding memory issue: copy current_binding_mode 2023-01-02 11:36:37 +01:00
Michael Stapelberg
90d7b9769c meson: specify check: false on run_command
We use run_command for conditionals, meaning meson execution should not stop
when the command returns false. This change keeps our meson setup working
throughout the upcoming change of default behavior (check: true).
2023-01-02 11:36:37 +01:00
Michael Stapelberg
16f83396b4 GitHub Actions: switch to meson setup subcommand
Using “meson” instead of “meson setup” results in a warning.
2023-01-02 11:36:37 +01:00
Michael Stapelberg
8fe28d1a95 fix -Wmaybe-unused and -Wstringop-truncation warnings 2023-01-02 11:36:37 +01:00
Michael Stapelberg
d06e87eb8d GitHub Actions: build with -D_FORTIFY_SOURCE=2
This requires --buildtype=debugoptimized (or --buildtype=release, but
optimizations need to be enabled), and will allow us to keep the i3 build free
of warnings during development.

Distributions like Debian build with -D_FORTIFY_SOURCE=2.
2023-01-02 11:36:37 +01:00
Michael Stapelberg
3e184daf29 release.sh: update after 4.22 release 2023-01-02 11:36:37 +01:00
Michael Stapelberg
7d3a3ae0fb clean up old release notes 2023-01-02 09:49:20 +01:00
Michael Stapelberg
6984dff01a debian: update changelog 2023-01-02 09:46:32 +01:00
Michael Stapelberg
47b2caa116 Merge branch 'release-4.22' 2023-01-02 09:39:12 +01:00
Michael Stapelberg
57f984ae67 Restore non-git version suffix 2023-01-02 09:39:12 +01:00
Michael Stapelberg
b85da284a7 release i3 4.22 2023-01-02 09:39:00 +01:00
Michael Stapelberg
ab6f1fd160 fix focus <direction> with negative gaps (#5333)
fixes #5293
2022-12-21 08:11:51 +01:00
Orestis Floros
ed690c7ba0 Merge pull request #5324 from orestisfl/5323/mode-in-binding-event
Add "mode" field in binding event
2022-12-14 14:24:05 +01:00
Orestis Floros
d5c8319b6c Add "mode" field in binding event
This does *not* go in the binding object to reflect the same hierarchy
of the config file: a mode is a collection of bindings.

Fixes #5323
2022-12-14 13:23:12 +01:00
Demian
1786b13f0d i3-dmenu-desktop: Allow more than one --entry-type (#5294)
Unlike in the man page, only one --entry-type is reasonable possible.
On using multiple --entry-types and a command offers multiple, duplicates are removed i3-dmenu-desktop.
See more at #5291

added --show-duplicates flag for this
2022-12-06 17:23:10 +01:00
Michael Stapelberg
fd95a47183 Update to clang-format-12 (as 10 is no longer installable) (#5317)
No changes to the code are needed.
2022-12-06 11:06:18 +01:00
Orestis Floros
30131ed697 Support nonprimary output keyword (#5273)
Fixes #4878
2022-11-19 18:11:26 +01:00
Orestis Floros
a1e4b44955 command.spec: Put cmd_border stuff together (#5266) 2022-11-13 16:22:41 +01:00
Orestis Floros
029cb8af19 Use mask to determine workspace gaps assignments (#5283)
Fixes #5282
2022-11-13 16:03:58 +01:00
Orestis Floros
60c3fedb73 window_update_motif_hints: Do not assert that the property will always be there (#5281)
Fixes #5280
2022-11-13 10:48:26 +01:00
Michael Stapelberg
96614a2f32 apply updated workspace gap assignments after reload (#5279)
Fixes https://github.com/i3/i3/issues/5257
2022-11-12 17:32:30 +01:00
Michael Stapelberg
1ba0eaca22 Make floating_from and tiling_from criterion work in commands, too (#5278)
Fixes https://github.com/i3/i3/issues/5258
2022-11-12 16:44:08 +01:00
Michael Stapelberg
2ac6180b90 gaps: position graphical resize bar in the middle between windows (#5277)
Fixes https://github.com/i3/i3/issues/5256
2022-11-12 15:48:07 +01:00
Michael Stapelberg
170a322cc2 fix: prevent gaps inside floating split containers (#5276)
Fixes https://github.com/i3/i3/issues/5272
2022-11-12 14:58:13 +01:00
Michael Stapelberg
d130126204 gaps: fix inner gaps for stacked/tabbed containers in splith/splitv (#5275)
Fixes https://github.com/i3/i3/issues/5261
2022-11-12 14:08:13 +01:00
Michael Stapelberg
2b236955bd use con_border_style() to fix titles in stacked/tabbed containers (#5274)
Previously, the code was directly accessing con->border_style, which circumvents
the special-casing for stacked/tabbed containers that forces window titles even
for title-less containers.

Fixes https://github.com/i3/i3/issues/5269
2022-11-12 12:43:55 +01:00
Orestis Floros
be27a2f50d userguide: gaps: mention minimum version (#5265) 2022-11-08 17:14:22 +01:00
Michael Stapelberg
804bca3a9a gaps: make workspace gap assignments order-independent (#5259)
This commit moves subtracting the global gaps from the workspace gaps:
previously, this calculation was done while parsing the configuration
(order dependent), now it’s done at workspace assignment evaluation time.

related to https://github.com/i3/i3/issues/3724

fixes https://github.com/i3/i3/issues/5253
2022-11-07 19:02:57 +01:00
Michael Stapelberg
14795c303c fix title bar rendering with hide_edge_borders smart (#5260)
related to https://github.com/i3/i3/pull/5245

fixes https://github.com/i3/i3/issues/5254
2022-11-07 19:01:58 +01:00
Michael Stapelberg
e6a28b9475 gaps: change workspace rendering to fix sizes with large outer gaps (#5252)
Currently, containers only consider their neighbors and screen edges.

If >2 containers are in a line, the outer containers adjust from outer gaps, but
the middle containers know nothing of this and only consider the inner gaps.

When the outer gaps differ substantially from the inner gaps, the left-most and
right-most containers are smaller as only they adjust for the larger outer gaps.

This commit changes the rendering: containers are now always inset by their
inner gap settings, and workspace containers is now inset by the outer gap
settings.

The result is that many tiled containers have the same size, and the gaps
overall work as the user might expect them to previous combinations of
outer/inner gap settings still produce the same result, albeit with fixed
outer-most sizes.

fixes https://github.com/Airblader/i3/issues/22

related to https://github.com/i3/i3/issues/3724

Co-authored-by: Cameron Leger <contact@cameronleger.com>
2022-11-06 21:22:21 +01:00
Michael Stapelberg
69e13d7821 Revert "gaps: use logical_px() to work correctly on hi-dpi monitors" (#5251)
This reverts commit 6b658f88be.

The commit was misguided: the pixel values are already run through logical_px()
when parsing the configuration directive or command, so they should not be run
through another logical_px() pass at rendering time.
2022-11-06 19:42:30 +01:00
Michael Stapelberg
2a91514a31 t/548-motif-hints: add missing $x->flush after $x->change_property
I noticed the test was flaky before, possibly this fixes it.

related to #3009
2022-11-06 18:18:23 +01:00
Orestis Floros
9dcf37b428 Fix motif behavior according to spec
See https://linux.die.net/man/3/vendorshell
The important part is:
> MWM_DECOR_ALL
> All decorations *except* those specified by other flag bits that are set
2022-11-06 18:18:23 +01:00
Michael Stapelberg
9e3a9e8225 Allow text drawing to use the alpha channel. (#5246)
This is the last remaining diff from the i3-gaps tree.

related to https://github.com/i3/i3/issues/3724

Tested using the following config with picom:

bar {
	i3bar_command i3bar -t
	status_command i3status
	colors {
		# fully	transparent text on opaque background:
		statusline #ffffff00
		background #000000ff
	}
}
2022-11-06 12:43:37 +01:00
Michael Stapelberg
c8fd8eff21 userguide: document smart_borders 2022-11-06 12:43:01 +01:00
Michael Stapelberg
6fe625f469 userguide: document hide_edge_borders smart_no_gaps
related to https://github.com/i3/i3/issues/3724
2022-11-06 12:43:01 +01:00
Michael Stapelberg
d26ddcbfe5 draw leaf window decorations on ->frame instead of ->parent->frame
related to https://github.com/i3/i3/issues/3724
fixes https://github.com/i3/i3/issues/1966
2022-11-05 15:58:15 +01:00
Michael Stapelberg
6e6af01b7a draw_util: refactor surface_initialized macro into function
This makes it possible to set a breakpoint in gdb on a line in the function and
get a backtrace of un-initialized surface access.
2022-11-05 15:58:15 +01:00
Michael Stapelberg
a59423df81 check-spelling: add another false positive (#5247) 2022-11-05 15:56:36 +01:00
Michael Stapelberg
2bfa06b7df meson.build: include new gaps1920.png file in dist tarball (#5242) 2022-11-01 18:26:57 +01:00
Michael Stapelberg
9c9b110ed1 config.spec: add missing smart_gaps inverse_outer comment 2022-11-01 17:55:46 +01:00
Michael Stapelberg
588cc4c79c refactor cmd_gaps to get rid of all #define
I’m still not 100% happy with how the function turned out (it still does too
many things at once), but this seems like an improvement — at least reading and
navigating the code with LSP now works better.
2022-11-01 17:55:46 +01:00
Michael Stapelberg
4d0323fa9e t/319-gaps.t: also test the gaps command 2022-11-01 17:55:46 +01:00
Michael Stapelberg
3f400b8ad0 move gaps-specific logic out of con.c and render.c into gaps.c 2022-11-01 17:55:46 +01:00
Michael Stapelberg
a5791b2e64 gaps: allow optional px suffix 2022-11-01 17:55:46 +01:00
Michael Stapelberg
b2c696f680 add basic gaps test 2022-11-01 17:55:46 +01:00
Michael Stapelberg
b82b3e85da userguide: document gaps config directive and gaps command 2022-11-01 17:55:46 +01:00
Michael Stapelberg
6b658f88be gaps: use logical_px() to work correctly on hi-dpi monitors 2022-11-01 17:55:46 +01:00
Michael Stapelberg
5b0f848a40 Fix config.spec comment 2022-11-01 17:55:46 +01:00
Michael Stapelberg
9ac027234b refactor render_con() global parameter into should_inset_con()
This bundles the logic all in one place and thereby makes it a little easier to
understand.
2022-11-01 17:55:46 +01:00
Michael Stapelberg
2fbb36b95f remove dead code: window_rect is overwritten a few lines below 2022-11-01 17:55:46 +01:00
Michael Stapelberg
b825dc124a Merge gaps support as-is
This code was copied over unmodified from https://github.com/Airblader/i3-gaps.

I have split out the differences between i3-gaps and i3 into three areas:
1. Gaps
2. i3bar height
3. rgba colors
2022-11-01 17:55:46 +01:00
Michael Stapelberg
0b89d4b2a7 implement bar { padding } config directive
related to https://github.com/i3/i3/issues/3724
related to https://github.com/i3/i3/pull/4288
fixes https://github.com/i3/i3/issues/3721
2022-10-30 22:22:08 +01:00
Michael Stapelberg
327bca26d8 add test for bar { height } 2022-10-30 22:22:08 +01:00
Michael Stapelberg
c45342e74f Merge support for the bar { height } option as-is from i3-gaps
related to https://github.com/i3/i3/issues/3724
related to https://github.com/i3/i3/issues/3721

In a follow-up commit, we can evolve this into the padding directive as
discussed on issue #3721.
2022-10-30 22:22:08 +01:00
Michael Stapelberg
62eb0033b1 docs/userguide: fix asciidoc block syntax (for asciidoctor)
asciidoctor is a bit stricter in what it accepts: the leading and trailing lines
need to have the exact same number of characters, and apparently there needs to
be a blank line after the trailing delimiter line.
2022-10-30 22:22:08 +01:00
Michael Stapelberg
a68eb3a71e delete old release notes 2022-10-30 22:22:08 +01:00
Orestis Floros
080c73d1a4 i3-dmenu-desktop: ignore SIGPIPE when writing to dmenu (#5228)
Fixes broken test
2022-10-29 08:20:09 +02:00
Michael Stapelberg
1f53ae4614 debian: update changelog 2022-10-24 21:33:43 +02:00
Michael Stapelberg
23bc304477 Update debian/changelog 2022-10-24 21:23:07 +02:00
Michael Stapelberg
c6bfd05276 Merge branch 'stable' into next 2022-10-24 21:23:07 +02:00
Michael Stapelberg
85252a3bd1 Merge branch 'release-4.21.1' 2022-10-24 21:23:06 +02:00
Michael Stapelberg
9ffcc51183 Restore non-git version suffix 2022-10-24 21:23:06 +02:00
Michael Stapelberg
39afa033e4 release i3 4.21.1 2022-10-24 21:22:50 +02:00
Michael Stapelberg
3b9d70af41 tiling drag: only start when there are drop targets (#5213)
This prevents potentially confusing drag & drop on fullscreen containers and
only-containers on workspaces.

fixes https://github.com/i3/i3/issues/5184
2022-10-24 21:13:03 +02:00
Michael Stapelberg
c55b52a7cc tiling drag: ignore scratchpad windows when locating drop targets (#5211)
fixes https://github.com/i3/i3/issues/5170
2022-10-24 21:13:00 +02:00
Michael Stapelberg
131b0c5b3d make tiling drag configurable
fixes https://github.com/i3/i3/issues/5155
2022-10-24 21:12:58 +02:00
Michael Stapelberg
aa876585e8 tiling drag: left-click needs threshold, mod-click doesn’t
related to https://github.com/i3/i3/issues/5155
2022-10-24 21:12:56 +02:00
Michael Stapelberg
f1754e12c0 increase drag threshold, run it through logical_px()
related to https://github.com/i3/i3/issues/5155
2022-10-24 21:12:53 +02:00
Michael Stapelberg
e12d2f6a1d tiling drag: fix cursor (wrong argument passed) (#5207)
Currently, the cursor is XCURSOR_CURSOR_TOP_RIGHT_CORNER (== BORDER_TOP),
but the intended cursor was XCURSOR_CURSOR_MOVE.

noticed this as part of https://github.com/i3/i3/issues/5198
2022-10-24 21:12:51 +02:00
Michael Stapelberg
b88ca36a5a tiling drag: allow click immediately, to focus on decoration click (#5206)
With the introduction of tiling drag, i3’s click behavior in window decorations
changed: before tiling drag, in a tabbed or stacked container, a window would be
focused/raised on mouse down. After tiling drag, on mouse up.

This commit sends XCB_ALLOW_REPLAY_POINTER before running the tiling drag code,
thereby restoring the focus/raise-on-mouse-down behavior without affecting the
tiling drag operation.

fixes https://github.com/i3/i3/issues/5169
2022-10-24 21:12:48 +02:00
bodea
7abd58abf2 Escape ~ to prevent interpretation as subscript. (#5168) 2022-10-24 21:12:43 +02:00
Matias Goldfeld
d62183a2b8 Fix segfault during config validation (#5167) (#5173)
Configs with bar blocks will segfault during validation since they
copy the i3 font which is not set during validation.
2022-10-24 21:12:38 +02:00
Tudor Brindus
9d6a8735eb Raise floating windows when their border is clicked (#5196)
This logic already existed for `floating_drag_window`, but we need it
for `floating_resize_window` too.

Fixes #5195.
2022-10-24 21:12:30 +02:00
Michael Stapelberg
decc37eba1 Fix i3-dmenu-desktop quoting (#5162)
Commit 70f23caa9a introduced new issues.

Instead of distinguishing " and \, as that commit attempted,
let’s instead keep the level of escaping by escaping each backslash,
just like each double quote.

I tested this with:

    # recommended way to quote $ and " in quoted arguments, not ambiguous
    Exec=/tmp/logargs "hello \\$PWD \\"and\\" more"

    # permitted way to quote $ and " in quoted arguments, but ambiguous
    Exec=/tmp/logargs "hello \$PWD \"and\" more"

    # permitted way to quote arguments, slightly unusual to quote first arg
    Exec="/tmp/logargs" hey

    # a complicated shell expression, not ambiguous
    Exec=sh -c "if [ -n \\"\\$*\\" ]; then exec /tmp/logargs --alternate-editor= --display=\\"\\$DISPLAY\\" \\"\\$@\\"; else exec /tmp/logargs --alternate-editor= --create-frame; fi" placeholder %F

related to https://github.com/i3/i3/issues/4697 (electrum, original)
related to https://github.com/i3/i3/issues/5152 (phpstorm, breakage)
related to https://github.com/i3/i3/issues/5156 (emacsclient, breakage)
2022-10-24 21:12:27 +02:00
Orestis Floros
3f58d51ec6 Fix motif logic for new floats
- manage.c still used wrong `motif_border_style == BS_NORMAL`
- container must be set to floating first for correct code path and
  correct max_user_border_style to be used in con_set_border_style
- Motif test now includes default_floating_border
2022-10-24 21:12:25 +02:00
Orestis Floros
304e815ed4 Motif hints: Respect maximum border style configuration set by user
Context:
Motif hints [1] allow applications to request specific window manager
frame decorations. Most applications like alacritty, chromium, and
godot, use the hints as a binary flag, setting or un-setting
`MWM_DECOR_ALL`.
Previously [2], we had disallowed applications to set the "normal"
border style through motif hints. This effectively meant that users that
had set `default_border pixel` would not see applications spawning with
normal decorations [3].
However, that meant that applications like godot [4] could not toggle
their border between none and normal so the behaviour changed with
v4.21 [5].
That change however also allowed applications to override the default
none/pixel border style the user set. For example, alacritty can be
configured to either have all or no decorations [6] and they always set
the motif hint on startup, completely overriding i3 user's preference:
1. If decorations are disabled with alacritty's config then they will
   override `default_border normal` and no title will be used.
2. If decorations are enabled (also the default behavior) with
   alacritty's config then they will override `default_border pixel` and
   a title will be used.

This patch redefines how we interpret motif hints. When a client sets
`MWM_DECOR_ALL`, we interpret it as "the maximum decoration the user has
allowed for this window". I.e., if a client was all decorations and the
user expects the window to not have a title, we don't include the title
in "all" decorations.

The user's preference is determined by these:
1. For new tiling windows, as set by `default_border`
2. For new floating windows, as set by `default_floating_border`
3. For all windows that the user runs the `border` command, whatever is
   the result of that command for that window.
Example:
- User opens new tiling window with `default_border pixel` => maximum
  decoration = PIXEL
- Window requests all/title decorations => i3 enforces the user maximum
  decoration, PIXEL (no change)
- Window requests no decorations => i3 accepts it and sets border to
  NONE, maximum decoration remains PIXEL
- User toggles the border, next style is NORMAL => maximum decoration is
  now NORMAL
- Window requests no decorations => i3 accepts it and sets border to
  NONE
- Window requests all/title decorations => i3 accepts it and sets the
  maximum border, NORMAL
- User toggles the border, next style is NONE => maximum decoration is
  now NONE
- Window requests all/title decorations => i3 enforces the user maximum
  decoration, NONE (no change)

With this, we will still allow behaviour where windows can toggle their
border style with motif hints [4][7].

Reference/footnotes:
[1]: https://linux.die.net/man/3/vendorshell
[2]: https://github.com/i3/i3/pull/2386
[3]: Notice how there is apparently a gap because `default border none`
settings would not be respected if an application wanted just "border"
decorations but this was never reported, probably because of the rare
conjunction of applications requesting that and users defaulting to none
borders.
[4]: https://github.com/godotengine/godot/issues/40037
[5]: https://github.com/i3/i3/pull/5135
[6]: Set by an underlying library here:
fafdedfb7d/src/platform_impl/linux/x11/util/hint.rs (L113-L142)
called by alactitty here:
4ddb608563/alacritty/src/display/window.rs (L341)
[7]: https://github.com/i3/i3/issues/3678

Closes #3678
Fixes #5149
2022-10-24 21:12:23 +02:00
Orestis Floros
0af2bac9ed Order border_style_t enum according to amount of decoration
The only place where this matters is with command `border toggle` which
cycles through them. Luckily, the behaviour does not change because the
order is the same with the new enum.
2022-10-24 21:12:20 +02:00
Erich Heine
5e4ed2fc75 Adds sticky field to get_tree reply in ipc doc 2022-10-24 21:12:00 +02:00
Orestis Floros
de3fc07123 Update actions/checkout to v3 (#5226)
Fixes #5225
2022-10-24 21:01:23 +02:00
Orestis Floros
b18b80ca40 i3-dmenu-desktop test: Do not autostart i3 (#5224)
This actually fixes a hang that happens on my machine for some reason.
Regardless, starting i3 is not necessary for this test.
2022-10-24 19:57:07 +02:00
Michael Stapelberg
5e759ed424 tiling drag: only start when there are drop targets (#5213)
This prevents potentially confusing drag & drop on fullscreen containers and
only-containers on workspaces.

fixes https://github.com/i3/i3/issues/5184
2022-10-18 22:10:03 +02:00
Michael Stapelberg
941229ee62 tiling drag: ignore scratchpad windows when locating drop targets (#5211)
fixes https://github.com/i3/i3/issues/5170
2022-10-16 22:12:45 +02:00
Michael Stapelberg
55d400b17d make tiling drag configurable
fixes https://github.com/i3/i3/issues/5155
2022-10-16 18:21:08 +02:00
Michael Stapelberg
2ba393f084 tiling drag: left-click needs threshold, mod-click doesn’t
related to https://github.com/i3/i3/issues/5155
2022-10-16 18:21:08 +02:00
Michael Stapelberg
6479cb7deb increase drag threshold, run it through logical_px()
related to https://github.com/i3/i3/issues/5155
2022-10-16 18:21:08 +02:00
Michael Stapelberg
8128774386 tiling drag: fix cursor (wrong argument passed) (#5207)
Currently, the cursor is XCURSOR_CURSOR_TOP_RIGHT_CORNER (== BORDER_TOP),
but the intended cursor was XCURSOR_CURSOR_MOVE.

noticed this as part of https://github.com/i3/i3/issues/5198
2022-10-16 16:53:15 +02:00
Michael Stapelberg
a6c86fd794 tiling drag: allow click immediately, to focus on decoration click (#5206)
With the introduction of tiling drag, i3’s click behavior in window decorations
changed: before tiling drag, in a tabbed or stacked container, a window would be
focused/raised on mouse down. After tiling drag, on mouse up.

This commit sends XCB_ALLOW_REPLAY_POINTER before running the tiling drag code,
thereby restoring the focus/raise-on-mouse-down behavior without affecting the
tiling drag operation.

fixes https://github.com/i3/i3/issues/5169
2022-10-16 15:21:22 +02:00
bodea
4f3d4c26f6 Escape ~ to prevent interpretation as subscript. (#5168) 2022-10-11 18:09:26 +02:00
Matias Goldfeld
c5dc0d8c93 Fix segfault during config validation (#5167) (#5173)
Configs with bar blocks will segfault during validation since they
copy the i3 font which is not set during validation.
2022-10-10 18:52:55 +02:00
Tudor Brindus
06e31ece8f Raise floating windows when their border is clicked (#5196)
This logic already existed for `floating_drag_window`, but we need it
for `floating_resize_window` too.

Fixes #5195.
2022-10-10 14:22:55 +02:00
Michael Stapelberg
812ec43d46 Fix i3-dmenu-desktop quoting (#5162)
Commit 70f23caa9a introduced new issues.

Instead of distinguishing " and \, as that commit attempted,
let’s instead keep the level of escaping by escaping each backslash,
just like each double quote.

I tested this with:

    # recommended way to quote $ and " in quoted arguments, not ambiguous
    Exec=/tmp/logargs "hello \\$PWD \\"and\\" more"

    # permitted way to quote $ and " in quoted arguments, but ambiguous
    Exec=/tmp/logargs "hello \$PWD \"and\" more"

    # permitted way to quote arguments, slightly unusual to quote first arg
    Exec="/tmp/logargs" hey

    # a complicated shell expression, not ambiguous
    Exec=sh -c "if [ -n \\"\\$*\\" ]; then exec /tmp/logargs --alternate-editor= --display=\\"\\$DISPLAY\\" \\"\\$@\\"; else exec /tmp/logargs --alternate-editor= --create-frame; fi" placeholder %F

related to https://github.com/i3/i3/issues/4697 (electrum, original)
related to https://github.com/i3/i3/issues/5152 (phpstorm, breakage)
related to https://github.com/i3/i3/issues/5156 (emacsclient, breakage)
2022-09-28 18:29:26 +02:00
Orestis Floros
8ec41334ec Fix motif logic for new floats
- manage.c still used wrong `motif_border_style == BS_NORMAL`
- container must be set to floating first for correct code path and
  correct max_user_border_style to be used in con_set_border_style
- Motif test now includes default_floating_border
2022-09-24 20:46:47 +02:00
Orestis Floros
f6097d4a37 Motif hints: Respect maximum border style configuration set by user
Context:
Motif hints [1] allow applications to request specific window manager
frame decorations. Most applications like alacritty, chromium, and
godot, use the hints as a binary flag, setting or un-setting
`MWM_DECOR_ALL`.
Previously [2], we had disallowed applications to set the "normal"
border style through motif hints. This effectively meant that users that
had set `default_border pixel` would not see applications spawning with
normal decorations [3].
However, that meant that applications like godot [4] could not toggle
their border between none and normal so the behaviour changed with
v4.21 [5].
That change however also allowed applications to override the default
none/pixel border style the user set. For example, alacritty can be
configured to either have all or no decorations [6] and they always set
the motif hint on startup, completely overriding i3 user's preference:
1. If decorations are disabled with alacritty's config then they will
   override `default_border normal` and no title will be used.
2. If decorations are enabled (also the default behavior) with
   alacritty's config then they will override `default_border pixel` and
   a title will be used.

This patch redefines how we interpret motif hints. When a client sets
`MWM_DECOR_ALL`, we interpret it as "the maximum decoration the user has
allowed for this window". I.e., if a client was all decorations and the
user expects the window to not have a title, we don't include the title
in "all" decorations.

The user's preference is determined by these:
1. For new tiling windows, as set by `default_border`
2. For new floating windows, as set by `default_floating_border`
3. For all windows that the user runs the `border` command, whatever is
   the result of that command for that window.
Example:
- User opens new tiling window with `default_border pixel` => maximum
  decoration = PIXEL
- Window requests all/title decorations => i3 enforces the user maximum
  decoration, PIXEL (no change)
- Window requests no decorations => i3 accepts it and sets border to
  NONE, maximum decoration remains PIXEL
- User toggles the border, next style is NORMAL => maximum decoration is
  now NORMAL
- Window requests no decorations => i3 accepts it and sets border to
  NONE
- Window requests all/title decorations => i3 accepts it and sets the
  maximum border, NORMAL
- User toggles the border, next style is NONE => maximum decoration is
  now NONE
- Window requests all/title decorations => i3 enforces the user maximum
  decoration, NONE (no change)

With this, we will still allow behaviour where windows can toggle their
border style with motif hints [4][7].

Reference/footnotes:
[1]: https://linux.die.net/man/3/vendorshell
[2]: https://github.com/i3/i3/pull/2386
[3]: Notice how there is apparently a gap because `default border none`
settings would not be respected if an application wanted just "border"
decorations but this was never reported, probably because of the rare
conjunction of applications requesting that and users defaulting to none
borders.
[4]: https://github.com/godotengine/godot/issues/40037
[5]: https://github.com/i3/i3/pull/5135
[6]: Set by an underlying library here:
fafdedfb7d/src/platform_impl/linux/x11/util/hint.rs (L113-L142)
called by alactitty here:
4ddb608563/alacritty/src/display/window.rs (L341)
[7]: https://github.com/i3/i3/issues/3678

Closes #3678
Fixes #5149
2022-09-24 20:46:47 +02:00
Orestis Floros
eddced6b45 Order border_style_t enum according to amount of decoration
The only place where this matters is with command `border toggle` which
cycles through them. Luckily, the behaviour does not change because the
order is the same with the new enum.
2022-09-24 20:46:47 +02:00
Orestis Floros
8e9b29419f Merge pull request #5151 from orestisfl/release-notes
Clean up 4.21 release notes
2022-09-22 15:37:03 +02:00
Orestis Floros
016d0b5f07 Clean up 4.21 release notes 2022-09-22 15:28:53 +02:00
Michael Stapelberg
4ab34d5042 GitHub Actions: skip build + test steps in meson dist (#5148)
Originally I thought we should remove the build and test steps in favor of the
“meson dist” step. But, then we lose verbose build command lines and access
to the test logs, and having the failing test logs readily available is
critical.

So, instead, let’s make the “meson dist” step not repeat the work that was
already done in earlier steps. The Meson manual even documents the --no-tests
flag precisely for our use case:

> The meson dist command has a --no-tests option to skip build and tests steps
> of generated packages. It can be used to not waste time for example when done
> in CI that already does its own testing.

From https://mesonbuild.com/Creating-releases.html

fixes https://github.com/i3/i3/issues/5145
2022-09-21 22:19:57 +02:00
Michael Stapelberg
28671a622b release.sh: re-add warning about debian/changelog changes
As long as we push auto builder packages from our CI, we need to update the
version number via the changelog.
2022-09-21 21:58:41 +02:00
Michael Stapelberg
3e434ba0ce release.sh: latest released version numbers 2022-09-21 21:58:41 +02:00
Michael Stapelberg
6ae232a323 release.sh: fix Debian source repository configuration
Debian switched to deb822 sources.list:

https://twitter.com/zekjur/status/1572622368492888065
2022-09-21 21:58:41 +02:00
Michael Stapelberg
5caf49323c release.sh: fix regexp for updating version in index.html 2022-09-21 21:58:41 +02:00
Michael Stapelberg
12cdf435aa release.sh: update website branch name 2022-09-21 21:58:41 +02:00
Michael Stapelberg
d7b9a45ff3 release.sh: remove dput instruction
Package maintenance is done by sur5r these days (thanks!)
2022-09-21 21:58:41 +02:00
Erich Heine
0967021858 Adds sticky field to get_tree reply in ipc doc 2022-09-21 19:27:32 +02:00
Michael Stapelberg
f0856c285c debian: update changelog 2022-09-21 18:38:54 +02:00
Michael Stapelberg
ac95dffd6b Update debian/changelog 2022-09-21 18:26:55 +02:00
Michael Stapelberg
2bdcae8149 Merge branch 'release-4.21' 2022-09-21 18:26:55 +02:00
Michael Stapelberg
c0ef3caec8 Merge branch 'next' into stable 2022-09-21 18:26:55 +02:00
Michael Stapelberg
d7f4707e05 Restore non-git version suffix 2022-09-21 18:26:55 +02:00
Michael Stapelberg
5bc4280a48 release i3 4.21 2022-09-21 18:26:43 +02:00
Michael Stapelberg
8ade46bdf0 unflake t/289-ipc-shutdown-event.t (#5144)
Before this commit, the test was flaky: it relied on the Perl test process
sending the kill() system call before i3 exited. This can easily be triggered
by adding a sleep(1) after the “cmd 'exit'” line.

This is because with i3_autostart => 1 (the default), i3test.pm kills i3
or bails out if it can’t.

So, we instead set i3_autostart => 0 and launch i3 ourselves.
This will unfortunately still make the code kill i3 and bail out,
because launch_with_config updates the $i3_pid variable that i3test.pm
uses for tracking whether it should clean up i3.
The solution is to exit i3 by calling exit_gracefully,
which will make the i3test.pm state correct.

related to https://github.com/i3/i3/issues/3009
2022-09-21 17:47:40 +02:00
Orestis Floros
227c1538be Add some release notes (#5138) 2022-09-20 09:13:34 +02:00
Orestis Floros
516d442e9a tiling_drag: Correctly switch to workspace when dragging across outputs (#5141)
Fixes #5089
2022-09-20 09:13:14 +02:00
viri
8252144cc3 motif: restore BS_NORMAL correctly (#5135)
motif hints: restore BS_NORMAL correctly
2022-09-19 20:12:15 +02:00
Orestis Floros
0ac5e248f2 tiling_drag: con_rect_plus_deco_height: Fix underflow (#5129)
Fixes #5069
2022-09-17 13:00:29 +02:00
zhrvn
5ce8e3241b Fix crash in parse_file() when parsing nested variables (#5003)
Count extra_bytes correctly

If there is a variable with the same name as the rest of another
variable after removing $, then it will be counted twice. Therefore,
we need to completely replace it with spaces (variable names cannot
contain spaces) in order to correctly calculate the length of a new
string.

fixes https://github.com/i3/i3/pull/5002

Co-authored-by: Ivan Zharov <zhiv.email@gmail.com>
Co-authored-by: Michael Stapelberg <stapelberg@users.noreply.github.com>
2022-09-12 09:03:50 +02:00
zhrvn
e48b9aa284 Fix segfault with mode "default" key bindings (#5007)
ignore bindings when not in a valid mode

Co-authored-by: Ivan Zharov <zhiv.email@gmail.com>
Co-authored-by: Michael Stapelberg <stapelberg@users.noreply.github.com>
2022-09-11 15:22:01 +02:00
Michael Stapelberg
f795c2c8da testsuite: catch i3 SIGSEGV by installing SIGCHLD handler (#5123)
The testsuite already contains quite a number of SIGCHLD handler
installation/un-installations. Here is my attempt at an inventary.

1. complete-run.pl installs a SIGCHLD handler in the `start_xserver()` function
   call, which prints an error and exits when all x server processes have exited.

2. In the TestWorker child process, a SIGCHLD handler is installed to reap dead
   test child processes.

3. The TestWorker child process forks another child process for running the test
   file, where the previously installed SIGCHLD handler (point 2) is unset.

   This is where this commit comes in: it installs a SIGCHLD handler in the test
   file child process, which will trigger when the i3 subprocess dies.

4. (For completeness: i3test.pm defines an END block where it unsets the
    previous SIGCHLD handler before it kills the subprocesses.)

With this commit, when i3 segfaults, the output will look like this:

    Writing logfile to 'testsuite-2022-09-10-21-14-46-4.20-103-gb242bceb/complete-run.log'...
    [:100] /home/michael/i3/testcases/t/167-workspace_layout.t: BAILOUT
    completed 0 of 1 tests
    test /home/michael/i3/testcases/t/167-workspace_layout.t bailed out:
    could not kill i3: No such process

fixes https://github.com/i3/i3/issues/4437
2022-09-10 21:29:04 +02:00
Michael Stapelberg
4b5ead023e open_logbuffer: avoid overflow by comparing (long long) (#5113)
I tested this on a machine with 256 GB of RAM.

fixes #4906
2022-09-09 10:26:17 +02:00
Michael Stapelberg
ac368e7916 config_parser: prevent trailing whitespace in output (string → word) (#5117)
fixes https://github.com/i3/i3/issues/5064
2022-09-09 10:23:55 +02:00
Michael Stapelberg
6fb58eb841 config: set bar block font to i3-wide font *after* parsing (#5118)
Otherwise, the font directive needs to come before bar blocks,
which is surprising to users.

fixes https://github.com/i3/i3/issues/5031
2022-09-09 10:21:33 +02:00
paperluigis
4db383e430 man/i3-input: fix a typo: chose → choose (#5087) 2022-09-06 20:30:23 +02:00
mariano
e9c63d3001 Add wezterm to i3-sensible-terminal (#5107) 2022-09-06 20:23:56 +02:00
bodea
b242bcebcf i3-sensible-pager: sanitize LESS environment variable (#5111)
When an error is encountered such as "The configured command for this shortcut
could not be run successfully", the "show errors" button on i3-nagbar doesn't
work if $PAGER is less, and $LESS contains either the -E or -F flag (the
window pops up, but immediately disappears).

Strip these flags from the LESS environment variable before invoking $pager.
2022-09-06 08:42:02 +02:00
Orestis Floros
ebcd1d43ea Allow dragging tiled windows with the mouse. (#3085)
Fixes #2643

Inner drop region behaves like move to mark.
The outer region is close to the edge (currently 30px from the edge).
This will place the container as a sibling in the given direction within
the parent container. If the move direction goes against the orientation
of the parent container, tree_move() is called.

Contributors:
Co-authored-by: Orestis Floros <orestisflo@gmail.com>
See #3085
- Inner drop region behaves like move to mark
- Handle workspaces
- Fix crash when target closes
- Initiate tiling drag from titlebar
- Hide indicator until container is dragged outside of original position
- Calculate outer_threshold using percentages instead of fixed pixel
values
- Emit 'move' event properly
- Don't focus previously unfocused containers
- Use tree_split() on different orientation
- Fix redundant split containers
- DT_PARENT
- Readability & optimizations
- Limit parent threshold by render_deco_height()
- Tests
- Fullscreen container handling
- Initiate drag from title bar
- Fix issue of EnterNotify events still triggering after drag_callback
  is called
- Include decorations for drop target calculation

Co-authored-by: Michael Forster <email@michael-forster.de>
See #2178
- Original implementation of tiling drag + indicator window
> A container can be dragged by the title bar to one of the four sides
> of another container. That container will then be split either
> horizontally or vertically.

Co-authored-by: Tony Crisci <tony@dubstepdish.com>
See #2653
- Original implementation of outer/inner drop region indicator:
> There are two drop regions per direction.
>
> The inner region is closer to the center of the window. Dropping on
> this region will split the target container and put the container
> within the split at the given direction beside the target container.
>
> The outer region is close to the edge (currently 30px from the edge).
> This will place the container as a sibling in the given direction within
> the parent container.
>
> Dropping into the outer region moves the con beside the target. If the
> move direction goes against the orientation of the parent container, the
> con moves out of the row.
- Fix crash: Ignore containers without a managed window (eg i3bar)
2022-07-28 12:03:16 +02:00
Orestis Floros
807e972330 Fix Wbitwise-instead-of-logical warnings
> error: use of bitwise '|' with boolean operands
2022-07-28 09:25:55 +02:00
Michael Stapelberg
103dc7b55d fix travis/check-spelling.pl for recent Lintian changes 2022-07-28 09:25:55 +02:00
Jay Ta'ala
ddc587933d Split container parents should be redrawn when swapping child containers (within parent) (#4765)
Redraw split container parents when swapping child containers

Split container parents should be redrawn when swapping child containers  so they show the correct window ordering (note without this higher level split parent container titles will only update when changing layout or moving child cons in/out.
2022-06-30 20:21:14 +02:00
Josh Soref
458c148934 cleanup-bintray: remove remaining bintray references (#5038)
* The `cleanup-bintray.pl` script is just unused
* The `dh_builddeb` override is no longer necessary as bintray is no longer used
2022-06-30 08:21:54 +02:00
Kjetil Torgrim Homme
3597cc636e highlight the difference between "workspace N" and "workspace number N" commands 2022-06-28 00:25:06 +02:00
zhiv-git
ce2665ca96 Fix segfault when bindsym command is empty (#5001)
Remove end token from BINDCOMMAND

fixes i3/i3#5000: bindsym command cannot be empty, because NULL string
causes i3 to segfault when copied in configure_binding()
2022-06-08 22:38:21 +02:00
George Rodrigues
6ab64aa5b7 Fix typos (#4989) 2022-06-01 09:49:05 +02:00
Michael Stapelberg
612a9317b0 GitHub Actions: declare /usr/src/i3 as safe directory (#4992) 2022-05-31 18:29:36 +02:00
André Silva
dba30fc987 clear surface on x_push_node (#4577) 2022-03-28 10:18:55 +02:00
takelley1
41cd852f2f Fix typo in comment (#4936)
* Fix typo in comment

* Fix other typo in comment
2022-03-28 10:14:52 +02:00
Ingo Bürk
c822eff1bf Remove Xlib references (#4845)
* remove Xlib import from i3bar

* remove unused Xlib declarations
2022-02-10 10:25:20 +01:00
Gergely Risko
bd5cbbb010 Use proper WM_Sx registration for i3 (#4843)
i3's WM ownership is registered with the X Atom WM_S_S0 (for screen0),
instead of the correct WM_S0.  The relevant xcb-util API didn't
change, this is simply a bug in i3 that has always been there:
https://gitlab.freedesktop.org/xorg/lib/libxcb-util/-/blob/0.4.0/src/atoms.c#L65
2022-02-07 17:43:55 +01:00
Orestis Floros
70288d7b68 Merge pull request #4710 from pstray/title_window_icon-toggle
Implement title_window_icon toggle
2021-12-11 14:33:23 +01:00
Peder Stray
813336e068 Implement title_window_icon toggle
A feature described in i3/i3#4709
2021-12-11 14:20:07 +01:00
Orestis Floros
b17117190d Merge pull request #4717 from orestisfl/janitorial
Remove outdated //-style commented-out code
2021-12-06 22:55:38 +01:00
Orestis Floros
a4ac843cca Remove outdated //-style commented-out code
This makes the whole code-base have zero changes with clang-format
(v13).
2021-12-06 22:14:41 +01:00
sergio
70f23caa9a fix #4697, adds backslashes quotation for exec (#4699)
Signed-off-by: Sergio E. Nemirowski <sergio@outerface.net>
2021-12-06 09:53:50 +01:00
Chris Templin
729452448b docs: change IPC window_type value (#4668) 2021-12-01 20:38:00 +01:00
Michael Stapelberg
07ea5019c9 debian: bump to libpcre2-dev for autobuilds (#4705)
related to https://github.com/i3/i3/issues/4682
2021-11-29 23:41:07 +01:00
Uli Schlachter
6c7efc4de8 Switch from pcre to pcre2 (#4684)
The issue at [0] was opened and I just took a stab at it. I have no
prior experience with pcre and pcre2, but using [1,2] I hacked together
something that seems to work. Next, Michael told me to turn that
patch/hack into a PR, so here we are.

The dependency in meson.build now uses version:'>=10', but this is more
a random guess than actual knowledge.

There was a while loop in regex_new() that dealt with an error when pcre
was not compiled with UTF-8 support. This loop uses a magic constant of
32 for the error code. I just dropped this loop, because I was just
writing a hack and did not intend to turn this into a PR. Also, a quick "grep
32 /usr/include/pcre.h" does not find anything useful, so... *shrug*

pcre_study() was removed without replacement, so the corresponding code
is also simply removed.

Testing done: The test suite passes for me. YMMV.

[0]: https://github.com/i3/i3/issues/4682
[1]: https://www.pcre.org/current/doc/html/pcre2api.html
[2]: https://www.pcre.org/current/doc/html/pcre2demo.html

Signed-off-by: Uli Schlachter <psychon@znc.in>
Fixes: https://github.com/i3/i3/issues/4682
2021-11-29 18:20:54 +01:00
Ingo Bürk
298c3aede9 Merge pull request #4698 from orestisfl/user_output_names_find_next
user_output_names_find_next: Always initialize target_output
2021-11-27 23:38:13 +01:00
Orestis Floros
854616ed2e user_output_names_find_next: Always initialize target_output
This way, if the user has provided a valid, existing output in the list
of outputs, the focus & move workspace to output commands will not
report a misleading failure.

Side-effect is that the command code will try to execute a no-op e.g. by
moving the workspace to the output it already is on. But that's what the
user is actually requesting in this case and it shouldn't be a problem.

Fixes #4691
2021-11-27 22:52:48 +01:00
Ingo Bürk
d44e1442c2 Merge pull request #4485 from ToxicGLaDOS/long_commands_crash
Fix bug where long commands crash i3
2021-11-18 22:35:23 +01:00
Ingo Bürk
46bdd537a8 Merge pull request #4622 from orestisfl/focus-output-next
Focus output next
2021-11-18 22:33:13 +01:00
Ingo Bürk
52c831ca22 Merge pull request #4651 from orestisfl/docs-ipc-socket
docs: Make more clear that an IPC socket is always created
2021-11-18 22:31:35 +01:00
Ingo Bürk
b7056a82f4 Merge pull request #4667 from orestisfl/transient_for-loop
Fix transient_for endless loop
2021-11-18 22:31:19 +01:00
Orestis Floros
e1d3e6b2f6 Fix transient_for endless loop
Other approaches would be:
- Slow/fast pointer technique.
- Using a set/associative map to save 'seen' nodes. i3 does not have
  such data structure.

Counting the total amount of windows is the simpler to implement.

I've also extracted the logic in a function and re-used it in render.c.

Fixes #4404
2021-11-11 20:29:02 +01:00
Orestis Floros
43e805a00d Merge pull request #4656 from stapelberg/flaky
Fix flaky tests: sync after kill
2021-11-07 13:51:55 +01:00
Michael Stapelberg
673185a63b t/219-ipc-window-focus.t: sync after kill 2021-11-07 11:56:06 +01:00
Michael Stapelberg
c4fdcc5dbc fix flaky testcases/t/185-scratchpad.t: sync after kill command 2021-11-07 11:47:42 +01:00
Orestis Floros
8e9da491f4 Merge pull request #4648 from orestisfl/formatting-new-action
Move clang-format and safe-wrappers check in new job
2021-11-06 14:16:09 +01:00
Orestis Floros
7f3316269d Release notes script fixes (#4652)
* release-notes: Sort filenames

* release-notes: Fix print-urls
2021-11-06 14:13:18 +01:00
Orestis Floros
93b2b44893 cmd_focus_output: Multiple outputs 2021-11-05 18:34:54 +01:00
Orestis Floros
34ef7f8c33 tests: Extract focused_output in library 2021-11-05 18:33:14 +01:00
Orestis Floros
bc4f35a6bb Extract common functions out of cmd_move_con_to_output 2021-11-05 18:33:14 +01:00
Orestis Floros
680ddc7e10 Move clang-format, safe-wrappers, release notes check in new job
Benefits:
- Faster feedback in case of error
- More checks run in parallel
- Removes stuff from Dockerfiles
- Uses newest available clang-format (in ubuntu repos)
2021-11-05 18:29:42 +01:00
Ingo Bürk
20d0591e77 Merge pull request #4604 from orestisfl/fix-focus-warp
Fix focus issue when moving windows across outputs
2021-11-05 15:32:07 +01:00
Orestis Floros
117fb13177 Merge pull request #4650 from orestisfl/focused_tab_title
Add title tab color
2021-11-05 15:24:25 +01:00
Anton Älgmyr
863a9eada4 Fix focus issue when moving windows across outputs
Fixes #3518

This fix avoids the issue of consuming our own warp events by never
calling warp on the wrong container.

Tried to add a test but it also succeeds with version before patch,
see #4604 for discussion

Co-Authored-By: Orestis <orestisflo@gmail.com>
2021-11-05 15:21:59 +01:00
Orestis Floros
f36050b303 Add title tab color
Fixes #4575
2021-11-05 15:19:18 +01:00
Orestis Floros
3c81e8ddaa Merge pull request #4610 from orestisfl/release-notes-next
Keep each release note in a single file
2021-11-05 10:36:24 +01:00
Orestis Floros
38cf8a21ef Update release notes
https://github.com/i3/i3/pull/4638
https://github.com/i3/i3/pull/4647
2021-11-05 10:13:30 +01:00
Orestis Floros
c083e023b6 Keep each release note in a single file
To avoid annoying merge conflicts.

Perl script is provided for convenience and simple format checking in
PRs.
2021-11-05 10:13:22 +01:00
Orestis Floros
220144361a userguide: Make more clear that an IPC socket is always created
Follow-up from #4647
2021-11-04 21:47:55 +01:00
Orestis Floros
3e0fc25b6b Merge pull request #4649 from orestisfl/fix-errno
Fix compilation error on debian
2021-11-04 19:31:44 +01:00
Orestis Floros
ce0d08cc6d Fix compilation error on debian 2021-11-04 19:27:40 +01:00
Uli Schlachter
445cea8e3a Do not replace existing IPC socket (#4638)
Imagine you are a happy i3 user and want to also write patches for i3.
You use "Xephyr :1" to get another X11 server and then start your newly
build i3 in it with "DISPLAY=:1 ./i3". You test your changes and
everything seems fine. You are happy. Later that day, you try to log
out, but the $mod+Shift+e key binding from the default config no longer
works. i3-msg cannot connect to the IPC socket because "No such file or
directory". What is going on?

The problem boils down to $I3SOCK having something like two meanings.
When i3 starts, it sets the environment variable $I3SOCK to the path of
its IPC socket. That way, any process started from i3 inherits this and
i3-msg knows how to talk to i3. However, when this variable is already
set when i3 starts, then i3 will replace the existing socket. Thus, in
the earlier experiments, the "separate i3" that was used for
experimenting stole the "main i3"'s socket and replaced it with its own.
When it exited, it deleted that socket.

This commit adds half a work around to this problem: When creating the
IPC socket, i3 will now first try to connect() to the socket. If this
succeeds, it will complain and refuse to use this socket. If the
connect() call fails, it will proceed as usual and create the socket.

Note that trying to connect() to a socket that no process listens on
will fail. Thus, this new code only really "triggers" when some process
is actively listening on this socket and accepting connections.

Example output for when the socket is already in use:

$ I3SOCK=/tmp/sdfdsf DISPLAY=:2 ./i3
31.10.2021 17:03:55 - [libi3] ERROR: Refusing to create UNIX socket at /tmp/sdfdsf: Socket is already in use
31.10.2021 17:03:55 - ERROR: Could not create the IPC socket, IPC disabled

This commit sadly only provides part of the solution. i3 will still
delete the socket when shutting down, even if it failed to create the
IPC socket. Thus, the ipc socket will still break, but now only later.
This will be fixed separately.

First-step-towards-fixing: https://github.com/i3/i3/issues/4381
Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-11-04 17:22:22 +01:00
Michael Stapelberg
d83940a8fc Merge branch 'release-4.20.1' 2021-11-03 09:23:23 +01:00
Michael Stapelberg
53f52beb09 Update debian/changelog 2021-11-03 09:23:23 +01:00
Michael Stapelberg
63aec362c9 Restore non-git version suffix 2021-11-03 09:23:23 +01:00
Michael Stapelberg
f97268dbd8 Merge branch 'stable' into next 2021-11-03 09:23:23 +01:00
Michael Stapelberg
b952e74145 release i3 4.20.1 2021-11-03 09:23:07 +01:00
Michael Stapelberg
6fe33394e1 post-release release.sh changes 2021-11-03 09:14:58 +01:00
Baptiste Daroussin
c6ea9bbace portability: use getcwd() instead of get_current_dir_name() (#4645) 2021-11-03 09:03:33 +01:00
Baptiste Daroussin
058ebb0d42 portability: add missing headers
libi3.h defins a macors with the default modes to create a directory.
Those modes are defined in sys/stat.h, so include the necessary header
2021-11-03 09:03:28 +01:00
lycurgus
884717489f Correct short form of raw option in i3-msg manpage 2021-11-03 09:03:14 +01:00
Jakob Haufe
b895d9d3cc Extend title length for xmlto
man.th.title.max.length defaults to 20 which leads to a broken .TH line
for i3-migrate-config-to-v4.1:

.TH "I3\-MIGRATE\-CONFIG\" "1" "02/01/2021" "i3 4\&.19\&.1" "i3 Manual"
2021-11-03 09:03:09 +01:00
rvalieris
39ff5699d6 check for both arguments NULL on strings_differ 2021-11-03 09:03:03 +01:00
Orestis Floros
ecc4f91fb4 Merge pull request #4647 from psychon/refuse-start-without-ipc-socket
Refuse to start without IPC socket
2021-11-02 23:13:52 +01:00
Uli Schlachter
03a0375e7f Refuse to start without IPC socket
This change was (basically) suggested by @orestisfl in
https://github.com/i3/i3/pull/4638#issuecomment-958093518

Testing done:

$ I3SOCK=/dev/null DISPLAY=:2 LC_ALL=C build/i3
bind(): Address already in use
i3: Could not create the IPC socket: /dev/null

Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-11-02 21:08:42 +01:00
Baptiste Daroussin
aceb36ae60 portability: use getcwd() instead of get_current_dir_name() (#4645) 2021-11-02 20:14:24 +01:00
Orestis Floros
e4d3ec9217 Merge pull request #4644 from bapt/portability-nits
portability: add missing headers
2021-11-02 19:50:58 +01:00
Baptiste Daroussin
e4a3f6e4cd portability: add missing headers
libi3.h defins a macors with the default modes to create a directory.
Those modes are defined in sys/stat.h, so include the necessary header
2021-11-02 10:43:27 +01:00
Orestis Floros
40b0c8e5b8 Merge pull request #4618 from Jonta/patch-3
Docs: Testsuite: Grammar
2021-11-01 21:42:49 +01:00
Orestis Floros
b0632bddfc Merge pull request #4637 from lycurgus/patch-1
Correct short form of raw option in i3-msg manpage
2021-10-31 09:36:30 +01:00
lycurgus
8e46943985 Correct short form of raw option in i3-msg manpage 2021-10-31 19:01:32 +11:00
Ingo Bürk
36f9f86714 Merge pull request #4629 from sur5r/xmlto-titlelength
Extend title length for xmlto
2021-10-28 08:40:08 +02:00
Jakob Haufe
a352cecc6a Extend title length for xmlto
man.th.title.max.length defaults to 20 which leads to a broken .TH line
for i3-migrate-config-to-v4.1:

.TH "I3\-MIGRATE\-CONFIG\" "1" "02/01/2021" "i3 4\&.19\&.1" "i3 Manual"
2021-10-27 19:15:01 +00:00
Jonta
9cc9cc2be0 Docs: Testsuite: Grammar
Incorporating orestisfl's suggestion, and expanding a little
2021-10-24 17:38:14 +02:00
Orestis Floros
b7af69cba8 Merge pull request #4617 from rvalieris/fix
check for both arguments NULL on strings_differ
2021-10-22 17:40:27 +02:00
Jonta
19252515bc Docs: Testsuite: Grammar 2021-10-22 14:21:16 +02:00
rvalieris
64021b6143 check for both arguments NULL on strings_differ 2021-10-22 07:51:07 -03:00
Orestis Floros
e938cae8a0 Merge pull request #4609 from stapelberg/post-release
post-release release.sh changes
2021-10-19 20:05:27 +02:00
Michael Stapelberg
93e8ccd792 post-release release.sh changes 2021-10-19 18:16:34 +02:00
Michael Stapelberg
46f4fe4ecc debian: update changelog 2021-10-19 08:45:28 +02:00
Michael Stapelberg
d854dc597b Update debian/changelog 2021-10-19 08:38:14 +02:00
Michael Stapelberg
1d05918961 Merge branch 'release-4.20' 2021-10-19 08:38:14 +02:00
Michael Stapelberg
533b76378a Merge branch 'next' into stable 2021-10-19 08:38:14 +02:00
Michael Stapelberg
2472f8b8b4 Restore non-git version suffix 2021-10-19 08:38:14 +02:00
Michael Stapelberg
d216a5c9d3 release i3 4.20 2021-10-19 08:37:58 +02:00
Ingo Bürk
e05af2650e Merge pull request #4599 from stapelberg/docs-release
Docs and release notes prep for the next release
2021-10-18 08:10:30 +02:00
Michael Stapelberg
b37c3e3d25 release notes: plug headline features, order and clarify changelog 2021-10-17 20:01:54 +02:00
Michael Stapelberg
b04f206e39 userguide: use “all” criterion in title_window_icon examples 2021-10-17 20:01:41 +02:00
Orestis Floros
ab389e1d76 Merge pull request #4565 from stapelberg/skip
i3-dmenu-desktop: ignore duplicate files and directories
2021-10-07 23:38:47 +02:00
Michael Stapelberg
25bf911537 i3-dmenu-desktop: ignore duplicate files and directories
This is required when e.g. mpv is installed in the NixOS global system
environment *and* in the user environment.

Standalone reproducer:

mkdir -p test1/share/applications test2/share
ln -svf /usr/share/applications/i3.desktop test1/share/applications
ln -svf $PWD/test1/share/applications test2/share
export XDG_DATA_DIRS='test1/share:test2/share'
i3-dmenu-desktop

fixes #4522
2021-10-07 23:22:23 +02:00
Orestis Floros
8323108ca1 Merge pull request #4566 from stapelberg/spelling
fix travis/check-spelling.pl for updated API
2021-10-07 16:01:02 +02:00
Michael Stapelberg
b3d4281b8b fix travis/check-spelling.pl for updated API 2021-10-06 18:23:46 +02:00
Orestis Floros
eada44be1e Merge pull request #4538 from psychon/log-focus-out
Log FocusOut events
2021-09-25 13:53:22 +02:00
Uli Schlachter
16b09672c8 Log FocusOut events
Currently, i3 only logs FocusIn events. Thus, a debug log tells us when
some window gets the focus. However, we don't know when it loses the
focus. This commit remedies this by adding some log messages for this.

Since I had no idea what to log, this just logs all the fields from the
event plus tries to find a name for the window.

Signed-off-by: Uli Schlachter <psychon@znc.in>
Idea-in-context-of: https://github.com/i3/i3/issues/4532
2021-09-25 13:47:36 +02:00
Michael Stapelberg
535da94536 GET_CONFIG: add raw/variable-processed contents of all config files (#4528)
We do this by adding to included_files as i3 processes the configs.

This should allow for easy debugging, without having to change how i3 processes
config files.

related to #4192
2021-09-22 08:54:37 +02:00
Orestis Floros
d3ff9afbb5 Merge pull request #4436 from psychon/check-cairo-status
Check cairo status in draw_util_surface_free()
2021-09-16 19:07:07 +02:00
Uli Schlachter
d2f5e7e46e Fix a minor memory leak
When xcb_request_check() returns an error, something has to clean up and
free this error.

Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-09-15 18:14:37 +02:00
Uli Schlachter
2e500f0817 Check cairo status in draw_util_surface_free()
When "something goes wrong" in cairo-land, the corresponding cairo
object goes into an error state. These errors are sticky. Thus, it is
enough to check for errors before destroying the context.

This commit adds a check in draw_util_surface_free() to check the cairo
context's status and print a log message if anything is wrong.

The idea here is to help debugging drawing issues. Instead of "nothing
visible", the corresponding log message hopefully helps debugging.

This code would have saved me lots of time in figuring out why my pull
request #4379 did not work.

Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-09-15 18:14:37 +02:00
Ingo Bürk
a0938bd606 Update docs to Discussions (#4503) 2021-09-10 09:18:01 +02:00
Ingo Bürk
3686cef63c Merge pull request #4469 from vincentbernat/fix/handler-xrandr
handlers.c: send an "output" event on monitor configuration change
2021-09-10 08:09:32 +02:00
Vincent Bernat
381d7e6a98 doc: fix version number in release notes 2021-09-10 08:04:59 +02:00
Vincent Bernat
f9e6d5dd7a handlers.c: send an "output" event on monitor configuration change
When adding/removing a monitor, the outputs are likely to be modified.
Send an IPC event "output", like when there is a screen configuration
change.

Signed-off-by: Vincent Bernat <vincent@bernat.ch>
2021-09-10 08:04:38 +02:00
Ingo Bürk
3d2a1ef80a Merge pull request #4502 from orestisfl/discussion-template
Add option to start a new discussion in the templates
2021-09-10 07:18:58 +02:00
Ingo Bürk
570138eb72 Merge pull request #4501 from orestisfl/window-logs
Make window id logging hex everywhere
2021-09-10 07:17:27 +02:00
Orestis Floros
e1ca00d164 Add option to start a new discussion in the templates 2021-09-10 00:51:19 +02:00
Orestis Floros
af23c0febf Make window id logging hex everywhere
Right now some logs use %d and some use %08x and it can be confusing to
follow.
2021-09-09 21:05:24 +02:00
Orestis Floros
ce2c21885d Merge pull request #4497 from orestisfl/icons-title-align
Make window icons follow title alignment
2021-09-09 09:27:30 +02:00
Orestis Floros
cc44d9a999 Make window icons follow title alignment
Fixes #4464.

Inspired by changes from #4453 but I didn't end up using the original
code.

For ALIGN_LEFT and ALIGN_CENTER icon offset is calculated first and then
title offset is based on that. For ALIGN_RIGHT title offset is
calculated first but we make sure that the icon fits in the window.

Additionally, icons will now hide if there is not enough space to render
them + their padding. Realistically, this won't happen in normal
usecases.

Effectively, icons have higher precedence over text: draw_util_text will
hide text when it exceeds the max width and the code takes this into
consideration. Icons will only hide when they can't fit at all, not in
order to assure that the text can be displayed fully.
2021-09-09 08:51:14 +02:00
Ingo Bürk
579973b1ef Merge pull request #4499 from orestisfl/gh-actions-logs
GH actions: Archive complete logs on test failure
2021-09-08 07:26:20 +02:00
Ingo Bürk
f45d5d1a53 Merge pull request #4498 from orestisfl/memleak
Free window role & machine
2021-09-08 06:41:30 +02:00
Orestis Floros
3ba7599aa4 GH actions: Archive complete logs on test failure
https://docs.github.com/en/actions/reference/usage-limits-billing-and-administration#artifact-and-log-retention-policy

> By default, the artifacts and log files generated by workflows are
> retained for 90 days before they are automatically deleted.
2021-09-08 00:05:11 +02:00
Orestis Floros
8649a7e229 Free window role & machine 2021-09-07 22:52:14 +02:00
Ingo Bürk
3bdea50548 Merge pull request #4495 from kgilmer/config-directives-no-file-no-error
Lower severity of missing include path specified from error to info.
2021-09-06 12:18:34 +02:00
Ken Gilmer
46cffcc059 Lower severity of missing include path specified from error to info. Addresses https://github.com/i3/i3/issues/4494. 2021-09-05 22:14:25 -07:00
Ingo Bürk
986292c662 Merge pull request #4491 from i3/orestisfl-patch-1
Add #4409 to release notes
2021-09-01 07:48:40 +02:00
Orestis Floros
603e26baa8 Add #4409 to release notes 2021-08-31 23:58:10 +02:00
Orestis Floros
9a02399cee Merge pull request #4454 from vincentbernat/feature/i3-notify
main: signal readiness by notifying systemd
2021-08-30 15:26:58 +02:00
Vincent Bernat
5b6a564190 main: signal readiness by notifying systemd
This is useful if we want to be able to start some services depending
on i3, notably some script using the socket or third-party bars like
polybar. To make use of this change, user is expected to use the
following stanza:

```
[Unit]
Description=i3 window manager
PartOf=graphical-session.target

[Service]
Type=notify
ExecStart=/usr/bin/i3
ExecStopPost=/bin/systemctl --user stop graphical-session.target
Restart=on-failure
```

Something similar is already possible using socket activation. For
example, we could use:

```
[Unit]
Description=i3 window manager socket
PartOf=graphical-session.target

[Socket]
ListenStream=%t/i3.sock
ExecStartPost=/bin/systemctl --user set-environment I3SOCK=%t/i3.sock
```

And other units could `Requires=i3.socket`. Unfortunately, not
everything is using I3SOCK. Notably, `i3 --get-socketpath` does not
and that's what i3ipcpp is doing. An alternative would be to patch `i3
--get-socketpath` to use I3SOCK if present, however, this may be a bit
risky. Should we check the environment variable first or the root
attribute?

Another alternative not requiring any modification is to have a
dedicated `i3-session.target`:

```
[Unit]
Description=i3 session
BindsTo=graphical-session.target
Wants=wallpaper.service
Wants=wallpaper.timer
Wants=polybar.service
Wants=i3-companion.service
```

And trigger it from i3:

```
exec_always --no-startup-id systemctl --user start --no-block i3-session.target
```

The proposed change being quite small, it seems harmless and
low-maintenance.

Signed-off-by: Vincent Bernat <vincent@bernat.ch>
2021-08-30 15:20:04 +02:00
Uli Schlachter
7b6e864823 Implement support for the WM_Sn selection (#4374)
Closes #536

When the WM_Sn selection is already owned at startup, this now either
errors out or waits for the old selection owner to exit.
2021-08-28 01:01:38 +02:00
Jeff Smith
01ba240272 Fix bug where long commands crash i3
Commands with more than 10 words would result in i3 crashing. The root
cause is that the stack which holds arguments with identifiers is
only 10 big, but generate-command-parser.pl was giving every word an
identifier of "".
2021-08-24 16:34:30 -05:00
Michael Stapelberg
36ba1043d5 ipc: document scratchpad_state (#4466)
fixes https://github.com/i3/i3/issues/4465
2021-07-30 22:48:40 +02:00
Ingo Bürk
fee005bc87 Merge pull request #4460 from iscgar/isaac/all-criterion
Implement 'all' matching criterion
2021-07-26 08:05:25 +02:00
Isaac Garzon
3a818c0f20 Implement 'all' matching criterion
This criterion matches all open windows, as a more readable (and
correct) version of the 'class=".*"' criterion (more correct because
it'll also match windows which don't have WM_CLASS set yet).
2021-07-24 22:18:58 +03:00
tomty89
fc65ca36b1 Use mkdirp() in get_process_filename() (#4397)
Avoids race condition in case multiple i3 instances are started in parallel with e.g. systemd user units for multiple X(vfb) servers.
2021-07-05 17:21:21 +02:00
Michael Stapelberg
abbf6a85d7 Implement showing window icons in titlebar (#4439)
This feature defaults to off, and can be turned on for individual windows,
or (with for_window) for all new windows. See the userguide change.

This commit is partially based on work by:

• Marius Muja
• mickael9
• Esteve Varela Colominas
• Bernardo Menicagli
2021-06-13 08:35:52 +02:00
Michael Stapelberg
eaa5e636f9 Implement include config directive (#4420)
The implementation uses wordexp(3) just like sway:
https://github.com/i3/i3/issues/1197#issuecomment-226844106

Thanks to jajm for their implementation at
bb55709d0a

This required refactoring the config parser to be re-entrant
(no more global state) and to return an error instead of dying.

In case a file cannot be opened, i3 reports an error but proceeds with the
remaining configuration.

Key bindings can be overwritten or removed using the new --remove flag of the
bindsym/bindcode directive.

All files that were successfully included are displayed in i3 --moreversion.

One caveat is i3 config file variable expansion, see the note in the userguide.

fixes #4192
2021-06-02 21:01:43 +02:00
Ingo Bürk
4c93f61353 Merge pull request #4430 from stapelberg/actions
README: update build status badge for GitHub Actions
2021-05-23 16:35:38 +02:00
Michael Stapelberg
e4b3f46366 README: update build status badge for GitHub Actions 2021-05-23 16:06:13 +02:00
Michael Stapelberg
c94f41b2da switch from Travis to GitHub actions for continuous integration (CI) (#4428) 2021-05-23 15:44:28 +02:00
Vladimir Panteleev
e44aa7a9a9 docs/ipc: Explicitly state null as a possible type
This helps write correct descriptions of the JSON schema for strongly
typed languages that support deserializing to native types (and
require a different type for null).
2021-05-20 21:47:43 +02:00
Vladimir Panteleev
d4c23ec24b docs/ipc: Add descriptions of all message payloads
These seemed to be inconsistently specified.

Particularly, the "SYNC" message payload was not described anywhere.

Even when the payload is empty, it is helpful to specify that
explicitly, as it prevents the reader from having to guess whether if
it's really empty, or otherwise somehow implicitly obvious.

The "Reply format" section is now "Messages and replies", and covers
both the reply format and the format of sent messages.
2021-05-20 21:47:43 +02:00
Vladimir Panteleev
8db3bef66d docs/ipc: Explicitly state that reply types correspond to message types
Help avoid some squinting to make sure everything actually matches.
2021-05-20 21:47:43 +02:00
Vladimir Panteleev
63099f7a83 docs/ipc: Fix inconsistent whitespace 2021-05-20 21:47:43 +02:00
Vladimir Panteleev
1f75e8d321 docs/ipc: Re-order and clarify reply format
- Make sure to place the description of the packet before the
  description of the payload.

- Describe the relationship of messages and replies.

- Add note on pipelining.
2021-05-20 21:47:43 +02:00
Vladimir Panteleev
73bcbbe1b1 docs/ipc: Fix grammar
Add missing "it". Also add two instances of "a", which sounds more
correct to me.
2021-05-20 21:47:43 +02:00
Vladimir Panteleev
bb6ee88440 docs/ipc: Clarify i3 --get-socketpath text 2021-05-20 21:47:43 +02:00
Kjetil Torgrim Homme
2245db63ab add cross reference to directive default_border from command border (#4273)
Added a couple of examples to make usage clearer with a cursory glance.
2021-05-20 21:47:20 +02:00
Uli Schlachter
d65a7ed250 Drop xcb_flush() before xcb_aux_sync() (#4378)
xcb_flush() flushes xcb's output buffer. Thus, after this call, all
previous requests are surely written to the connection with the X11
server.

xcb_aux_sync() synchronises with the X11 server. It makes sure all
previous requests were sent to the X11 server and already processed. It
does this via free(xcb_get_input_focus_reply(xcb_get_input_focus())): It
sends a request and waits for its reply. It is guaranteed that the X11
server processes requests in-order.

This means that the sequence xcb_flush(); xcb_aux_sync() first writes
whatever is in the output buffer and then does another write for the
four bytes of the GetInputFocus request from xcb_aux_sync().

Put differently: xcb_flush(); xcb_aux_sync() doesn't do anything more
than just xcb_aux_sync(). In fact, it has slightly more overhead for the
same result.

Thus, this commit drops all calls to xcb_flush() immediately after
xcb_aux_sync().

Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-05-20 21:44:13 +02:00
Uli Schlachter
60542da091 Do not "set" the wallpaper during startup (#4373)
"Set" the wallpaper during startup only sometimes

Since commit 4f5e0e7, i3 would take a screenshot and set that as the
background pixmap of the root window during startup. This is the easy
part of setting a proper X11 wallpaper.

The code in question was added because something either set the
background pixmap of the root window to NONE or the X11 server was
started with "-background none". This is apparently done by default by
e.g. gdm to avoid some flickering while the X11 server starts up.

This commit makes this code conditional: Only when no wallpaper is
detected is a screenshot taken.

Since I could not find any way to query the background of a window, a
more direct approach is taken to detect this situation: First, we find
some part of the root window that is not currently covered. Then we open
a white window there, close it again and grab a screenshot. If a
wallpaper is set, the X11 server will draw this wallpaper after the
window is closed and something else will be visible in the screenshot.

However, the wallpaper could have a white pixel at the tested position.
Thus, this procedure is repeated with a black window.

Only when this procedure produces two different pixel values is a
screenshot taken and set as the wallpaper.

Fixes: https://github.com/i3/i3/issues/4371
Fixes: https://github.com/i3/i3/issues/2869
Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-05-20 21:37:35 +02:00
j-jzk
fcae64f7fd Increase test timeout (#4416)
Default of 30 seconds can time-out on slower hardware
2021-04-29 15:59:23 +02:00
Ingo Bürk
91e751d5a2 Merge pull request #4411 from alarsyo/fix-broken-pango-link
docs: fix broken link to Pango documentation
2021-04-26 16:29:32 +02:00
Antoine Martin
00d4836ee4 docs: fix broken link to Pango documentation 2021-04-26 16:13:27 +02:00
Michael Stapelberg
9db03797da fix crash with “layout default”: ipc.c won’t dump L_DEFAULT layout (#4409)
fixes #4408
2021-04-20 09:04:07 +02:00
Michael Stapelberg
c8158875b4 default config: use dex for XDG autostart (#4405)
fixes #4402
2021-04-17 15:26:45 +02:00
Ingo Bürk
9bf9cb9926 Merge pull request #4377 from lbonn/signed-container-positions
ipc: return signed int for container positions
2021-04-10 16:24:43 +02:00
lbonn
496364fdbf ipc: return signed int for container positions
If we use json as a language-agnostic representation, it makes sense to
use proper signed integers here.
2021-04-10 14:17:00 +02:00
Orestis Floros
fcf4c235d2 Merge pull request #4394 from jaykhandkar/fix-userguide
docs/userguide - comment out TODO line
2021-04-06 12:59:17 +02:00
Jay Khandkar
e150b99ba8 docs/userguide - comment out TODO line
commented out TODO line in userguide which was showing up in the
online documentation.
2021-03-28 15:34:59 +05:30
Ingo Bürk
39376a94ab Merge pull request #4387 from ivanmilov/milov/ipc-floating-doc
Added floating field to _tree_reply in ipc documentation
2021-03-16 16:52:15 +01:00
Ivan Milov
52afb16391 Added floating field to _tree_reply in ipc documentation 2021-03-15 10:42:39 +01:00
Uli Schlachter
1c67683406 Make this sound less "X11 is horrible"
Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-03-12 10:18:07 +01:00
Uli Schlachter
fd500ee99d Add a section on the sync IPC command
Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-03-12 10:18:07 +01:00
Uli Schlachter
8a5eac3457 Improve the docs for I3_SYNC
I was confused by the previous state of the docs since I imagined that
there is still a race possible. Thus, the images are updated to
explicitly include the IPC reply from i3 (since that is an important
synchronisation component). I also tried to clarify some of the text.

Fixes: https://github.com/i3/i3/issues/4365
Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-03-12 10:18:07 +01:00
Ingo Bürk
af2171d01d Merge pull request #4363 from psychon/more-prefetch
Prefetch some information through xcb
2021-03-06 19:06:16 +01:00
Uli Schlachter
db5a7dc22a Only prefetch xinerama if it is likely needed
Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-03-06 18:43:46 +01:00
Ingo Bürk
42c3dbe025 Merge pull request #4362 from psychon/no-temp-cairo-surface2
Some follow-up to
2021-03-06 11:10:57 +01:00
Uli Schlachter
f8bc7052ea Remove unused member from surface_t
Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-03-06 09:27:15 +01:00
Uli Schlachter
bae1f4b354 Prefetch some information through xcb
This commit makes libxcb prefetch some information that will be needed
later anyway. This avoids some round-trips to the X11 server since the
information is already present when needed.

Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-03-05 14:16:52 +01:00
Ingo Bürk
757b3dd573 Merge pull request #4361 from psychon/no-temp-cairo-surface
No temporary cairo surface for font drawing
2021-03-05 14:06:57 +01:00
Uli Schlachter
b23c8875f7 font: Get rid of temporary cairo surface
i3 actually manages to have two different cairo surfaces referring to
the same drawable. One comes from the code in draw_util. The second is
temporarily created while rendering text via draw_text(). No idea how
well cairo handles this case.

This commit instead changes the code to pass the already existing cairo
surface from the caller through.

This might or might not fix https://github.com/i3/i3/pull/4357. My
thinking here is that cairo now knows the actual size of the drawable
and thus does not clip the drawing to a smaller size.

Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-03-05 11:37:03 +01:00
Uli Schlachter
8d645d0de6 Remove draw_text_ascii()
This function is unused since commit fa488d721d from 2017.

Signed-off-by: Uli Schlachter <psychon@znc.in>
2021-03-05 11:36:09 +01:00
Michael Stapelberg
46bc841162 Merge branch 'release-4.19.2' 2021-02-27 10:38:13 +01:00
Michael Stapelberg
7da136dca4 Update debian/changelog 2021-02-27 10:38:13 +01:00
Michael Stapelberg
4ef47087d4 Restore non-git version suffix 2021-02-27 10:38:13 +01:00
Michael Stapelberg
98e0a07f15 Merge branch 'stable' into next 2021-02-27 10:38:13 +01:00
Michael Stapelberg
ddebebd26b release i3 4.19.2 2021-02-27 10:37:57 +01:00
Michael Stapelberg
0e1a828003 cherry-pick release.sh changes into stable branch
related to https://github.com/i3/i3/issues/4350
2021-02-27 10:20:16 +01:00
Michael Stapelberg
b95bab0e34 mark travis/push-balto.sh as executable (#4347)
related to https://github.com/i3/i3/issues/4340
2021-02-13 10:10:59 +01:00
Michael Stapelberg
521949b567 travis: push i3 autobuild packages to balto instead of bintray (#4345)
related to https://github.com/i3/i3/issues/4340
2021-02-13 09:56:15 +01:00
Orestis Floros
5df0b4b571 Merge pull request #4336 from tbgiles/next
Clear framebuffer for containers with visible windows
2021-02-07 19:10:23 +01:00
Orestis Floros
e8949c7df1 Merge pull request #4338 from orestisfl/move-workspaces-to-multiple-outputs
Move workspaces to multiple outputs
2021-02-06 12:52:08 +01:00
Orestis Floros
8ff8db3c36 Accept multiple outputs in move container|workspace to output
Fixes #4337
2021-02-06 11:00:16 +01:00
Orestis Floros
f2243a7a90 Tests: Fix get_output_for_workspace
In the case of more than one workspaces in an output
2021-02-06 10:59:16 +01:00
Michael Stapelberg
36b6fef990 Merge branch 'release-4.19.1' 2021-02-01 09:04:01 +01:00
Michael Stapelberg
ab2a22a78b Update debian/changelog 2021-02-01 09:04:01 +01:00
Michael Stapelberg
001577c58c Set non-git version to 4.19.1-non-git. 2021-02-01 09:04:01 +01:00
Michael Stapelberg
33bbce6c38 Merge branch 'stable' into next 2021-02-01 09:04:01 +01:00
Tristan Giles
09e2bdaeba Clear framebuffer on containers that are visible windows 2021-01-30 22:44:43 -08:00
Orestis Floros
ef12e9a5ab Merge pull request #4331 from ImranVirani/patch-1
Properly quote rofi call in i3 config
2021-01-29 16:20:15 +01:00
Imran Virani
60213accae Properly quote rofi call in i3 config 2021-01-29 14:01:51 +01:00
Orestis Floros
62367c686d Merge pull request #4007 from xzfc/3981-client-machine
Match WM_CLIENT_MACHINE
2021-01-28 14:52:50 +01:00
Albert Safin
32c10a19f2 Add "machine" criterion to match WM_CLIENT_MACHINE
Closes #3981

Add "%machine" title_format placeholder
Add "machine" to the IPC and layout saving/restoring
2021-01-28 12:52:10 +01:00
Orestis Floros
921226783f Merge pull request #4330 from orestisfl/revert-clear-framebuffer
Revert "Clear framebuffer before drawing borders (#4308)"
2021-01-26 10:37:49 +01:00
Orestis Floros
8c8d5dc019 Revert "Clear framebuffer before drawing borders (#4308)"
This reverts commit 057a635279.
2021-01-25 08:13:15 +01:00
Michael Stapelberg
dcd6079c9b i3-dump-log -f: switch from pthreads to UNIX sockets
fixes #4117
2021-01-20 21:40:24 +01:00
Michael Stapelberg
131a6158c8 move set_nonblock, create_socket and path_exists to libi3 2021-01-20 21:40:24 +01:00
Orestis Floros
e4f12ac349 Merge pull request #4322 from orestisfl/i3bar-version
i3bar log version & correct exit code
2021-01-17 00:52:37 +01:00
Orestis Floros
ba134d3c7c i3bar: Exit with 1 on wrong argument 2021-01-15 23:35:31 +01:00
Orestis Floros
6668d03157 i3bar: LOG version on startup 2021-01-15 23:28:33 +01:00
Orestis Floros
9c7616cc98 Merge pull request #4315 from orestisfl/i3bar-fixes
I3bar fixes
2021-01-12 09:10:22 +01:00
Orestis Floros
1a29b28fa5 i3bar: No reason to get_workspaces after output event
got_output_reply() requests this information anyway and if it is
received before the output reply, the information will be erased by
get_output_reply().
2021-01-12 08:56:38 +01:00
Orestis Floros
6e0b29a65b i3bar: properly restart status command after config change 2021-01-12 08:56:38 +01:00
Orestis Floros
0370c5e297 i3bar: Properly close FDs 2021-01-12 08:35:41 +01:00
Orestis Floros
31580ddc7f Merge pull request #4231 from orestisfl/i3bar-default-bar_id
i3bar: Add default bar_id
2021-01-03 19:49:25 +01:00
Orestis Floros
052c4bea5c Merge pull request #4309 from i3/orestisfl-patch-1
Add release note for framebuffer fix
2021-01-03 10:26:55 +01:00
Orestis Floros
c1ec2ad19b i3bar: Add default bar_id
Instead of erroring, request the list of bar configs from i3 and use the
first one.
2021-01-03 10:17:43 +01:00
Orestis Floros
fb5d2a05c2 i3bar: Update manpage/help with options 2021-01-03 10:17:39 +01:00
Orestis Floros
2a2c350eb6 Add release note for framebuffer fix
#4308
2021-01-03 09:48:27 +01:00
Tristan Giles
057a635279 Clear framebuffer before drawing borders (#4308)
Clear framebuffer before copying surface to prevent visual garbage

Fixes #3481
Corrects problems of #4280
2021-01-03 09:36:43 +01:00
Ingo Bürk
d3d2612eba Merge pull request #4307 from i3/revert-4280-next
Revert "Clear pixmap to black before starting to draw"
2021-01-03 08:18:03 +01:00
Orestis Floros
5bf58a4c87 Revert "Clear pixmap to black before starting to draw" 2021-01-03 01:11:21 +01:00
Orestis Floros
bfa22cafea Merge pull request #4280 from tbgiles/next
Clear pixmap to black before starting to draw
2021-01-03 00:36:31 +01:00
Tristan Giles
337dd653ae Clear framebuffer before copying surface to prevent visual garbage
Fixes #3481
2021-01-02 23:54:11 +01:00
Orestis Floros
8d0b8b59c4 Merge pull request #4305 from i3/orestisfl-patch-1
Comment-out i3-dmenu-desktop bindcode
2021-01-02 23:09:06 +01:00
Orestis Floros
3f063bcb6f Comment-out duplicate i3-dmenu-desktop bindcode
Fixes #4304
2021-01-02 21:54:36 +01:00
Orestis Floros
b638ca593d Merge pull request #4230 from Xyene/tab-focus
Implement context-sensitive tab-clicking behavior
2021-01-01 22:17:27 +01:00
Tudor Brindus
f2ec5a4e77 Implement context-sensitive tab-clicking behavior
This commit makes it so that when clicking on a tab for the first time,
its inactive-focused child is focused. This matches i3 behavior when
scrolling on tabs.

A second click on the tab titlebar focuses the tab container itself.

Subsequent clicks cycle between these two states.

Closes #4225.
2021-01-01 16:04:33 -05:00
Orestis Floros
b2af112a04 Merge pull request #4284 from sojito/next
Add missing SYNC Reply type in IPC docs
2021-01-01 15:57:04 +01:00
Orestis Floros
87f033fbd4 Merge pull request #4299 from abeutot/fix_workspace_assignment_on_screen_change
Fix workspace assignment on screen change
2020-12-30 23:33:08 +01:00
Anaël Beutot
d539c6e288 Fix workspace assignements after output changes
Fix #4261

The previous method was modifying the same list it was iterating upon
causing an erronous iteration and thus not every workspace got assigned
back to were they should (only the first one).
2020-12-30 23:01:50 +01:00
Orestis Floros
5354eaa27e Merge pull request #4300 from abeutot/cleanup_remaining_cmdparse
Remove unused header file and references to it
2020-12-30 22:09:09 +01:00
Anaël Beutot
b1fb440345 Remove unused header file and references to it 2020-12-30 21:40:54 +01:00
Ingo Bürk
1d06cb976e Merge pull request #4297 from Airblader/feature-requests-doc
[Proposal] Update CONTRIBUTING and issue template(s) for feature requests
2020-12-30 20:17:32 +01:00
Ingo Bürk
b6ced01676 Update CONTRIBUTING and issue template(s) for feature requests 2020-12-29 20:29:48 +01:00
Michael Stapelberg
74b461e25e build.i3wm.org: disallow search engine indexing (#4295)
related to https://github.com/i3/i3.github.io/issues/41
2020-12-29 20:05:59 +01:00
Orestis Floros
da1a720782 Merge pull request #4296 from stapelberg/spelling
check-spelling: exempt typo until upstream merges the fix
2020-12-29 17:37:59 +01:00
Michael Stapelberg
3a1e44da68 check-spelling: exempt typo until upstream merges the fix
This must have come in with a new version of clang.
2020-12-29 17:13:11 +01:00
Romuald Brunet
146305d6c6 Remove small bulge in i3 SVG logo (#4281)
* Upgrade logo's inkscape SVG attributes

Inscape changed their attribute handling with version 1.0, this commit
is dedicated to this change

* Update logo to remove small bulge
2020-12-28 23:22:22 +01:00
Ralph Gutkowski
679335f19b Add missing SYNC Reply type in IPC docs 2020-12-16 16:36:55 +01:00
ajakk
27ff84ff42 Use docdir for all docs when building (#4269) 2020-12-08 08:11:59 +01:00
Orestis Floros
be4790802e Merge pull request #4244 from ekarpp/nagbar
i3-nagbar: add possibility to open bar on focused monitor
2020-11-29 23:34:43 +01:00
ekarpp
b6690045ed i3-nagbar: add possibility to open bar on focused monitor
Closes #3692

i3-nagbar will open by default on the monitor with input focus and using the flag -p on the primary monitor.
2020-11-29 22:36:49 +01:00
Orestis Floros
37ebd2a179 Merge pull request #4252 from orestisfl/create_workspace_on_output-duplicate-num
create_workspace_on_output: Prevent duplicate workspace nums
2020-11-28 23:41:49 +01:00
Orestis Floros
13757ac1ca workspace.c: Make small consistency changes 2020-11-28 23:22:55 +01:00
Orestis Floros
68258785ac create_workspace_on_output: Prevent duplicate workspace nums
When going through the `binding_workspace_names` to prioritize
user-specified names, we only check if the workspace exists, not the
workspace number. If the user specified a `bindsym … workspace number X`
directive, then it is appended in `binding_workspace_names` and a
workspace is created using that number even though from the POV of a
user that uses numbers to change workspaces, that workspace already
exists.

In similar code here:
1d9160f2d2/src/workspace.c (L997)
we do the check.

Fixes #4238
2020-11-28 23:22:55 +01:00
Dmitri Goutnik
d0067077ed declare parser generated sources as deps (#4264) 2020-11-21 19:18:24 +01:00
Michael Stapelberg
eb83b37936 userguide: clarify home row / vi navigational key relation (#4260)
Before this commit, the userguide mentioned “compatibility with most keyboard
layouts”, but that seems to not have been the intention of vi author(s).
2020-11-16 11:10:03 +01:00
Michael Stapelberg
9f3a3a1d98 i3-sensible-terminal: prioritize terminals with good accessibility (#4259)
We never did (and still do not) guarantee any order in which terminal emulators
are tried. Quoting from the i3-sensible-terminal man page:

> Please don’t complain about the order: If the user has any preference, they
> will have $TERMINAL set or modified their i3 configuration file.

This commit moves mate-terminal to the beginning of the list, which is
considered the most accessible terminal emulator among blind users.

fixes https://github.com/i3/i3/issues/4256
2020-11-16 09:56:49 +01:00
Michael Stapelberg
4b1ea08eef release.sh and release notes changes post-release (#4258) 2020-11-15 19:22:09 +01:00
Michael Stapelberg
a901498758 debian: update changelog 2020-11-15 18:28:25 +01:00
530 changed files with 13072 additions and 4386 deletions

View File

@@ -1,13 +1,14 @@
BasedOnStyle: google
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortBlocksOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: false
IndentWidth: 4
PointerBindsToType: false
BasedOnStyle: google
ColumnLimit: 0
SpaceBeforeParens: ControlStatements
SortIncludes: false
ForEachMacros: [ TAILQ_FOREACH, TAILQ_FOREACH_REVERSE, SLIST_FOREACH, CIRCLEQ_FOREACH, CIRCLEQ_FOREACH_REVERSE, NODES_FOREACH, NODES_FOREACH_REVERSE, FOREACH_NONINTERNAL]
IndentWidth: 4
InsertBraces: true
PointerBindsToType: false
SortIncludes: false
SpaceBeforeParens: ControlStatements
TypenameMacros: [ SLIST_HEAD, SLIST_ENTRY, LIST_HEAD, LIST_ENTRY, SIMPLEQ_HEAD, SIMPLEQ_ENTRY, TAILQ_HEAD, TAILQ_ENTRY, CIRCLEQ_HEAD, CIRCLEQ_ENTRY ]

View File

@@ -4,7 +4,7 @@
Note that bug reports and feature requests for related projects should be filed in the corresponding repositories for [i3status](https://github.com/i3/i3status) and [i3lock](https://github.com/i3/i3lock).
## i3 bug reports and feature requests
## i3 bug reports
1. Read the [debugging instructions](https://i3wm.org/docs/debugging.html).
2. Make sure you include a link to your logfile in your report (section 3).
@@ -18,6 +18,20 @@ Note that bug reports and feature requests for related projects should be filed
encountered the issue you are about to report while using a compositor,
please try reproducing it without a compositor.
## i3 feature requests
1. Read the [project goals](https://i3wm.org) on the website and make sure that
they are compatible with the feature you want to suggest.
2. We are generally happy with the current feature set of i3 and instead focus
on maintenance such as stability and fixing bugs. New features will rarely
be considered if they require additional configuration and/or commands, or
if they add significant complexity (either through the exposed configuration
or mental complexity) to the project.
3. Explain in detail what problem the feature addresses and why existing
features fall short.
4. Consider whether the feature could instead be implemented using the
[IPC](https://i3wm.org/docs/ipc.html) or other external tooling.
## Pull requests
* Before sending a pull request for new features, please check with us that the
@@ -27,11 +41,14 @@ Note that bug reports and feature requests for related projects should be filed
* Use `clang-format` to format your code.
* Run the [testsuite](https://i3wm.org/docs/testsuite.html)
* If your changes should be reported on the next release's changelog, also
update the [RELEASE-notes-next](../RELEASE-notes-next) file in the root
folder. Example of changes that should be reported are bug fixes present in
the latest stable version of i3 and new enhancements. Example of changes that
should not be reported are minor code improvements, documentation, regression
and fixes for bugs that were introduced in the `next` branch.
add a small single-line file starting with a number (see examples) containing
a short explanation of your change either in the
[changes](../release-notes/changes) or the
[bugfixes](../release-notes/bugfixes/) folder. Example of changes that should
be reported are bug fixes present in the latest stable version of i3 and new
enhancements. Example of changes that should not be reported are minor code
improvements, documentation, regression and fixes for bugs that were
introduced in the `next` branch.
## Finding something to do

View File

@@ -3,7 +3,12 @@ PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATI
-->
## I'm submitting a…
<!-- Please check one of the following options with "x" -->
<!--
Check one of the following options with "x".
Please note that at this point we focus on maintaining i3 and fixing bugs, and will rarely consider features which require further configuration or significant complexity.
In such cases you should consider and present specific benefits derived from adding this feature such that it can be weighed against the cost of additional complexity and maintenance.
-->
<pre>
[ ] Bug
[ ] Feature Request

View File

@@ -1,72 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
<!--
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
-->
## I'm submitting a…
<!-- Please check one of the following options with "x" -->
<pre>
[x] Bug
[ ] Feature Request
[ ] Documentation Request
[ ] Other (Please describe in detail)
</pre>
## Current Behavior
<!--
Describe the current behavior,
e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
-->
## Expected Behavior
<!--
Describe the desired behavior you expect after mitigation of the issue,
e.g., »The window left next to the current window should be focused.«
-->
## Reproduction Instructions
<!--
Please provide detailed instructions on how the bug can be reproduced.
E.g., »Open three windows in a V[A H[B C]] layout on a new workspace«
-->
## Environment
<!--
Please include your exact i3 version.
Note that we only support the latest major release and the current development version. If you are using an older version of i3, please first update to the current release version and reproduce the issue there.
-->
Output of `i3 --moreversion 2>&-`:
<pre>
i3 version:
</pre>
<!--
Please include your (complete) i3 config with which the issue occurs. You can either paste the file directly or provide a link to a service such as pastebin.
If you would like to help debugging the issue, please try to reduce the config such that it is as close to the default config as possible while still reproducing the issue. This can help us bisect the root cause.
-->
<details><summary>Config file</summary><pre>
</pre>
</details>
<!--
Providing a logfile can help us trace the root cause of an issue much quicker. You can learn how to generate the logfile here:
https://i3wm.org/docs/debugging.html
Providing the logfile is optional.
-->
<pre>
Logfile URL:
</pre>
<!--
Please also answer the questions below to help us process your issue faster. If you have any other information to share, please add it here as well.
-->
<pre>
- Linux Distribution & Version:
- Are you using a compositor (e.g., xcompmgr or compton):
</pre>

107
.github/ISSUE_TEMPLATE/bug_report.yml vendored Executable file
View File

@@ -0,0 +1,107 @@
name: Bug Report
description: Create a report to help us improve.
labels: [bug]
body:
- type: checkboxes
id: terms
attributes:
label: Welcome
options:
- label: Yes, I'm using the latest major release or the current development version. These are the only supported versions.
required: true
- label: Yes, I've searched similar issues and discussions on GitHub and didn't find any.
required: true
- type: textarea
id: current
attributes:
label: Current Behavior
placeholder: |-
Describe the current behavior,
e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
placeholder: |-
Describe the desired behavior you expect after mitigation of the issue,
e.g., »The window left next to the current window should be focused.«
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction Instructions
placeholder: |-
Please provide detailed instructions on how the bug can be reproduced.
E.g., »Open three windows in a V[A H[B C]] layout on a new workspace«
validations:
required: true
- type: textarea
id: version
attributes:
label: i3 version
description: |-
Paste the output of
```
i3 --moreversion 2>&-
```
render: text
validations:
required: true
- type: textarea
id: config
attributes:
label: Config file
description: |-
Please include your (complete) i3 config with which the issue occurs.
If you would like to help debugging the issue, please try to reduce the config such that it is as close to the default config as possible while still reproducing the issue. This can help us bisect the root cause.
render: text
validations:
required: true
- type: input
id: distro
attributes:
label: Linux distribution & Version
validations:
required: true
- type: dropdown
id: compositor
attributes:
label: Are you using a compositor?
description: |-
Try running
```shell
pidof picom
pidof compton
```
If any IDs show up, you are running a compositor
options:
- I don't know
- I am sure I don't run any compositor
- picom
- compton
- Other
validations:
required: true
- type: input
id: verbose-output
attributes:
label: Logfile
description: |-
Providing the URL to a logfile can help us trace the root cause of an issue much quicker. You can learn how to generate the logfile here:
https://i3wm.org/docs/debugging.html
Providing the logfile is optional.
validations:
required: false

7
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
contact_links:
- name: Userguide
url: https://i3wm.org/docs/userguide.html
about: i3 Users Guide
- name: Ask a question or request support for using i3
url: https://github.com/i3/i3/discussions/new
about: Ask a question or request support for using i3

View File

@@ -1,47 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
<!--
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
-->
## I'm submitting a…
<!-- 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>

57
.github/ISSUE_TEMPLATE/feature_request.yml vendored Executable file
View File

@@ -0,0 +1,57 @@
name: Feature request
description: Suggest an idea for this project
labels: [enhancement]
body:
- type: checkboxes
id: terms
attributes:
label: Welcome
options:
- label: Yes, I've searched similar issues and discussions on GitHub and didn't find any.
required: true
- type: checkboxes
id: impact
attributes:
label: Impact
description: |-
Please note that at this point we focus on maintaining i3 and fixing bugs, and will rarely consider features which require further configuration or significant complexity.
In such cases you should consider and present specific benefits derived from adding this feature such that it can be weighed against the cost of additional complexity and maintenance.
Keep in mind that i3 provides a powerful way to interact with it through its IPC interface: https://i3wm.org/docs/ipc.html.
options:
- label: This feature requires new configuration and/or commands
required: false
- type: textarea
id: current
attributes:
label: Current Behavior
placeholder: |-
Describe the current behavior,
e.g., »When pressing Alt+j (focus left), the window above the current window is focused.«
validations:
required: true
- type: textarea
id: desired
attributes:
label: Desired Behavior
placeholder: |-
Describe the desired behavior you expect after mitigation of the issue,
e.g., »The window left next to the current window should be focused.«
validations:
required: true
- type: textarea
id: version
attributes:
label: i3 version
description: |-
Paste the output of
```
i3 --moreversion 2>&-
```
render: text
validations:
required: true

98
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,98 @@
name: GitHub Actions
on:
push:
branches: [ next, master, actions ]
pull_request:
branches: [ next ]
jobs:
build:
name: build and test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
compiler: [gcc, clang]
env:
CC: ${{ matrix.compiler }}
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
DOCKER_EMAIL: ${{ secrets.DOCKER_EMAIL }}
DOCKER_USER: ${{ secrets.DOCKER_USER }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
BALTO_TOKEN: ${{ secrets.BALTO_TOKEN }}
steps:
- uses: actions/checkout@v4
- run: git fetch --prune --unshallow
- name: construct container name
run: |
echo "BASENAME=i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base.Dockerfile)" >> $GITHUB_ENV
echo "BASENAME_UBUNTU=i3wm/travis-base-ubuntu:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu.Dockerfile)" >> $GITHUB_ENV
- name: fetch or build Docker container
run: |
docker pull ${{ env.BASENAME }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME }} travis/travis-base.Dockerfile
- name: fetch or build extra Docker containers
if: github.ref == 'refs/heads/next' && matrix.compiler == 'gcc'
run: |
docker pull ${{ env.BASENAME_UBUNTU }} || ./travis/docker-build-and-push.sh ${{ env.BASENAME_UBUNTU }} travis/travis-base-ubuntu.Dockerfile
- name: build i3
run: |
docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} /bin/sh -c 'rm -rf build; mkdir -p build && cd build && CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Werror -fno-common -D_FORTIFY_SOURCE=3" meson setup .. -Ddocs=true -Dmans=true -Db_sanitize=address --buildtype=debugoptimized && ninja -v'
- name: Upload docs html for manual inspection
uses: actions/upload-artifact@v4
with:
name: i3-docs
path: |
build/*.html
if: matrix.compiler == 'gcc'
- name: check spelling
run: |
docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/check-spelling.pl
- name: run i3 tests
run: |
docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} ./travis/run-tests.sh
- name: Archive test logs
uses: actions/upload-artifact@v4
with:
name: test-logs
path: build/testsuite-*
if: ${{ failure() }}
- name: build dist tarball
run: |
docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${{ env.BASENAME }} /bin/sh -c 'rm -rf distbuild; mkdir distbuild && cd distbuild && meson setup .. -Ddocs=true -Dmans=true && meson dist --no-tests'
- name: build Debian packages
if: github.ref == 'refs/heads/next' && matrix.compiler == 'gcc'
run: |
echo "::group::Debian amd64"
docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/debian-build.sh deb/debian-amd64/DIST
echo "::endgroup::"
echo "::group::Ubuntu amd64"
docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME_UBUNTU }} ./travis/debian-build.sh deb/ubuntu-amd64/DIST
echo "::endgroup::"
- name: push Debian packages to balto
if: github.ref == 'refs/heads/next' && matrix.compiler == 'gcc'
run: |
travis/push-balto.sh
- name: build docs
if: github.ref == 'refs/heads/next' && matrix.compiler == 'gcc'
run: |
docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${{ env.BASENAME }} ./travis/docs.sh
- name: push docs to GitHub pages
if: github.ref == 'refs/heads/next' && matrix.compiler == 'gcc'
run: |
travis/deploy-github-pages.sh
formatting:
name: Check formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: check & print release notes
run: ./release-notes/generator.pl
- name: Install dependencies
run: |
sudo apt-get install -y clang-format-15
- name: Check formatting
run: clang-format-15 --dry-run --Werror $(git ls-files '*.c' '*.h')
- name: Verify safe wrapper functions are used
run: ./travis/check-safe-wrappers.sh

1
.gitignore vendored
View File

@@ -50,4 +50,3 @@ LAST_VERSION
# it is up to you to arrange for it to be ignored by git,
# e.g. by listing your directory in .git/info/exclude.
/build

View File

@@ -1,71 +0,0 @@
dist: trusty
services:
- docker
language: c
compiler:
- gcc
- clang
addons:
apt:
packages:
# For https support in HTTP::Tiny.
- libio-socket-ssl-perl
env:
global:
- BASENAME="i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base.Dockerfile)"
- BASENAME_386="i3wm/travis-base-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-386.Dockerfile)"
- BASENAME_UBUNTU="i3wm/travis-base-ubuntu:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu.Dockerfile)"
- BASENAME_UBUNTU_386="i3wm/travis-base-ubuntu-386:$(date +'%Y-%m')-$(./travis/ha.sh travis/travis-base-ubuntu-386.Dockerfile)"
- secure: "B5IICA8MPx/FKaB50rTPqL8P1NU+Q0yuWl+lElL4+a9xSyLikfm3NzUPHoVwx8lNw2AVK6br7p0OmF7vMFjqAgrgc1cajTtEae5uFRKNUrWLpXM046YgNEYLLIHsQOjInxE+S4O6EFVzsUqsu8aeo2Xhq4sm4iUocG7e5isYgYo=" # DOCKER_PASS
- secure: "EIvrq8PG7lRjidppG0RCv4F0X4GP3DT9F5+ixVuGPfhK/hZT3jYC2AVY9G+NnUcXVwQEpW92rlqpftQ/qZ13FoyWokC8ZyoyD06fr5FPCfoFF3OczZwAJzZYkObI/hE9+/hXcylx/Os6N4INd2My1ntGk3JPsWL9riopod5EjSg=" # DOCKER_EMAIL
- secure: "hvhBunS4xXTgnIOsk/BPT7I7FrJhvVwCSt5PfxxvMqNaztOJI9BuK7ZrZ5Cy38KyHwlh3VHAH5AaCygJcPauoSQCV3bpnlbaWn3ruq2F0Q697Q5uNf73liXzyUqb9/Zvfvge4y4WWOhP5tVz1C6ZBe/NfhU7pqKLMA+6ads+99c=" # DOCKER_USER
- secure: "uJuuefmnJUuEH15ZD8xQilibx7EeBvMHBLoIZ8bgGHeleEImBD0XbD1ypvhYJKpviOmw5BkZmc9bVO8DGDEHYbSlIa2xDlF6vGrwgCEaxcMIhOAhv+dW9C/maJVieLOEPM01/fK2qdKESZaLvlopkWmxZwDyMObI9L7AMW9zQD8=" # BINTRAY_USER
- secure: "L3aPSNLySPXtWCW+xf8h/AAdquwNgxyTQpYOwexJmTPav82Qx8uQlp1yJkUmt+a+FLZDFfQeMivaHq0311RvuQVmkAJx49DjaddrwqOJut2UPsoVDn1WeuAcSHIXOq/0H+zgFMr/PGY0HXIsw1mTMhgheGJNqg09BvYWROCEAcA=" # BINTRAY_KEY
- secure: "sBMVn4C/WRWgoAytEFGx4CC5O55Q63h02AcuBnb1jXcBm0RenoBpzUPtxSseJwDPUA1o/UkuEDDjm3PosT5NF+dvED01VDFMsPVE11K0u6+avYy3jYXqyUEDW3G2o6Wo/2aqNjmd++8jskBdS9+Cx9gaFbgxfzSp0Yfu3oJm/4c=" # GH_TOKEN
install:
- if [ -a .git/shallow ]; then git fetch --unshallow; fi
- docker pull ${BASENAME} || ./travis/docker-build-and-push.sh ${BASENAME} travis/travis-base.Dockerfile
- ./travis/skip-pkg.sh || docker pull ${BASENAME_UBUNTU} || ./travis/docker-build-and-push.sh ${BASENAME_UBUNTU} travis/travis-base-ubuntu.Dockerfile
- ./travis/skip-pkg.sh || docker pull ${BASENAME_386} || ./travis/docker-build-and-push.sh ${BASENAME_386} travis/travis-base-386.Dockerfile
- ./travis/skip-pkg.sh || docker pull ${BASENAME_UBUNTU_386} || ./travis/docker-build-and-push.sh ${BASENAME_UBUNTU_386} travis/travis-base-ubuntu-386.Dockerfile
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 'rm -rf build; mkdir -p build && cd build && CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Werror -fno-common" meson .. -Ddocs=true -Dmans=true -Db_sanitize=address && ninja -v'
- 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
- docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} /bin/sh -c 'rm -rf distbuild; mkdir distbuild && cd distbuild && meson .. -Ddocs=true -Dmans=true && ninja -v dist'
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/debian-build.sh deb/debian-amd64/DIST
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_UBUNTU} ./travis/debian-build.sh deb/ubuntu-amd64/DIST
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_386} linux32 ./travis/debian-build.sh deb/debian-i386/DIST
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME_UBUNTU_386} linux32 ./travis/debian-build.sh deb/ubuntu-i386/DIST
- ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/docs.sh
- ./travis/skip-pkg.sh || travis/prep-bintray.sh
deploy:
- provider: bintray
file: travis/bintray-autobuild-debian.json
user: $BINTRAY_USER
key: $BINTRAY_KEY
skip_cleanup: true
on:
branch: next
condition: $CC = gcc
- provider: bintray
file: travis/bintray-autobuild-ubuntu.json
user: $BINTRAY_USER
key: $BINTRAY_KEY
skip_cleanup: true
on:
branch: next
condition: $CC = gcc
- provider: script
script: travis/deploy-github-pages.sh
skip_cleanup: true
on:
branch: next
condition: $CC = gcc
after_deploy:
- travis/cleanup-bintray.pl i3-autobuild
- travis/cleanup-bintray.pl i3-autobuild-ubuntu

View File

@@ -1,5 +1,14 @@
Revision history for AnyEvent-I3
0.19 2024-04-09
* use Carp for errors (includes stacktraces)
* introduce (preferred) RUN_COMMAND spelling
* migrate tooling to ExtUtils::MakeMaker
* implement the tick event
* introduce the sync IPC command
* introduce the GET_BINDING_STATE IPC command
0.18 2017-08-19
* support the GET_CONFIG command

View File

@@ -8,7 +8,6 @@ use AnyEvent::Handle;
use AnyEvent::Socket;
use AnyEvent;
use Encode;
use Scalar::Util qw(tainted);
use Carp;
=head1 NAME
@@ -17,11 +16,11 @@ AnyEvent::I3 - communicate with the i3 window manager
=cut
our $VERSION = '0.18';
our $VERSION = '0.19';
=head1 VERSION
Version 0.18
Version 0.19
=head1 SYNOPSIS
@@ -132,35 +131,10 @@ sub i3 {
AnyEvent::I3->new(@_)
}
# Calls i3, even when running in taint mode.
sub _call_i3 {
my ($args) = @_;
my $path_tainted = tainted($ENV{PATH});
# This effectively circumvents taint mode checking for $ENV{PATH}. We
# do this because users might specify PATH explicitly to call i3 in a
# custom location (think ~/.bin/).
(local $ENV{PATH}) = ($ENV{PATH} =~ /(.*)/);
# In taint mode, we also need to remove all relative directories from
# PATH (like . or ../bin). We only do this in taint mode and warn the
# user, since this might break a real-world use case for some people.
if ($path_tainted) {
my @dirs = split /:/, $ENV{PATH};
my @filtered = grep !/^\./, @dirs;
if (scalar @dirs != scalar @filtered) {
$ENV{PATH} = join ':', @filtered;
warn qq|Removed relative directories from PATH because you | .
qq|are running Perl with taint mode enabled. Remove -T | .
qq|to be able to use relative directories in PATH. | .
qq|New PATH is "$ENV{PATH}"|;
}
}
# Otherwise the qx() operator wont work:
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
chomp(my $result = qx(i3 $args));
# Circumventing taint mode again: the socket can be anywhere on the
# system and thats okay.
if ($result =~ /^([^\0]+)$/) {
return $1;
}
@@ -182,21 +156,21 @@ instance on the current DISPLAY which is almost always what you want.
sub new {
my ($class, $path) = @_;
$path = _call_i3('--get-socketpath') unless $path;
# This is the old default path (v3.*). This fallback line can be removed in
# a year from now. -- Michael, 2012-07-09
$path ||= '~/.i3/ipc.sock';
# We have I3SOCK now
$path ||= $ENV{I3SOCK};
$path ||= _call_i3('--get-socketpath');
# Check if we need to resolve ~
if ($path =~ /~/) {
# We use getpwuid() instead of $ENV{HOME} because the latter is tainted
# and thus produces warnings when running tests with perl -T
my $home = (getpwuid($<))[7];
my $home = $ENV{HOME};
confess "Could not get home directory" unless $home and -d $home;
$path =~ s/~/$home/g;
}
if(!-S $path) {
die "$path is not a socket", $/;
}
bless { path => $path } => $class;
}
@@ -315,6 +289,11 @@ sub subscribe {
# Register callbacks for each message type
for my $key (keys %{$callbacks}) {
if (!exists $events{$key}) {
warn "Could not subscribe to event type '$key'." .
" Supported events are " . join(" ", sort keys %events), $/;
next;
}
my $type = $events{$key};
$self->{callbacks}->{$type} = $callbacks->{$key};
}

View File

@@ -1,4 +1,4 @@
#!perl -T
#!perl
use Test::More tests => 1;

View File

@@ -1,4 +1,4 @@
#!perl -T
#!perl
# vim:ts=4:sw=4:expandtab
use Test::More tests => 3;

View File

@@ -1,4 +1,4 @@
#!perl -T
#!perl
# vim:ts=4:sw=4:expandtab
use Test::More tests => 3;

View File

@@ -1,4 +1,4 @@
#!perl -T
#!perl
use strict;
use warnings;

View File

@@ -1,4 +1,4 @@
#!perl -T
#!perl
use strict;
use warnings;

View File

@@ -1,4 +1,4 @@
#!perl -T
#!perl
use strict;
use warnings;

1
I3_VERSION Normal file
View File

@@ -0,0 +1 @@
4.19.1-non-git

View File

@@ -27,7 +27,7 @@ https://mesonbuild.com/Quick-guide.html#compiling-a-meson-project
In case youre unfamiliar:
$ mkdir -p build && cd build
$ meson ..
$ meson setup
$ ninja
Please make sure that i3-migrate-config-to-v4 and i3-config-wizard are

View File

@@ -1,7 +1,7 @@
![Logo](docs/logo-30.png) i3: A tiling window manager
=====================================================
[![Build Status](https://travis-ci.org/i3/i3.svg?branch=next)](https://travis-ci.org/i3/i3)
[![Build Status](https://github.com/i3/i3/actions/workflows/main.yml/badge.svg)](https://github.com/i3/i3/actions/workflows/main.yml)
[![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)

View File

@@ -1,27 +0,0 @@
┌──────────────────────────────┐
│ Release notes for i3 v4.19.1 │
└──────────────────────────────┘
This is i3 v4.19. This version is considered stable. All users of i3 are
strongly encouraged to upgrade.
This is a bugfix release for v4.19
┌────────────────────────────┐
│ Bugfixes │
└────────────────────────────┘
• fix workspaces not moving to assigned output after output becomes available
• fix duplicate bindcode after i3-config-wizard
• fix commented-out rofi call in default i3 config
┌────────────────────────────┐
│ Thanks! │
└────────────────────────────┘
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
Anaël Beutot, Imran Virani, Orestis Floros
-- Michael Stapelberg, 2021-02-01

62
RELEASE-NOTES-4.24 Normal file
View File

@@ -0,0 +1,62 @@
┌──────────────────────────────┐
│ Release notes for i3 v4.24 │
└──────────────────────────────┘
This is i3 v4.24. This version is considered stable. All users of i3 are
strongly encouraged to upgrade.
Aside from a number of bug fixes and detail improvements, the tiling drag
feature has been extended to allow swapping containers with the mouse.
See the swap_modifier in the userguide:
https://i3wm.org/docs/userguide.html
┌────────────────────────────┐
│ Changes in i3 v4.24 │
└────────────────────────────┘
• docs/userguide: add note to default_border about title bar in stacked/tabbed
• docs/userguide: fix focus output next example
• docs/hacking-howto: update build instructions
• docs/testsuite: update instructions
• docs/ipc: update section on IPC socket location
• docs/ipc: describe workspace events in more detail
• i3-sensible-terminal: add rio
• i3bar: use short-form text on a per-block basis
• reap zombie children when starting
• do not grab mouse pointer when executing bindings
• tiling drag: swap containers with the mouse
• disable automatic v3-to-v4 migration script
• pass _NET_WM_STATE_MAXIMIZED_{HORZ, VERT} (fixes tab bar in Google Chrome)
• avoid creating redundant containers when switching between layouts
• deprecate smart_borders in favour of hide_edge_borders smart/smart_no_gaps
• float windows of type _NET_WM_WINDOW_TYPE_NOTIFICATION by default
• add “popup_during_fullscreen all”
• mark fullscreen windows as maximized
• support multiple _NET_WM_STATE changes in one ClientMessage
• fix size_t format specifiers on 32-bit systems
┌────────────────────────────┐
│ Bugfixes │
└────────────────────────────┘
• i3bar: fix clicks when horizontal padding is used
• consider workspace_auto_back_and_forth in focus workspace
• workspace next/prev: do not skip identically numbered workspaces
• make order of numbered workspace consistent with non-numbered
• fix crash with focus output and command criteria matching scratchpad window
• fix crash when reloading config with invalid criteria
• fix error log related crash
┌────────────────────────────┐
│ Thanks! │
└────────────────────────────┘
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
a-kenji, Alessandro Vinciguerra, Bimba Laszlo, colona_, Eddie Lebow, Harimbola
Santatra, Junicchi, Malix, Nikolay Nechaev, Orestis Floros, rsgowman,
sethpollen, Seth Pollen, systec-awe, Tasos Sahanidis, Theo Buehler, Wesley
Schwengle, Yonas Yanfa, yuvallangerontheroad
-- Michael Stapelberg, 2024-11-06

View File

@@ -319,7 +319,7 @@
<dc:title>steckdenis</dc:title>
</cc:Agent>
</dc:creator>
<dc:description>Logo for I3, an improved dynamic tiling window manager: http://i3.zekjur.net/</dc:description>
<dc:description>Logo for i3 - an improved tiling window manager: http://i3.zekjur.net/</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
</cc:Work>

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -329,7 +329,7 @@
<dc:title>steckdenis</dc:title>
</cc:Agent>
</dc:creator>
<dc:description>Logo for I3, an improved dynamic tiling window manager: http://i3.zekjur.net/</dc:description>
<dc:description>Logo for i3 - an improved tiling window manager: http://i3.zekjur.net/</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
</cc:Work>

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

56
debian/changelog vendored
View File

@@ -1,3 +1,57 @@
i3-wm (4.24-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Wed, 06 Nov 2024 18:34:06 +0100
i3-wm (4.23-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Sun, 29 Oct 2023 15:42:11 +0100
i3-wm (4.22-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Mon, 02 Jan 2023 09:46:22 +0100
i3-wm (4.21.2-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Mon, 24 Oct 2022 21:22:36 +0200
i3-wm (4.21.1-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Mon, 24 Oct 2022 21:22:36 +0200
i3-wm (4.21-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Wed, 21 Sep 2022 18:14:14 +0200
i3-wm (4.20.1-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Wed, 03 Nov 2021 09:22:48 +0100
i3-wm (4.20-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Sat, 27 Feb 2021 10:32:17 +0100
i3-wm (4.19.2-1) unstable; urgency=medium
* New upstream release.
-- Michael Stapelberg <stapelberg@debian.org> Sat, 27 Feb 2021 10:32:17 +0100
i3-wm (4.19-1) unstable; urgency=medium
* New upstream release.
@@ -609,7 +663,7 @@ i3-wm (3.d-bf1-1) unstable; urgency=low
* Bugfix: Resize client after updating base_width/base_height
* Bugfix: Force render containers after setting the client active
* Bugfix: Fix two problems in resizing floating windows with right mouse
* Bugfix: Use more precise floating point arithmetics
* Bugfix: Use more precise floating point arithmetic
* Bugfix: Correctly place new windows below fullscreen windows
-- Michael Stapelberg <michael@stapelberg.de> Mon, 21 Dec 2009 22:33:02 +0100

4
debian/control vendored
View File

@@ -22,7 +22,7 @@ Build-Depends: debhelper (>= 10),
pkg-config,
libev-dev (>= 1:4.04),
libyajl-dev (>= 2.0.4),
libpcre3-dev (>= 1:8.10),
libpcre2-dev,
libstartup-notification0-dev (>= 0.10),
libcairo2-dev (>= 1.14.4),
libpango1.0-dev,
@@ -44,7 +44,7 @@ Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
Provides: x-window-manager
Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl, rxvt-unicode | x-terminal-emulator
Description: improved dynamic tiling window manager
Description: improved tiling window manager
Key features of i3 are good documentation, reasonable defaults (changeable in
a simple configuration file) and good multi-monitor support. The user
interface is designed for power users and emphasizes keyboard usage. i3 uses

4
debian/rules vendored
View File

@@ -17,9 +17,5 @@ override_dh_auto_configure:
# Set -Ddocdir; the default is /usr/share/doc/i3
dh_auto_configure -- -Ddocdir=/usr/share/doc/i3-wm -Dmans=true
override_dh_builddeb:
# bintray does not support xz currently.
dh_builddeb -- -Zgzip
%:
dh $@ --buildsystem=meson

View File

@@ -29,7 +29,7 @@
add_ignore_event, xcb_intern_atom, xcb_intern_atom_reply, fprintf, printf, free, load_configuration,%
XInternAtom, exit, strlen}}
}{}
\title{i3 - an improved dynamic tiling window manager}
\title{i3 - an improved tiling window manager}
\author{sECuRE beim NoName e.V.\\
~\\
powered by \LaTeX, of course}

View File

@@ -147,16 +147,17 @@ After pressing "b" in the crash dialog, you will get a file called
id (PID) and the second one is incremented each time you generate a backtrace,
starting at 0.
== Sending bug reports/debugging on IRC
In Linux, if the backtrace just says +No stack.+, that's because gdb does not
have necessary permissions to attach to a running process. You can fix that by
running from a terminal (you can open a new tty, e.g. with ctrl-alt-F2):
When sending bug reports, please attach the *whole* log file. Even if you think
you found the section which clearly highlights the problem, additional
information might be necessary to completely diagnose the problem.
---------------------------------------------------------------------
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
---------------------------------------------------------------------
When debugging with us in IRC, be prepared to use a so-called nopaste service
such as https://pastebin.com because pasting large amounts of text in IRC
sometimes leads to incomplete lines (servers have line length limitations) or
flood kicks.
Afterwards, try re-generating the stack trace. Note that this setting re-sets
after reboot, see more info at
https://www.kernel.org/doc/Documentation/security/Yama.txt.
== Debugging i3bar

BIN
docs/gaps1920.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -14,7 +14,7 @@ you find necessary, please do not hesitate to contact me.
<p>
++++
This document is not 100% up to date. Specifically, everything up to and
including <<data_structures>> has been updated recently. The rest might contain
including <<startup>> has been updated recently. The rest might contain
outdated information.
++++
</p>
@@ -28,36 +28,24 @@ https://mesonbuild.com/[The Meson Build system]; see
https://mesonbuild.com/Quick-guide.html#compiling-a-meson-project[Quickstart
Guide → Compiling a Meson project]. In case youre unfamiliar:
$ mkdir -p build && cd build
$ meson ..
$ ninja
mkdir -p build
meson setup build
meson compile -C build
=== Build system features
* We use the +AX_ENABLE_BUILDDIR+ macro to enforce builds happening in a separate
directory. This is a prerequisite for the +AX_EXTEND_SRCDIR+ macro and building
in a separate directory is common practice anyway. In case this causes any
trouble when packaging i3 for your distribution, please open an issue.
* +ninja test+ runs the i3 testsuite. See docs/testsuite for details.
* +make check+ runs the i3 testsuite. See docs/testsuite for details.
* +meson dist+ builds a release tarball and runs tests on the result.
* +make distcheck+ (runs testsuite on +make dist+ result, tiny bit quicker
feedback cycle than waiting for the travis build to catch the issue).
* +meson -Ddocs=true -Dmans=true+ will enable the options to build docs and
manpages. These options require additional dependencies that are normally not
required for users who just want to build i3.
* +make uninstall+ (occasionally requested by users who compile from source)
* +make+ will build manpages/docs by default if the tools are installed.
Conversely, manpages/docs are not tried to be built for users who dont want
to install all these dependencies to get started hacking on i3. Manpages and
docs can be disabled with the +--disable-mans++ and ++--disable-docs++
configure options respectively.
* non-release builds will enable address sanitizer by default. Use the
+--disable-sanitizers+ configure option to turn off all sanitizers, and see
+--help+ for available sanitizers.
* Coverage reports are now generated using +make check-code-coverage+, which
requires specifying +--enable-code-coverage+ when calling configure.
* +meson -Db_sanitize=address+ will enable the address sanitizer which is
disabled by default. A summary of memory leaks will be printed on program
exit. This can include false-positives. For other options of the +b_sanitize+
flag see https://mesonbuild.com/Builtin-options.html.
== Pull requests
@@ -341,30 +329,26 @@ ensure that the operating system on which i3 is compiled has all the expected
features, i3 comes with +include/queue.h+. On BSD systems, you can use +man
queue(3)+. On Linux, you have to use google (or read the source).
The lists used are +SLIST+ (single linked lists), +CIRCLEQ+ (circular
queues) and +TAILQ+ (tail queues). Usually, only forward traversal is necessary,
so an +SLIST+ works fine. If inserting elements at arbitrary positions or at
the end of a list is necessary, a +TAILQ+ is used instead. However, for the
windows inside a container, a +CIRCLEQ+ is necessary to go from the currently
selected window to the window above/below.
The lists used are +SLIST+ (single linked lists), +CIRCLEQ+ (circular queues)
and +TAILQ+ (tail queues). Usually, +TAILQ+ is used which allows inserting
elements at arbitrary positions or at the end of the list. If only forward
traversal is necessary, an +SLIST+ can be used. +CIRCLEQ+ is used just to
manage the X11 state of each window.
== Naming conventions
[[startup]]
== Startup (src/main.c, main())
There is a row of standard variables used in many events. The following names
should be chosen for those:
* +conn+ is the xcb_connection_t
* +event+ is the event of the particular type
* +con+ names a container
* +current+ is a loop variable when using +TAILQ_FOREACH+ etc.
== Startup (src/mainx.c, main())
Among other things, the main() function does the following:
* Establish the xcb connection
* Load the i3 config
* Check for XKB extension on the separate X connection, load Xcursor
* Check for RandR screens (with a fall-back to Xinerama)
* Set up EWMH hints
* Grab the keycodes for which bindings exist
* Check for XRandR screens
* Manage all existing windows
* Exec configured startup processes
* Start i3bar if configured
* Enter the event loop
== Keybindings
@@ -400,8 +384,8 @@ the correct state.
Then, it looks through all bindings and gets the one which matches the received
event.
The bound command is parsed by the cmdparse lexer/parser, see +parse_cmd+ in
+src/cmdparse.y+.
The bound command is parsed by the i3 parser, see +parse_command+ in
+src/commands_parser.c+.
== Manage windows (src/main.c, manage_window() and reparent_window())
@@ -564,9 +548,7 @@ split container.
==== Default layout
In default layout, containers are placed horizontally or vertically next to
each other (depending on the +con->orientation+). If a child is a leaf node (as
opposed to a split container) and has border style "normal", appropriate space
will be reserved for its window decoration.
each other (depending on the +con->orientation+).
==== Stacked layout

View File

@@ -83,7 +83,7 @@ $parser->html_header_after_title(
<ul id="nav">
<li><a style="border-bottom: 2px solid #fff" href="/docs">Docs</a></li>
<li><a href="/screenshots">Screens</a></li>
<li><a href="https://www.reddit.com/r/i3wm/">FAQ</a></li>
<li><a href="https://www.github.com/i3/i3/discussions">Get Help</a></li>
<li><a href="/contact">Contact</a></li>
<li><a href="https://bugs.i3wm.org/">Bugs</a></li>
</ul>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -191,7 +191,7 @@ separator_block_width::
is 9 pixels), since the separator line is drawn in the middle.
markup::
A string that indicates how the text of the block should be parsed. Set to
+"pango"+ to use https://developer.gnome.org/pango/stable/pango-Markup.html[Pango markup].
+"pango"+ to use https://developer.gnome.org/pango/1.46/[Pango markup].
Set to +"none"+ to not use any markup (default). Pango markup only works
if you use a pango font.

View File

@@ -0,0 +1,184 @@
i3bar workspace buttons protocol
================================
This document explains the protocol in which i3bar expects input for
configuring workspace buttons. This feature is available since i3 version 4.23.
The program defined by the +workspace_command+ configuration option for i3bar can
modify the workspace buttons displayed by i3bar. The command should constantly
print in its standard output a stream of messages following the protocol
defined in this page.
If you are looking for the status line protocol instead, see https://i3wm.org/docs/i3bar-protocol.html.
== The protocol
Each message should be a newline-delimited JSON array. The array is in the same
format as the +GET_WORKSPACES+ ipc event, see
https://i3wm.org/docs/ipc.html#_workspaces_reply.
As an example, this is the output of the +i3-msg -t get_workspaces+ command:
------------------------------
[
{
"id": 94131549984064,
"num": 1,
"name": "1",
"visible": false,
"focused": false,
"output": "HDMI-A-0",
"urgent": false
},
{
"id": 94131550477584,
"num": 2,
"name": "2",
"visible": true,
"focused": true,
"output": "HDMI-A-0",
"urgent": false
},
{
"id": 94131550452704,
"num": 3,
"name": "3:some workspace",
"visible": false,
"focused": false,
"output": "HDMI-A-0",
"urgent": false
}
]
------------------------------
Please note that this example was pretty printed for human consumption, with
the +"rect"+ field removed. Workspace button commands should output each array
in one line.
Each element in the array represents a workspace. i3bar creates one workspace
button for each element in the array. The order of these buttons is the same as
the order of the elements in the array.
In general, we recommend subscribing to the +workspace+ and +output+
https://i3wm.org/docs/ipc.html#_workspace_event[events],
fetching the current workspace information with +GET_WORKSPACES+, modifying the
JSON array in the response according to your needs and then printing it to the
standard output. However, you are free to build a new message from the ground
up.
=== Workspace objects in detail
The documentation of +GET_WORKSPACES+ should be sufficient to understand the
meaning of each property but here we provide extra notes for each property and
its meaning with respect to i3bar.
All properties but +name+ are optional.
id (integer)::
If it is included it will be used to switch to that workspace when you
click the corresponding button. If it's not provided, the +name+ will be
used. You can use the +id+ field to present workspaces under a modified
name.
num (integer)::
The only use of a workspace's number is if the +strip_workspace_numbers+
setting is enabled.
name (string)::
The only required property. If an +id+ is provided you can freely change
the +name+ as you wish, effectively renaming the buttons of i3bar.
visible (boolean)::
Defaults to +false+ if not included. +focused+ takes precedence over it,
however +visible+ is important for more than one monitor.
focused (boolean)::
Defaults to +false+ if not included. Generally, exactly one of the
workspaces should be +focused+. If not, no button will have the
+focused_workspace+ color.
urgent (boolean)::
Defaults to +false+ if not included.
rect (map)::
Not used by i3bar but will be ignored.
output (string)::
Defaults to the primary output if not included.
== Examples
These example scripts require the https://stedolan.github.io/jq/[jq] utility to
be installed but otherwise just use the standard +i3-msg+ utility included with
i3. However, you can write your own scripts in your preferred language, with
the help of one of the
https://i3wm.org/docs/ipc.html#_see_also_existing_libraries[pre-existing i3
libraries]
=== Base configuration
------------------------------
bar {
workspace_command /path/to/your/script.sh
}
------------------------------
=== Re-create the default behaviour of i3bar
Not very useful by itself but this will be the basic building block of all the
following scripts. This one does not require +jq+.
------------------------------
#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
# Initially print the current workspaces before we receive any events. This
# avoids having an empty bar when starting up.
i3-msg -t get_workspaces;
# Then, while we receive events, update the workspace information.
while read EVENT; do i3-msg -t get_workspaces; done;
}
------------------------------
=== Hide workspace named +foo+ unless if it is focused.
------------------------------
#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
i3-msg -t get_workspaces;
while read EVENT; do i3-msg -t get_workspaces; done;
} | jq --unbuffered -c '[ .[] | select(.name != "foo" or .focused) ]'
------------------------------
Important! Make sure you use the +--unbuffered+ flag with +jq+, otherwise you
might not get the changes in real-time but whenever they are flushed, which
might mean that you are getting an empty bar until enough events are written.
=== Show empty workspaces +foo+ and +bar+ on LVDS1 even if they do not exist at the moment.
------------------------------
#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
i3-msg -t get_workspaces;
while read EVENT; do i3-msg -t get_workspaces; done;
} | jq --unbuffered -c '
def fake_ws(name): {
name: name,
output: "LVDS1",
};
. + [ fake_ws("foo"), fake_ws("bar") ] | unique_by(.name)
'
------------------------------
=== Sort workspaces in reverse alphanumeric order
------------------------------
#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
i3-msg -t get_workspaces;
while read EVENT; do i3-msg -t get_workspaces; done;
} | jq --unbuffered -c 'sort_by(.name) | reverse'
------------------------------
=== Append "foo" to the name of each workspace
------------------------------
#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
i3-msg -t get_workspaces;
while read EVENT; do i3-msg -t get_workspaces; done;
} | jq --unbuffered -c '[ .[] | .name |= . + " foo" ]'
------------------------------

288
docs/ipc
View File

@@ -10,14 +10,22 @@ workspace bar.
The method of choice for IPC in our case is a unix socket because it has very
little overhead on both sides and is usually available without headaches in
most languages. In the default configuration file, the ipc-socket gets created
in +/tmp/i3-%u.XXXXXX/ipc-socket.%p+ where +%u+ is your UNIX username, +%p+ is
the PID of i3 and XXXXXX is a string of random characters from the portable
filename character set (see mkdtemp(3)). You can get the socketpath from i3 by
calling +i3 --get-socketpath+.
most languages.
By default i3 will set the path of the IPC socket based on:
All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
X11 property, stored on the X11 root window.
1. The +ipc-socket+ configuration directive if it is used
2. The +I3SOCK+ environmental variable if it is set
3. +$XDG_RUNTIME_DIR/i3/ipc-socket.%p+ if the directory is available where +%p+
is the PID of i3 and XXXXXX is a string of random characters
4. +/tmp/i3-%u.XXXXXX/ipc-socket.%p+ where +%u+ is your UNIX username
You can get the socketpath from i3 by executing +i3 --get-socketpath+, which
will print the path to the standard output (plus a newline) or by reading the
+I3SOCK+ environmental variable.
All i3 utilities, like +i3-msg+ and +i3-input+ will determine the path of the
IPC socket frome the +I3SOCK+ environmental variable if it is set or the
+I3_SOCKET_PATH+ X11 property, stored on the X11 root window.
[WARNING]
.Use an existing library!
@@ -39,12 +47,12 @@ my $sock = IO::Socket::UNIX->new(Peer => $path);
== Sending messages to i3
To send a message to i3, you have to format in the binary message format which
i3 expects. This format specifies a magic string in the beginning to ensure
the integrity of messages (to prevent follow-up errors). Following the magic
string comes the length of the payload of the message as 32-bit integer, and
the type of the message as 32-bit integer (the integers are not converted, so
they are in native byte order).
To send a message to i3, you have to format it in the binary message format
which i3 expects. This format specifies a magic string in the beginning to
ensure the integrity of messages (to prevent follow-up errors). Following the
magic string comes the length of the payload of the message as a 32-bit
integer, and the type of the message as a 32-bit integer (the integers are not
converted, so they are in native byte order).
The magic string currently is "i3-ipc" and will only be changed when a change
in the IPC API is done which breaks compatibility (we hope that we dont need
@@ -96,18 +104,30 @@ $sock->write(format_ipc_command("exit"));
== Receiving replies from i3
Replies from i3 usually consist of a simple string (the length of the string
is the message_length, so you can consider them length-prefixed) which in turn
contain the JSON serialization of a data structure. For example, the
GET_WORKSPACES message returns an array of workspaces (each workspace is a map
with certain attributes).
Each message sent to i3 will cause exactly one reply to be sent in return. The
order of the sent replies will always correspond to the order of the sent
requests. The only exception to this is <<events>>, which (once subscribed to)
may be sent at any time (though never in the middle of another event or reply).
=== Reply format
It is generally safe to send several messages to i3 without first waiting for a
reply for each one (pipelining) -- though, note that depending on the language /
network library you use, writing to the socket without also reading from it may
cause a deadlock due to the socket buffers getting full.
The reply format is identical to the normal message format. There also is
the magic string, then the message length, then the message type and the
payload.
The payload of replies from i3 usually consists of a simple string (the length
of the string is the message_length, so you can consider them length-prefixed),
which in turn contain the JSON serialization of a data structure. For example,
the GET_WORKSPACES message returns an array of workspaces (each workspace is a
map with certain attributes).
Replies currently have a 1:1 correspondence to messages, with the message type
of the reply corresponding to the message type of the message which caused the
reply to be sent.
The following reply types are implemented:
COMMAND (0)::
@@ -127,16 +147,30 @@ BAR_CONFIG (6)::
VERSION (7)::
Reply to the GET_VERSION message.
BINDING_MODES (8)::
Reply to the GET_BINDING_MODES message.
Reply to the GET_BINDING_MODES message.
GET_CONFIG (9)::
Reply to the GET_CONFIG message.
TICK (10)::
Reply to the SEND_TICK message.
SYNC (11)::
Reply to the SYNC message.
GET_BINDING_STATE (12)::
Reply to the GET_BINDING_STATE message.
== Messages and replies
[[_command_reply]]
=== COMMAND reply
=== RUN_COMMAND / COMMAND
Run the payload as an https://i3wm.org/docs/userguide.html#list_of_commands[i3
command] (like the commands you can bind to keys).
*Message:*
The message payload is the string containing the command to execute. There is
no JSON encoding or trailing newline.
*Reply:*
The reply consists of a list of serialized maps for each command that was
parsed. Each has the property +success (bool)+ and may also include a
@@ -170,7 +204,15 @@ When the specified command cannot be parsed, `success` will be false and
-------------------
[[_workspaces_reply]]
=== WORKSPACES reply
=== GET_WORKSPACES / WORKSPACES
Get the list of current workspaces.
*Message:*
No payload.
*Reply:*
The reply consists of a serialized list of workspaces. Each workspace has the
following properties:
@@ -234,7 +276,16 @@ output (string)::
-------------------
[[_subscribe_reply]]
=== SUBSCRIBE reply
=== SUBSCRIBE
Subscribe this IPC connection to the event types specified in the message
payload. See <<events>>.
*Message:*
A JSON-encoded array of event types to subscribe to.
*Reply:*
The reply consists of a single serialized map. The only property is
+success (bool)+, indicating whether the subscription was successful (the
@@ -246,7 +297,15 @@ default) or whether a JSON parse error occurred.
-------------------
[[_outputs_reply]]
=== OUTPUTS reply
=== GET_OUTPUTS / OUTPUTS
Get the list of current outputs.
*Message:*
No payload.
*Reply:*
The reply consists of a serialized list of outputs. Each output has the
following properties:
@@ -257,7 +316,7 @@ active (boolean)::
Whether this output is currently active (has a valid mode).
primary (boolean)::
Whether this output is currently the primary output.
current_workspace (string)::
current_workspace (string or null)::
The name of the current workspace that is visible on this output. +null+ if
the output is not active.
rect (map)::
@@ -293,7 +352,15 @@ rect (map)::
-------------------
[[_tree_reply]]
=== TREE reply
=== GET_TREE / TREE
Get the i3 layout tree.
*Message:*
No payload.
*Reply:*
The reply consists of a serialized tree. Each node in the tree (representing
one container) has at least the properties listed below. While the nodes might
@@ -329,7 +396,7 @@ orientation (string)::
"vertical".
THIS FIELD IS OBSOLETE. It is still present, but your code should not
use it. Instead, rely on the layout field.
percent (float)::
percent (float or null)::
The percentage which this container takes in its parent. A value of
+null+ means that the percent property does not make sense for this
container, for example for the root container.
@@ -350,21 +417,30 @@ deco_rect (map)::
The coordinates of the *window decoration* inside its container. These
coordinates are relative to the container and do not include the actual
client window.
actual_deco_rect (map)::
See +deco_rect+. i3 v4.22 changed the way title bars are rendered. Before
i3 v4.22, the deco_rect was always relative to the parent coordinates.
Starting with i3 v4.22, this remains true for tabbed/stacked containers
(actual_deco_rect is identical to deco_rect), but for normal-border leaf
containers within vertical/horizontal split containers, actual_deco_rect
is relative to the container itself. For more background, see
https://github.com/i3/i3/issues/1966
geometry (map)::
The original geometry the window specified when i3 mapped it. Used when
switching a window to floating mode, for example.
window (integer)::
window (integer or null)::
The X11 window ID of the *actual client window* inside this container.
This field is set to null for split containers or otherwise empty
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)::
This optional field contains all available X11 window properties from the
following list: *title*, *instance*, *class*, *window_role* and *transient_for*.
following list: *title*, *instance*, *class*, *window_role*, *machine*
and *transient_for*.
window_type (string)::
The window type (_NET_WM_WINDOW_TYPE). Possible values are undefined, normal,
dialog, utility, toolbar, splash, menu, dropdown_menu, popup_menu, tooltip and
notification.
The window type (_NET_WM_WINDOW_TYPE). Possible values are `undefined`,
unknown, normal, dialog, utility, toolbar, splash, menu, dropdown_menu,
popup_menu, tooltip and notification.
urgent (bool)::
Whether this container (window, split container, floating container or
workspace) has the urgency hint set, directly or indirectly. All parent
@@ -379,6 +455,9 @@ focus (array of integer)::
order. Traversing the tree by following the first entry in this array
will result in eventually reaching the one node with +focused+ set to
true.
sticky (bool)::
Whether this window is "sticky". If it is also floating, this window will
be present on all workspaces on the same output.
fullscreen_mode (integer)::
Whether this container is in fullscreen state or not.
Possible values are
@@ -386,12 +465,18 @@ fullscreen_mode (integer)::
+1+ (fullscreened on output) or
+2+ (fullscreened globally).
Note that all workspaces are considered fullscreened on their respective output.
floating (string)::
Floating state of container.
Can be either "auto_on", "auto_off", "user_on" or "user_off"
nodes (array of node)::
The tiling (i.e. non-floating) child containers of this node.
floating_nodes (array of node)::
The floating child containers of this node. Only non-empty on nodes with
type +workspace+.
scratchpad_state (string)::
Whether the window is not in the scratchpad ("none"), freshly moved to
the scratchpad but not yet resized ("fresh") or moved to the scratchpad
and resized ("changed").
Please note that in the following example, I have left out some keys/values
which are not relevant for the type of the node. Otherwise, the example would
@@ -532,7 +617,15 @@ JSON dump:
-----------------------
[[_marks_reply]]
=== MARKS reply
=== GET_MARKS / MARKS
Gets the names of all currently set marks.
*Message:*
No payload.
*Reply:*
The reply consists of a single array of strings for each container that has a
mark. A mark can only be set on one container, so the array is unique.
@@ -541,7 +634,15 @@ The order of that array is undefined.
If no window has a mark the response will be the empty array [].
[[_bar_config_reply]]
=== BAR_CONFIG reply
=== GET_BAR_CONFIG / BAR_CONFIG
Gets the specified bar configuration or the names of all bar configurations if payload is empty.
*Message:*
No payload, or the ID of the bar whose configuration to retrieve.
*Reply:*
This can be used by third-party workspace bars (especially i3bar, but others
are free to implement compatible alternatives) to get the +bar+ block
@@ -641,7 +742,15 @@ binding_mode_text/binding_mode_bg/binding_mode_border::
--------------
[[_version_reply]]
=== VERSION reply
=== GET_VERSION / VERSION
Gets the i3 version.
*Message:*
No payload.
*Reply:*
The reply consists of a single JSON dictionary with the following keys:
@@ -674,7 +783,15 @@ loaded_config_file_name (string)::
-------------------
[[_binding_modes_reply]]
=== BINDING_MODES reply
=== GET_BINDING_MODES / BINDING_MODES
Gets the names of all currently configured binding modes.
*Message:*
No payload.
*Reply:*
The reply consists of an array of all currently configured binding modes.
@@ -684,18 +801,66 @@ The reply consists of an array of all currently configured binding modes.
---------------------
[[_config_reply]]
=== CONFIG reply
=== GET_CONFIG / CONFIG
The config reply is a map which currently only contains the "config" member,
which is a string containing the config file as loaded by i3 most recently.
Returns the last loaded i3 config.
*Message:*
No payload.
*Reply:*
The config reply is a map which contains the following fields:
config (string)::
The top-level config file contents that i3 has loaded most recently.
This field is kept for backwards compatibility. See +included_configs+
instead.
included_configs (array of maps)::
i3 adds one entry to this array for each config file it loads, in
order. The first entrys +raw_contents+ are identical to the +config+
field.
Each +included_configs+ entry contains the following fields
path (string)::
Absolute path name to the config file that i3 loaded.
raw_contents (string)::
The raw contents of the file as i3 read them.
variable_replaced_contents (string)::
The contents of the file after i3 replaced all variables. This is useful
for debugging variable replacement.
*Example:*
-------------------
{ "config": "font pango:monospace 8\nbindsym Mod4+q exit\n" }
{
"config": "include font.cfg\n",
"included_configs": [
{
"path": "/home/michael/configfiles/i3/config",
"raw_contents": "include font.cfg\n",
"variable_replaced_contents": "include font.cfg\n"
},
{
"path": "/home/michael/configfiles/i3/font.cfg",
"raw_contents": "set $font pango:monospace 8\nfont $font",
"variable_replaced_contents": "set pango:monospace 8 pango:monospace 8\nfont pango:monospace 8\n"
}
],
}
-------------------
[[_tick_reply]]
=== TICK reply
=== SEND_TICK / TICK
Sends a tick event with the specified payload.
*Message:*
The payload of the tick event to send to IPC event listeners.
*Reply:*
The reply is a map containing the "success" member. After the reply was
received, the tick event has been written to all IPC connections which subscribe
@@ -709,7 +874,15 @@ events generated prior to the +SEND_TICK+ message (happened-before relation).
-------------------
[[_sync_reply]]
=== SYNC reply
=== SYNC
Sends an i3 sync event with the specified random value to the specified window.
*Message:*
A JSON-encoded map with the properties "rnd" and "window" (both integer).
*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
@@ -721,7 +894,15 @@ responded to.
-------------------
[[_binding_state_reply]]
=== GET_BINDING_STATE reply
=== GET_BINDING_STATE
Request the current binding state, i.e. the currently active binding mode name.
*Message:*
No payload.
*Reply:*
The binding_state reply is a map which currently only contains the "name"
member, which is the name of the currently active binding mode as a string.
@@ -823,9 +1004,18 @@ if ($is_event) {
=== workspace event
This event consists of a single serialized map containing a property
+change (string)+ which indicates the type of the change ("focus", "init",
"empty", "urgent", "reload", "rename", "restored", "move"). A
+current (object)+ property will be present with the affected workspace
+change (string)+ which indicates the type of the change.
* +empty+ the workspace has become empty
* +focus+ the workspace has received input focus
* +init+ the workspace has been created
* +move+ the workspace has been moved to a different output
* +reload+ i3 config has been reloaded
* +rename+ the workspace's name has changed
* +restored+ the workspace's layout has changed to a previously saved layout
* +urgent+ the workspace has become urgent or lost its urgent status
A +current (object)+ property will be present with the affected workspace
whenever the type of event affects a workspace (otherwise, it will be +null+).
When the change is "focus", an +old (object)+ property will be present with the
@@ -925,6 +1115,8 @@ binding that ran a command because of user input. The +change (string)+ field
indicates what sort of binding event was triggered (right now it will always be
+"run"+ but may be expanded in the future).
The +mode (string)+ field contains the name of the mode the binding was run in.
The +binding (object)+ field contains details about the binding that was run:
command (string)::

View File

@@ -185,9 +185,9 @@ Therefore, if you just start Emacs via dmenu, it will not get swallowed by that
container. Only if you start Emacs with the proper instance name (+emacs24
--name notmuch+), it will get swallowed.
You can match on "class", "instance", "window_role" and "title". All values are
case-sensitive regular expressions (PCRE). Use +xprop(1)+ and click into a
window to see its properties:
You can match on "class", "instance", "window_role", "title" and "machine". All
values are case-sensitive regular expressions (PCRE). Use +xprop(1)+ and click
into a window to see its properties:
--------------------------------------------------------------------------------
$ xprop

View File

@@ -4,11 +4,10 @@ Michael Stapelberg <michael@i3wm.org>
September 2012
This document explains how the i3 testsuite works, how to use it and extend it.
It is targeted at developers who not necessarily have been doing testing before
or have not been testing in Perl before. In general, the testsuite is not of
It is targeted at developers who haven't necessarily done testing before,
or have not used Perl for testing before. In general, the testsuite is not of
interest for end users.
== Introduction
The i3 testsuite is a collection of files which contain testcases for various
@@ -37,8 +36,8 @@ that, but it will also be useful for every future change.
Apart from this document, you should also have a look at:
1. The "Modern Perl" book, which can be found at
http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
1. The "Modern Perl" book:
https://i3wm.org/downloads/modern_perl_a4.pdf
2. The latest Perl documentation of the "i3test" (general testcase setup) and
"i3test::Test" (additional test instructions) modules:
https://build.i3wm.org/docs/lib-i3test.html respectively
@@ -77,30 +76,20 @@ used to install the testsuite. Many users prefer to use the more modern
The tests additionally require +Xephyr(1)+ to run a nested X server. Install
+xserver-xephyr+ on Debian or +xorg-server-xephyr+ on Arch Linux.
.Installing testsuite dependencies using cpanminus (preferred)
.Installing testsuite dependencies using cpanminus
--------------------------------------------------------------------------------
$ cd ~/i3/testcases
$ sudo apt-get install cpanminus
$ sudo cpanm .
# Install testsuite system-level dependencies. Xvfb is optional but recommended.
$ sudo apt-get install xcb-proto cpanminus xvfb xserver-xephyr
# Install dependencies in ~/perl5 local library
$ cpanm --local-lib=~/perl5 local::lib App::cpanminus Module::Install
# Activate the local library
$ eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib)
$ cd ~/i3/testcases/
$ cpanm .
$ cd ~/i3/AnyEvent-I3
$ sudo cpanm Module::Install
$ sudo cpanm .
$ cpanm .
--------------------------------------------------------------------------------
If you dont want to use cpanminus for some reason, the same works with cpan:
.Installing testsuite dependencies using cpan
--------------------------------------------------------------------------------
$ cd ~/i3/testcases
$ sudo cpan .
$ cd ~/i3/AnyEvent-I3
$ sudo cpan Module::Install
$ sudo cpan .
--------------------------------------------------------------------------------
In case you dont have root permissions, you can also install into your home
directory, see https://michael.stapelberg.de/cpan/
=== Mechanisms
==== Script: complete-run
@@ -120,48 +109,57 @@ tests are run under Xvfb.
---------------------------------------
$ cd ~/i3
$ mkdir -p build && cd build
$ mkdir -p build
$ meson ..
$ meson setup build
$ cd build
$ ninja
$ meson compile
# output omitted because it is very long
$ cd testcases
$ ./complete-run.pl
# output omitted because it is very long
All tests successful.
Files=78, Tests=734, 27 wallclock secs ( 0.38 usr 0.48 sys + 17.65 cusr 3.21 csys = 21.72 CPU)
Result: PASS
$ ./complete-run.pl t/04-floating.t
[:3] i3 startup: took 0.07s, status = 1
[:3] Running t/04-floating.t with logfile testsuite-2011-09-24-16-06-04-4.0.2-226-g1eb011a/i3-log-for-04-floating.t
[:3] t/04-floating.t finished
[:3] killing i3
output for t/04-floating.t:
ok 1 - use X11::XCB::Window;
ok 2 - The object isa X11::XCB::Window
ok 3 - Window is mapped
ok 4 - i3 raised the width to 75
ok 5 - i3 raised the height to 50
ok 6 - i3 did not map it to (0x0)
ok 7 - The object isa X11::XCB::Window
ok 8 - i3 let the width at 80
ok 9 - i3 let the height at 90
ok 10 - i3 mapped it to x=1
ok 11 - i3 mapped it to y=18
ok 12 - The object isa X11::XCB::Window
ok 13 - i3 let the width at 80
ok 14 - i3 let the height at 90
1..14
$ ./complete-run.pl t/005-floating.t
Running tests under Xvfb display :99
Starting 1 Xephyr instances, starting at :100...
Rough time estimate for this run: 9.65 seconds
Writing logfile to 'testsuite-2024-05-01-21-33-45-4.23-28-g5834b7e8/complete-run.log'...
[:100] i3/testcases/t/005-floating.t: finished
completed 0 of 1 tests
All tests successful.
Files=1, Tests=14, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.19 cusr 0.03 csys = 0.23 CPU)
Files=1, Tests=13, 0 wallclock secs ( 0.00 usr + 0.00 sys = 0.00 CPU)
Result: PASS
$ less latest/i3-log-for-04-floating.t
The slowest tests are:
i3/testcases/t/005-floating.t with 0.07 seconds
Test output:
[:100] i3/testcases/t/005-floating.t: starting
[:100] i3/testcases/t/005-floating.t: finished
output for i3/testcases/t/005-floating.t:
ok 1 - An object of class 'X11::XCB::Window' isa 'X11::XCB::Window'
ok 2 - Window is mapped
ok 3 - i3 raised the width to 75
ok 4 - i3 raised the height to 50
ok 5 - i3 did not map it to (0x0)
ok 6 - An object of class 'X11::XCB::Window' isa 'X11::XCB::Window'
ok 7 - i3 let the width at 80
ok 8 - i3 let the height at 90
ok 9 - i3 mapped it to x=20
ok 10 - i3 mapped it to y=20
ok 11 - An object of class 'X11::XCB::Window' isa 'X11::XCB::Window'
ok 12 - i3 let the width at 80
ok 13 - i3 let the height at 90
1..13
$ less latest/i3-log-for-005-floating.t
----------------------------------------
If your attempt to run the tests with a bare call to ./complete-run.pl fails, try this:
@@ -173,37 +171,34 @@ $ ./complete-run.pl --parallel=1 --keep-xserver-output
This will show the output of Xephyr, which is the X server implementation we
use for testing.
===== make command: +make check+
Make check runs the i3 testsuite.
You can still use ./testcases/complete-run.pl to get the interactive progress output.
===== ninja command: +ninja test+
+ninja test+ runs the i3 testsuite.
You can still use ./complete-run.pl to get the interactive progress output.
.Example invocation of +make check+
.Example invocation of +ninja test+
---------------------------------------
$ cd ~/i3
$ mkdir -p build && cd build
$ mkdir -p build
$ meson ..
$ meson setup build
$ cd build
$ ninja
# output omitted because it is very long
$ ninja test
[1/102] Generating config.h with a custom command
[1/2] Running all tests.
1/1 complete-run OK 34.39s
$ make check
# output omitted because it is very long
PASS: testcases/complete-run.pl
============================================================================
Testsuite summary for i3 4.13
============================================================================
# TOTAL: 1
# PASS: 1
# SKIP: 0
# XFAIL: 0
# FAIL: 0
# XPASS: 0
# ERROR: 0
============================================================================
Ok: 1
Expected Fail: 0
Fail: 0
Unexpected Pass: 0
Skipped: 0
Timeout: 0
$ less test-suite.log
Full log written to i3/build/meson-logs/testlog.txt
$ less latest/complete-run.log
----------------------------------------
==== Coverage testing
@@ -481,7 +476,7 @@ an i3 crash resulting in the testcase being unable to communicate with i3 via
IPC anymore.
[[i3_sync]]
== Appendix A: The i3 sync protocol
== Appendix A: The I3_SYNC protocol
Consider the following situation: You open two windows in your testcase, then
you use +focus left+ and want to verify that the X11 focus has been updated
@@ -499,9 +494,9 @@ is($x->input_focus, $left->id, 'left window focused');
However, the test fails. Sometimes. Apparently, there is a race condition in
your test. If you think about it, this is because you are using two different
pieces of software: You tell i3 to update focus, i3 confirms that, and then you
ask X11 to give you the current focus. There is a certain time i3 needs to
update the X11 state. If the testcase gets CPU time before X11 processed i3's
requests, the test will fail.
ask X11 to give you the current focus. There is a certain time that the X11
server needs to process the requests from i3. If the testcase's request for the
input focus is processed before i3's requests, the test will fail.
image::i3-sync.png["Diagram of the race condition", title="Diagram of the race condition"]
@@ -531,10 +526,10 @@ less robust.
The real solution for this problem is a mechanism which I call "the i3 sync
protocol". The idea is to send a request (which does not modify state) via X11
to i3 which will then be answered. Due to the request's position in the event
queue (*after* all previous events), you can be sure that by the time you
receive the reply, all other events have been dealt with by i3 (and, more
importantly, X11).
to i3 which will then be answered, again via X11. Because this answer is
generated via an X11 request, it will be sent to the X11 server *after* all
previous requests. Thus, you can be sure that by the time you receive the reply,
all other events have been dealt with by i3 (and, more importantly, X11).
image::i3-sync-working.png["Diagram of the i3 sync solution", title="Diagram of the i3 sync solution"]
@@ -569,7 +564,35 @@ i3 will then reply with the same ClientMessage, sent to the window specified in
request. You should use a random value in +data[1]+ and check that you received
the same one when getting the reply.
== Appendix B: Socket activation
== Appendix B: The sync IPC command
The above I3_SYNC protocol allows to synchronise with i3. However, it is not
enough for tests that also involve i3bar: There might still be messages from
i3bar in-flight even after synchronising with i3. Thus, there also exists a sync
IPC command, that is however not meant to be used directly. Instead, i3bar uses
it for implementing the I3_SYNC protocol.
The intended usage works like this:
1. You send an I3_SYNC message to i3bar's window. See <<i3_sync>>.
2. i3bar sends a SYNC IPC command to i3 with payload
+{"window":your-window-here,"rnd":your-random-value}+.
3. i3 reacts to this IPC command as if it received an I3_SYNC request via X11.
This protocol is used, for example, in t/525-i3bar-mouse-bindings.t: A mouse
button press on i3bar is triggered. i3bar reacts to this by sending IPC commands
to i3.
The necessary synchronisation is achieved by sending an I3_SYNC event to i3bar:
Because i3bar reacts with a sync IPC command to i3, all previous IPC commands from
i3bar will be handled first. Because i3 reacts via X11, all previous X11
requests from i3 will be handled by the X11 server first.
The actual test also has to sync with i3 first due to how X11 handling works.
For more details, refer to the documentation for +XAllowEvents+ with mode
+ReplayPointer+.
== Appendix C: Socket activation
Socket activation is a mechanism which was made popular by systemd, an init
replacement. It basically describes creating a listening socket before starting

View File

@@ -3,9 +3,9 @@ i3 Users Guide
Michael Stapelberg <michael@i3wm.org>
This document contains all the information you need to configure and use the i3
window manager. If it does not, please check https://www.reddit.com/r/i3wm/
first, then contact us on IRC (preferred) or post your question(s) on the
mailing list.
window manager. If it does not you can https://i3wm.org/contact/[contact us] on
https://github.com/i3/i3/discussions[GitHub Discussions], IRC, or the mailing
list.
== Default keybindings
@@ -53,12 +53,23 @@ existing window (rotated displays).
image:two_terminals.png[Two terminals]
To move the focus between the two terminals, you can use the direction keys
which you might know from the editor +vi+. However, in i3, your homerow is used
for these keys (in +vi+, the keys are shifted to the left by one for
compatibility with most keyboard layouts). Therefore, +$mod+j+ is left, +$mod+k+
is down, +$mod+l+ is up and `$mod+;` is right. So, to switch between the
terminals, use +$mod+k+ or +$mod+l+. Of course, you can also use the arrow keys.
To move the focus between the two terminals, you can use the arrow keys. For
convenience, the arrows are also available directly on the
https://en.wikipedia.org/wiki/Touch_typing[keyboards home row] underneath your
right hand:
|===
| `$mod+j` | left
| `$mod+k` | down
| `$mod+l` | up
| `$mod+;` | right
|===
Note that this differs by one key from the popular text editor `vi`, which was
https://twitter.com/hillelogram/status/1326600125569961991[developed on an
ADM-3A terminal and therefore uses `hjkl` instead of `jkl;`] -- i3s default is
meant to require minimal finger movement, but some `vi` users change their i3
config for consistency.
At the moment, your workspace is split (it contains two terminals) in a
specific direction (horizontal by default). Every window can be split
@@ -67,7 +78,7 @@ horizontally or vertically again, just like the workspace. The terminology is
or browser) and "split container" for containers that consist of one or more
windows.
TODO: picture of the tree
//TODO: picture of the tree
To split a window vertically, press +$mod+v+ before you create the new window.
To split it horizontally, press +$mod+h+.
@@ -185,6 +196,54 @@ provided by the i3 https://github.com/i3/i3/blob/next/etc/config.keycodes[defaul
Floating windows are always on top of tiling windows.
[[tiling_drag]]
=== Moving tiling containers with the mouse
Since i3 4.21, it's possible to drag tiling containers using the mouse. The
drag can be initiated either by dragging the window's titlebar or by pressing
the <<floating_modifier>> and dragging the container while holding the
left-click button. See the <<config_tiling_drag>> option for configuring which
action triggers the tiling drag.
Once the drag is initiated and the cursor has left the original container, drop
indicators are created according to the position of the cursor relatively to
the target container. These indicators help you understand what the resulting
<<tree>> layout is going to be after you release the mouse button.
The possible drop positions are:
Drop on container::
This happens when the mouse is relatively near the center of a container.
If the mouse is released, the result is exactly as if you had run the
+move container to mark+ command. See <<move_to_mark>>.
If the swap modifier is pressed before initiating the drag (+tiling_drag
swap_modifier+ set to Shift by default), the containers are swapped
instead. In that case, the result is exactly as if you had run the +swap
container with mark+ command. See <<swapping_containers>>.
Drop as sibling::
This happens when the mouse is relatively near the edge of a container. If
the mouse is released, the dragged container will become a sibling of the
target container, placed left/right/up/down according to the position of
the indicator.
This might or might not create a new v-split or h-split according to the
previous layout of the target container. For example, if the target
container is in an h-split and you drop the dragged container below it, the
new layout will have to be a v-split.
Drop to parent::
This happens when the mouse is relatively near the edge of a container (but
even closer to the border in comparison to the sibling case above) *and* if
that edge is also the parent container's edge. For example, if three
containers are in a horizontal layout then edges where this can happen is
the left edge of the left container, the right edge of the right container
and all bottom and top edges of all three containers.
If the mouse is released, the container is first dropped as a sibling to
the target container, like in the case above, and then is moved
directionally like with the +move left|right|down|up+ command. See
<<move_direction>>.
The color of the indicator matches the +client.focused+ setting. See <<client_colors>>.
[[tree]]
== Tree
i3 stores all information about the X11 outputs, workspaces and layout of the
@@ -263,7 +322,7 @@ single workspace on which you open three terminal windows. All these terminal
windows are directly attached to one node inside i3s layout tree, the
workspace node. By default, the workspace nodes orientation is +horizontal+.
Now you move one of these terminals down (+$mod+Shift+j+ by default). The
Now you move one of these terminals down (+$mod+Shift+k+ by default). The
workspace nodes orientation will be changed to +vertical+. The terminal window
you moved down is directly attached to the workspace and appears on the bottom
of the screen. A new (horizontal) container was created to accommodate the
@@ -299,14 +358,89 @@ keyboard layout. To start the wizard, use the command +i3-config-wizard+.
Please note that you must not have +~/.i3/config+, otherwise the wizard will
exit.
Since i3 4.0, a new configuration format is used. i3 will try to automatically
detect the format version of a config file based on a few different keywords,
but if you want to make sure that your config is read with the new format,
include the following line in your config file:
[[include]]
=== Include directive
---------------------
# i3 config file (v4)
---------------------
Since i3 v4.20, it is possible to include other configuration files from your i3
configuration.
*Syntax*:
-----------------
include <pattern>
-----------------
i3 expands `pattern` using shell-like word expansion, specifically using the
https://manpages.debian.org/wordexp.3[`wordexp(3)` C standard library function].
*Examples*:
--------------------------------------------------------------------------------
# Tilde expands to the users home directory:
include ~/.config/i3/assignments.conf
# Environment variables are expanded:
include $HOME/.config/i3/assignments.conf
# Wildcards are expanded:
include ~/.config/i3/config.d/*.conf
# Command substitution:
include ~/.config/i3/`hostname`.conf
# i3 loads each path only once, so including the i3 config will not result
# in an endless loop, but in an error:
include ~/.config/i3/config
# i3 changes the working directory while parsing a config file
# so that relative paths are interpreted relative to the directory
# of the config file that contains the path:
include assignments.conf
--------------------------------------------------------------------------------
If a specified file cannot be read, for example because of a lack of file
permissions, or because of a dangling symlink, i3 will report an error and
continue processing your remaining configuration.
To list all loaded configuration files, run `i3 --moreversion`:
--------------------------------------------------------------------------------
% i3 --moreversion
Binary i3 version: 4.19.2-87-gfcae64f7+ © 2009 Michael Stapelberg and contributors
Running i3 version: 4.19.2-87-gfcae64f7+ (pid 963940)
Loaded i3 config:
/tmp/i3.cfg (main) (last modified: 2021-05-13T16:42:31 CEST, 463 seconds ago)
/tmp/included.cfg (included) (last modified: 2021-05-13T16:42:43 CEST, 451 seconds ago)
/tmp/another.cfg (included) (last modified: 2021-05-13T16:42:46 CEST, 448 seconds ago)
--------------------------------------------------------------------------------
Variables are shared between all config files, but beware of the following limitation:
* You can define a variable and use it within an included file.
* You cannot use (in the parent file) a variable that was defined within an included file.
This is a technical limitation: variable expansion happens in a separate stage
before parsing include directives.
Conceptually, included files can only add to the configuration, not undo the
effects of already-processed configuration. For example, you can only add new
key bindings, not overwrite or remove existing key bindings. This means:
* The `include` directive is suitable for organizing large configurations into
separate files, possibly selecting files based on conditionals.
* The `include` directive is not suitable for expressing “use the default
configuration with the following changes”. For that case, we still recommend
copying and modifying the default config.
[NOTE]
====
Implementation-wise, i3 does not currently construct one big configuration from
all `include` directives. Instead, i3s config file parser interprets all
configuration directives in its `parse_file()` function. When processing an
`include` configuration directive, the parser recursively calls `parse_file()`.
This means the evaluation order of files forms a tree, or one could say i3 uses
depth-first traversal.
====
=== Comments
@@ -373,8 +507,8 @@ your bindings in the same physical location on the keyboard, use keycodes.
If you dont switch layouts, and want a clean and simple config file, use
keysyms.
Some tools (such as +import+ or +xdotool+) might be unable to run upon a
KeyPress event, because the keyboard/pointer is still grabbed. For these
Some tools (such as +xdotool+) might be unable to run upon a
KeyPress event, because the keyboard is still grabbed. For these
situations, the +--release+ flag can be used, which will execute the command
after the keys have been released.
@@ -443,7 +577,7 @@ for the keybinding.
# The middle button over a titlebar kills the window
bindsym --release button2 kill
# The middle button and a modifer over any part of the window kills the window
# The middle button and a modifier over any part of the window kills the window
bindsym --whole-window $mod+button2 kill
# The right button toggles floating
@@ -597,14 +731,18 @@ Default is +left+
title_align left|center|right
---------------------------------------------
[[default_border]]
=== Default border style for new windows
This option determines which border style new windows will have. The default is
This option determines which border style *new* windows will have. The default is
+normal+. Note that default_floating_border applies only to windows which are starting out as
floating windows, e.g., dialog windows, but not windows that are floated later on.
Setting border style to +pixel+ eliminates title bars. The border style +normal+ allows you to
adjust edge border width while keeping your title bar.
Setting border style to +pixel+ eliminates title bars in split layouts. The border style
+normal+ allows you to adjust edge border width while keeping your title bar.
The title bar is always visible in stacking and tabbed layouts, and this cannot be changed
through configuration.
*Syntax*:
---------------------------------------------
@@ -639,14 +777,23 @@ default_border pixel 3
=== Hiding borders adjacent to the screen edges
You can hide container borders adjacent to the screen edges using
+hide_edge_borders+. This is useful if you are using scrollbars, or do not want
to waste even two pixels in displayspace. The "smart" setting hides borders on
workspaces with only one window visible, but keeps them on workspaces with
multiple windows visible. Default is none.
+hide_edge_borders+ (the default is +none+). Hiding borders is useful if you are
using scrollbars, or do not want to waste even two pixels in displayspace.
The "smart" setting hides borders on workspaces with only one window visible,
but keeps them on workspaces with multiple windows visible.
The "smart_no_gaps" setting hides edge-specific borders of a container if the
container is the only container on its workspace and the gaps to the screen edge
are +0+.
[[_smart_borders]]
+hide_edge_borders+ has replaced the old +smart_borders+ syntax. Use the former
instead of the latter.
*Syntax*:
-----------------------------------------------
hide_edge_borders none|vertical|horizontal|both|smart
hide_edge_borders none|vertical|horizontal|both|smart|smart_no_gaps
-----------------------------------------------
*Example*:
@@ -800,7 +947,7 @@ considered.
*Syntax*:
------------------------------------------------------------
assign <criteria> [→] [workspace] [number] <workspace>
assign <criteria> [→] output left|right|up|down|primary|<output>
assign <criteria> [→] output left|right|up|down|primary|nonprimary|<output>
------------------------------------------------------------
*Examples*:
@@ -832,6 +979,9 @@ assign [class="^URxvt$"] → output right
# Assign urxvt to the primary output
assign [class="^URxvt$"] → output primary
# Assign urxvt to the first non-primary output
assign [class="^URxvt$"] → output nonprimary
----------------------
Note that you might not have a primary output configured yet. To do so, run:
@@ -945,6 +1095,7 @@ workspace 5 output VGA1 LVDS1
workspace "2: vim" output VGA1
---------------------------
[[client_colors]]
=== Changing colors
You can change all colors which i3 uses to draw the window decorations.
@@ -961,6 +1112,10 @@ client.focused::
client.focused_inactive::
A client which is the focused one of its container, but it does not have
the focus at the moment.
client.focused_tab_title::
Tab or stack container title that is the parent of the focused container
but not directly focused. Defaults to focused_inactive if not specified and
does not use the indicator and child_border colors.
client.unfocused::
A client which is not the focused one of its container.
client.urgent::
@@ -974,7 +1129,8 @@ client.background::
which do not cover the whole area of this window expose the color. Note
that this colorclass only takes a single color.
Colors are in HTML hex format (#rrggbb), see the following example:
Colors are in HTML hex format (#rrggbb, optionally #rrggbbaa), see the following
example:
*Examples (default colors)*:
----------------------------------------------------------------------
@@ -1004,7 +1160,7 @@ i3 uses Unix sockets to provide an IPC interface. This allows third-party
programs to get information from i3, such as the current workspaces
(to display a workspace bar), and to control i3.
The IPC socket is enabled by default and will be created in
By default, an IPC socket will be created in
+$XDG_RUNTIME_DIR/i3/ipc-socket.%p+ if the directory is available, falling back
to +/tmp/i3-%u.XXXXXX/ipc-socket.%p+, where +%u+ is your UNIX username, +%p+ is
the PID of i3 and XXXXXX is a string of random characters from the portable
@@ -1068,19 +1224,21 @@ mouse_warping none
When you are in fullscreen mode, some applications still open popup windows
(take Xpdf for example). This is because these applications might not be aware
that they are in fullscreen mode (they do not check the corresponding hint).
There are three things which are possible to do in this situation:
i3 supports four options for this situation:
1. Display the popup if it belongs to the fullscreen application only. This is
the default and should be reasonable behavior for most users.
2. Just ignore the popup (dont map it). This wont interrupt you while you are
in fullscreen. However, some apps might react badly to this (deadlock until
you go out of fullscreen).
3. Leave fullscreen mode.
1. +smart+: Display the popup if it belongs to the fullscreen application only.
This is the default and should be reasonable behavior for most users.
2. +ignore+: Just ignore the popup (dont map it). This wont interrupt you
while you are in fullscreen. However, some apps might react badly to this
(deadlock until you go out of fullscreen).
3. +leave_fullscreen+: Leave fullscreen mode.
4. +all+: Since i3 4.24: Display all floating windows regardless to which
application they belong to.
*Syntax*:
-----------------------------------------------------
popup_during_fullscreen smart|ignore|leave_fullscreen
-----------------------------------------------------
---------------------------------------------------------
popup_during_fullscreen smart|ignore|leave_fullscreen|all
---------------------------------------------------------
*Example*:
------------------------------
@@ -1259,6 +1417,119 @@ fullscreen toggle
bindsym Mod1+F fullscreen toggle
-------------------
[[config_tiling_drag]]
=== Tiling drag
You can configure how to initiate the tiling drag feature (see <<tiling_drag>>).
The default is +modifier+.
Since i3 4.24, you can configure a modifier key which, when pressed, will swap
instead of moving containers when dropping directly onto another container.
Defaults to +Shift+. Note that you have to be pressing both the floating
modifer and the swap modifier before the drag is initiated.
*Syntax*:
--------------------------------
tiling_drag off
tiling_drag modifier|titlebar [modifier|titlebar]
tiling_drag swap_modifier <modifier>
--------------------------------
*Examples*:
--------------------------------
# Only initiate a tiling drag when the modifier is held:
tiling_drag modifier
# Initiate a tiling drag on either titlebar click or held modifier:
tiling_drag modifier titlebar
# Disable tiling drag altogether
tiling_drag off
# Use Control to swap containers
tiling_drag swap_modifier Control
# Setting the swap_modifier to be the same key as the floating modifier will
# always swap without the need to hold two keys
floating_modifier Mod4
tiling_drag swap_modifier Mod4
--------------------------------
[[gaps]]
=== Gaps
Since i3 4.22, you can configure window gaps.
“Gaps” are added spacing between windows (or split containers) and to the screen edges:
image::gaps1920.png["Screenshot of i3 with gaps enabled (10 px inner gaps, 20 px outer gaps)",title="Gaps enabled (10 px inner gaps, 20 px outer gaps)"]
You can configure two different kind of gaps:
1. Inner gaps are space between two adjacent windows (or split containers).
2. Outer gaps are space along the screen edges. You can configure each side
(left, right, top, bottom) separately.
If you are familiar with HTML and CSS, you can think of inner gaps as `padding`,
and of outer gaps as `margin`, applied to a `<div>` around your window or split
container.
Note that outer gaps are added to the inner gaps, meaning the total gap size
between a screen edge and a window (or split container) will be the sum of outer
and inner gaps.
You can define gaps either globally or per workspace using the following
syntax.
*Syntax*:
-----------------------
# Inner gaps for all windows: space between two adjacent windows (or split containers).
gaps inner <gap_size>[px]
# Outer gaps for all windows: space along the screen edges.
gaps outer|horizontal|vertical|top|left|bottom|right <gap_size>[px]
# Inner and outer gaps for all windows on a specific workspace.
# <ws> can be a workspace number or name.
workspace <ws> gaps inner <gap_size>[px]
workspace <ws> gaps outer|horizontal|vertical|top|left|bottom|right <gap_size>[px]
# Enabling “Smart Gaps” means no gaps will be shown when there is
# precisely one window or split container on the workspace.
#
# inverse_outer only enables outer gaps when there is exactly one
# window or split container on the workspace.
smart_gaps on|off|inverse_outer
-----------------------
Outer gaps can be configured for each side individually with the `top`, `left`,
`bottom` and `right` directive. `horizontal` and `vertical` are shortcuts for
`left`/`right` and `top`/`bottom`, respectively.
*Example*:
-------------------------------------------------
# Configure 5px of space between windows and to the screen edges.
gaps inner 5px
# Configure an additional 5px of extra space to the screen edges,
# for a total gap of 10px to the screen edges, and 5px between windows.
gaps outer 5px
# Overwrite gaps to 0, I need all the space I can get on workspace 3.
workspace 3 gaps inner 0
workspace 3 gaps outer 0
# Only enable outer gaps when there is exactly one window or split container on the workspace.
smart_gaps inverse_outer
-------------------------------------------------
Tip: Gaps can additionally be changed at runtime with the `gaps` command, see
<<changing_gaps>>.
Tip: You can find an
https://github.com/Airblader/i3/wiki/Example-Configuration[example
configuration] that uses modes to illustrate various gap configurations.
== Configuring i3bar
The bar at the bottom of your monitor is drawn by a separate process called
@@ -1340,6 +1611,30 @@ bar {
}
-------------------------------------------------
[[workspace_command]]
=== Workspace buttons command
Since i3 4.23, i3bar can run a program and use its +stdout+ output to define
the workspace buttons displayed on the left hand side of the bar. With this
feature, you can, for example, rename the buttons of workspaces, hide specific
workspaces, always show a workspace button even if the workspace does not exist
or change the order of the buttons.
Also see <<status_command>> for the statusline option and
https://i3wm.org/docs/i3bar-workspace-protocol.html for the detailed protocol.
*Syntax*:
------------------------
workspace_command <command>
------------------------
*Example*:
-------------------------------------------------
bar {
workspace_command /path/to/script.sh
}
-------------------------------------------------
=== Display mode
You can either have i3bar be visible permanently at one edge of the screen
@@ -1378,7 +1673,7 @@ the windows key). The default value for the hidden_state is hide.
mode dock|hide|invisible
hidden_state hide|show
modifier <Modifier>|none
------------------------
-------------------------
*Example*:
----------------
@@ -1548,7 +1843,7 @@ tray_output none|primary|<output>
---------------------------------
*Example*:
-------------------------
----------------------------------
# disable system tray
bar {
tray_output none
@@ -1563,7 +1858,7 @@ bar {
bar {
tray_output HDMI2
}
-------------------------
----------------------------------
Note that you might not have a primary output configured yet. To do so, run:
-------------------------
@@ -1587,10 +1882,10 @@ tray_padding <px> [px]
-------------------------
*Example*:
-------------------------
---------------------------
# Obey Fitts's law
tray_padding 0
-------------------------
---------------------------
=== Font
@@ -1728,8 +2023,8 @@ bar {
=== Colors
As with i3, colors are in HTML hex format (#rrggbb). The following colors can
be configured at the moment:
As with i3, colors are in HTML hex format (#rrggbb, optionally #rrggbbaa). The
following colors can be configured at the moment:
background::
Background color of the bar.
@@ -1813,6 +2108,49 @@ while +#000000FF+ will be a fully opaque black (the same as +#000000+).
Please note that due to the way the tray specification works, enabling this
flag will cause all tray icons to have a transparent background.
[[i3bar_padding]]
=== Padding
To make i3bar higher (without increasing the font size), and/or add padding to
the left and right side of i3bar, you can use the +padding+ directive:
*Syntax*:
--------------------------------------
bar {
# 2px left/right and 2px top/bottom padding
padding 2px
# 2px top/bottom padding, no left/right padding
padding 2px 0
# 2px top padding, no left/right padding, 4px bottom padding
padding 2px 0 4px
# four value syntax
padding top[px] right[px] bottom[px] left[px]
}
--------------------------------------
*Examples*:
--------------------------------------
bar {
# 2px left/right and 2px top/bottom padding
padding 2px
# 2px top/bottom padding, no left/right padding
padding 2px 0
# 2px top padding, no left/right padding, 4px bottom padding
padding 2px 0 4px
# 2px top padding, 6px right padding, 4px bottom padding, 1px left padding
padding 2px 6px 4px 1px
}
--------------------------------------
Note: As a convenience for users who migrate from i3-gaps to i3, the +height+
directive from i3-gaps is supported by i3, but should be changed to +padding+.
[[list_of_commands]]
== List of commands
@@ -1860,15 +2198,24 @@ bindsym $mod+x [class="(?i)firefox"] kill
# kill only the About dialog from Firefox
bindsym $mod+x [class="Firefox" window_role="About"] kill
# kill all windows except for Firefox and Gnome Terminal.
# case-insensitive and uses negative lookaheads, supported by PCRE
bindsym $mod+x [class="^(?i)(?!firefox)(?!gnome-terminal).*"] kill
# enable floating mode and move container to workspace 4
for_window [class="^evil-app$"] floating enable, move container to workspace 4
# enable window icons for all windows with extra horizontal padding of 1px
for_window [all] title_window_icon padding 1px
# move all floating windows to the scratchpad
bindsym $mod+x [floating] move scratchpad
------------------------------------
The criteria which are currently implemented are:
all::
Matches all windows. This criterion requires no value.
class::
Compares the window class (the second part of WM_CLASS). Use the
special value +\_\_focused__+ to match all windows having the same window
@@ -1885,6 +2232,10 @@ window_type::
Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are
+normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+,
+popup_menu+, +tooltip+ and +notification+.
machine::
Compares the name of the machine the client window is running on
(WM_CLIENT_MACHINE). Usually, it is equal to the hostname of the local
machine, but it may differ if remote X11 apps are used.
id::
Compares the X11 window ID, which you can get via +xwininfo+ for example.
title::
@@ -1922,9 +2273,9 @@ tiling_from::
tiling are matched. With "user", only windows that the user made tiling
are matched.
The criteria +class+, +instance+, +role+, +title+, +workspace+ and +mark+ are
actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for
information on how to use them.
The criteria +class+, +instance+, +role+, +title+, +workspace+, +machine+ and
+mark+ are actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc
perlre+ for information on how to use them.
[[exec]]
=== Executing applications (exec)
@@ -2074,6 +2425,9 @@ available:
<criteria>::
Sets focus to the container that matches the specified criteria.
See <<command_criteria>>.
workspace::
Sets focus to the workspace that contains the container that matches the
specified criteria.
left|right|up|down::
Sets focus to the nearest container in the given direction.
parent::
@@ -2100,10 +2454,11 @@ output::
*Syntax*:
----------------------------------------------
<criteria> focus
<criteria> focus workspace
focus left|right|down|up
focus parent|child|floating|tiling|mode_toggle
focus next|prev [sibling]
focus output left|right|up|down|primary|<output>
focus output left|right|down|up|current|primary|nonprimary|next|<output1> [output2]…
----------------------------------------------
*Examples*:
@@ -2111,6 +2466,10 @@ focus output left|right|up|down|primary|<output>
# Focus firefox
bindsym $mod+F1 [class="Firefox"] focus
# Focus the workspace where firefox is, without necessarily focusing firefox
# itself.
bindsym $mod+x [class="Firefox"] focus workspace
# Focus container on the left, bottom, top, right
bindsym $mod+j focus left
bindsym $mod+k focus down
@@ -2123,6 +2482,9 @@ bindsym $mod+u focus parent
# Focus last floating/tiling container
bindsym $mod+g focus mode_toggle
# Focus the next output (effectively toggles when you only have two outputs)
bindsym $mod+x focus output next
# Focus the output right to the current one
bindsym $mod+x focus output right
@@ -2131,6 +2493,12 @@ bindsym $mod+x focus output HDMI-2
# Focus the primary output
bindsym $mod+x focus output primary
# Cycle focus through non-primary outputs
bindsym $mod+x focus output nonprimary
# Cycle focus between outputs VGA1 and LVDS1 but not DVI0
bindsym $mod+x focus output VGA1 LVDS1
-------------------------------------------------
Note that you might not have a primary output configured yet. To do so, run:
@@ -2138,6 +2506,7 @@ Note that you might not have a primary output configured yet. To do so, run:
xrandr --output <output> --primary
-------------------------
[[move_direction]]
=== Moving containers
Use the +move+ command to move a container.
@@ -2186,6 +2555,7 @@ bindsym $mod+c move absolute position center
bindsym $mod+m move position mouse
-------------------------------------------------------
[[swapping_containers]]
=== Swapping containers
Two containers can be swapped (i.e., move to each other's position) by using
@@ -2264,9 +2634,8 @@ exhausting numbered ones and looks for numbered ones after exhausting named ones
See <<move_to_outputs>> for how to move a container/workspace to a different
RandR output.
Workspace names are parsed as
https://developer.gnome.org/pango/stable/pango-Markup.html[Pango markup]
by i3bar.
Workspace names are parsed as https://developer.gnome.org/pango/1.46/[Pango
markup] by i3bar.
[[back_and_forth]]
To switch back to the previously focused workspace, use +workspace
@@ -2373,9 +2742,11 @@ bindsym $mod+2 workspace number "2: mail"
If a workspace does not exist, the command +workspace number "1: mail"+ will
create workspace "1: mail".
If a workspace with number 1 does already exist, the command will switch to this
If a workspace with number 1 already exists, the command will switch to this
workspace and ignore the text part. So even when the workspace has been renamed
to "1: web", the above command will still switch to it.
"1: web", the above command will still switch to it. The command +workspace 1+
will however create and move to a new workspace "1" alongside the existing
"1: mail" workspace.
=== Moving workspaces to a different screen
@@ -2390,16 +2761,19 @@ To move a container to another RandR output (addressed by names like +LVDS1+ or
+right+, +up+ or +down+), there are two commands:
*Syntax*:
------------------------------------------------------------
move container to output left|right|down|up|current|primary|<output>
move workspace to output left|right|down|up|current|primary|<output>
------------------------------------------------------------
-------------------------------------------------------------------------------------
move container to output left|right|down|up|current|primary|nonprimary|next|<output1> [output2]…
move workspace to output left|right|down|up|current|primary|nonprimary|next|<output1> [output2]…
-------------------------------------------------------------------------------------
*Examples*:
--------------------------------------------------------
# Move the current workspace to the next output
# (effectively toggles when you only have two outputs)
bindsym $mod+x move workspace to output right
bindsym $mod+x move workspace to output next
# Cycle this workspace between outputs VGA1 and LVDS1 but not DVI0
bindsym $mod+x move workspace to output VGA1 LVDS1
# Put this window on the presentation output.
bindsym $mod+x move container to output VGA1
@@ -2408,11 +2782,17 @@ bindsym $mod+x move container to output VGA1
bindsym $mod+x move container to output primary
--------------------------------------------------------
If you specify more than one output, the container/workspace is cycled through
them: If it is already in one of the outputs of the list, it will move to the
next output in the list. If it is in an output not in the list, it will move to
the first specified output. Non-existing outputs are skipped.
Note that you might not have a primary output configured yet. To do so, run:
-------------------------
xrandr --output <output> --primary
-------------------------
[[move_to_mark]]
=== Moving containers/windows to marks
To move a container to another container with a specific mark (see <<vim_like_marks>>),
@@ -2558,9 +2938,8 @@ unmark irssi
By default, i3 will simply print the X11 window title. Using +title_format+,
this can be customized by setting the format to the desired output. This
directive supports
https://developer.gnome.org/pango/stable/pango-Markup.html[Pango markup]
and the following placeholders which will be replaced:
directive supports https://developer.gnome.org/pango/1.46/[Pango markup] and the
following placeholders which will be replaced:
+%title+::
For normal windows, this is the X11 window title (_NET_WM_NAME or WM_NAME
@@ -2573,6 +2952,9 @@ and the following placeholders which will be replaced:
+%instance+::
The X11 window instance (first part of WM_CLASS). This corresponds to the
+instance+ criterion, see <<command_criteria>>.
+%machine+::
The X11 name of the machine (WM_CLIENT_MACHINE). This corresponds to the
+machine+ criterion, see <<command_criteria>>.
Using the <<for_window>> directive, you can set the title format for any window
based on <<command_criteria>>.
@@ -2594,6 +2976,32 @@ for_window [class=".*"] title_format "<b>%title</b>"
for_window [class="(?i)firefox"] title_format "<span foreground='red'>%title</span>"
-------------------------------------------------------------------------------------
[[title_window_icon]]
=== Window title icon
By default, i3 does not display the window icon in the title bar.
Starting with i3 v4.20, you can optionally enable window icons either for
specific windows or for all windows (using the <<for_window>> directive).
*Syntax*:
----------------------------------------
title_window_icon <yes|no|toggle>
title_window_icon <padding|toggle> <px>
----------------------------------------
*Examples*:
-------------------------------------------------------------------------------------
# show the window icon for the focused window to make it stand out
bindsym $mod+p title_window_icon on
# enable window icons for all windows
for_window [all] title_window_icon on
# enable window icons for all windows with extra horizontal padding
for_window [all] title_window_icon padding 3px
-------------------------------------------------------------------------------------
=== Changing border style
To change the border of the current client, you can use +border normal+ to use the normal
@@ -2623,10 +3031,16 @@ border 1pixel
bindsym $mod+t border normal 0
# use no window title and a thick border
bindsym $mod+y border pixel 3
# use window title *and* a thick border
bindsym $mod+y border normal 3
# use neither window title nor border
bindsym $mod+u border none
# no border on VLC
for_window [class="vlc"] border none
----------------------------------------------
To change the default for all windows, see the directive <<default_border>>.
[[shmlog]]
=== Enabling shared memory logging
@@ -2775,6 +3189,35 @@ bindsym $mod+b bar mode hide bar-1
bindsym $mod+Shift+b bar mode invisible bar-1
------------------------------------------------
[[changing_gaps]]
=== Changing gaps
Gaps can be modified at runtime with the following command syntax:
*Syntax*:
-----------------------------------------------
# Inner gaps: space between two adjacent windows (or split containers).
gaps inner current|all set|plus|minus|toggle <gap_size_in_px>
# Outer gaps: space along the screen edges.
gaps outer|horizontal|vertical|top|right|bottom|left current|all set|plus|minus|toggle <gap_size_in_px>
-----------------------------------------------
With `current` or `all` you can change gaps either for only the currently
focused or all currently existing workspaces (note that this does not affect the
global configuration itself).
*Examples*:
----------------------------------------------
gaps inner all set 20
gaps outer current plus 5
gaps horizontal current plus 40
gaps outer current toggle 60
for_window [class="vlc"] gaps inner 0, gaps outer 0
----------------------------------------------
To change the gaps for all windows, see the <<gaps>> configuration directive.
[[multi_monitor]]
== Multiple monitors
@@ -2808,7 +3251,8 @@ To help you get going if you have never used multiple monitors before, here is
a short overview of the xrandr options which will probably be of interest to
you. It is always useful to get an overview of the current screen configuration.
Just run "xrandr" and you will get an output like the following:
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
$ xrandr
Screen 0: minimum 320 x 200, current 1280 x 800, maximum 8192 x 8192
VGA1 disconnected (normal left inverted right x axis y axis)
@@ -2821,7 +3265,7 @@ LVDS1 connected 1280x800+0+0 (normal left inverted right x axis y axis) 261mm x
720x400 85.0
640x400 85.1
640x350 85.1
--------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
Several things are important here: You can see that +LVDS1+ is connected (of
course, it is the internal flat panel) but +VGA1+ is not. If you have a monitor
@@ -2833,12 +3277,15 @@ combined resolution of your monitors. By default, it is usually too low and has
to be increased by editing +/etc/X11/xorg.conf+.
So, say you connected VGA1 and want to use it as an additional screen:
-------------------------------------------
xrandr --output VGA1 --auto --left-of LVDS1
-------------------------------------------
This command makes xrandr try to find the native resolution of the device
connected to +VGA1+ and configures it to the left of your internal flat panel.
When running "xrandr" again, the output looks like this:
-------------------------------------------------------------------------------
$ xrandr
Screen 0: minimum 320 x 200, current 2560 x 1024, maximum 8192 x 8192
@@ -2861,6 +3308,7 @@ LVDS1 connected 1280x800+1280+0 (normal left inverted right x axis y axis) 261mm
640x400 85.1
640x350 85.1
-------------------------------------------------------------------------------
Please note that i3 uses exactly the same API as xrandr does, so it will see
only what you can see in xrandr.

View File

@@ -17,6 +17,10 @@ font pango:monospace 8
# text rendering and scalability on retina/hidpi displays (thanks to pango).
#font pango:DejaVu Sans Mono 8
# Start XDG autostart .desktop files using dex. See also
# https://wiki.archlinux.org/index.php/XDG_Autostart
exec --no-startup-id dex --autostart --environment i3
# The combination of xss-lock, nm-applet and pactl is a popular choice, so
# they are included here as an example. Modify as you see fit.
@@ -45,6 +49,10 @@ set $right semicolon
# use Mouse+Mod1 to drag floating windows to their wanted position
floating_modifier Mod1
# move tiling windows via drag & drop by left-clicking into the title bar,
# or left-clicking anywhere into the window while holding the floating modifier.
tiling_drag modifier titlebar
# start a terminal
bindsym Mod1+Return exec i3-sensible-terminal

View File

@@ -18,6 +18,10 @@ font pango:monospace 8
# text rendering and scalability on retina/hidpi displays (thanks to pango).
#font pango:DejaVu Sans Mono 8
# Start XDG autostart .desktop files using dex. See also
# https://wiki.archlinux.org/index.php/XDG_Autostart
exec --no-startup-id dex --autostart --environment i3
# The combination of xss-lock, nm-applet and pactl is a popular choice, so
# they are included here as an example. Modify as you see fit.
@@ -39,6 +43,10 @@ bindsym XF86AudioMicMute exec --no-startup-id pactl set-source-mute @DEFAULT_SOU
# Use Mouse+$mod to drag floating windows to their wanted position
floating_modifier $mod
# move tiling windows via drag & drop by left-clicking into the title bar,
# or left-clicking anywhere into the window while holding the floating modifier.
tiling_drag modifier titlebar
# start a terminal
bindcode $mod+36 exec i3-sensible-terminal

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env perl
# vim:ts=4:sw=4:expandtab
#
# i3 - an improved dynamic tiling window manager
# i3 - an improved tiling window manager
# © 2009 Michael Stapelberg and contributors (see also: LICENSE)
#
# generate-command-parser.pl: script to generate parts of the command parser
@@ -133,7 +133,7 @@ close($enumfh);
open(my $callfh, '>', "GENERATED_${prefix}_call.h");
my $resultname = uc(substr($prefix, 0, 1)) . substr($prefix, 1) . 'ResultIR';
say $callfh '#pragma once';
say $callfh "static void GENERATED_call(const int call_identifier, struct $resultname *result) {";
say $callfh "static void GENERATED_call(Match *current_match, struct stack *stack, const int call_identifier, struct $resultname *result) {";
say $callfh ' switch (call_identifier) {';
my $call_id = 0;
for my $state (@keys) {
@@ -150,8 +150,8 @@ for my $state (@keys) {
# calls to get_string(). Also replaces state names (like FOR_WINDOW)
# with their ID (useful for cfg_criteria_init(FOR_WINDOW) e.g.).
$cmd =~ s/$_/$statenum{$_}/g for @keys;
$cmd =~ s/\$([a-z_]+)/get_string("$1")/g;
$cmd =~ s/\&([a-z_]+)/get_long("$1")/g;
$cmd =~ s/\$([a-z_]+)/get_string(stack, "$1")/g;
$cmd =~ s/\&([a-z_]+)/get_long(stack, "$1")/g;
# For debugging/testing, we print the call using printf() and thus need
# to generate a format string. The format uses %d for <number>s,
# literal numbers or state IDs and %s for NULL, <string>s and literal
@@ -175,9 +175,9 @@ for my $state (@keys) {
say $callfh '#ifndef TEST_PARSER';
my $real_cmd = $cmd;
if ($real_cmd =~ /\(\)/) {
$real_cmd =~ s/\(/(&current_match, result/;
$real_cmd =~ s/\(/(current_match, result/;
} else {
$real_cmd =~ s/\(/(&current_match, result, /;
$real_cmd =~ s/\(/(current_match, result, /;
}
say $callfh " $real_cmd;";
say $callfh '#else';
@@ -228,8 +228,15 @@ for my $state (@keys) {
($call_identifier) = ($next_state =~ /^call ([0-9]+)$/);
$next_state = '__CALL';
}
my $identifier = $token->{identifier};
say $tokfh qq| { "$token_name", "$identifier", $next_state, { $call_identifier } },|;
my $identifier;
# Set $identifier to NULL if there is no identifier
if ($token->{identifier} eq ""){
$identifier = "NULL"
}
else{
$identifier = qq|"$token->{identifier}"|;
}
say $tokfh qq| { "$token_name", $identifier, $next_state, { $call_identifier } },|;
}
say $tokfh '};';
}

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-config-wizard: Program to convert configs using keycodes to configs using
@@ -157,8 +157,9 @@ static struct stack_entry stack[10];
static void push_string(const char *identifier, const char *str) {
for (int c = 0; c < 10; c++) {
if (stack[c].identifier != NULL &&
strcmp(stack[c].identifier, identifier) != 0)
strcmp(stack[c].identifier, identifier) != 0) {
continue;
}
if (stack[c].identifier == NULL) {
/* Found a free slot, lets store it here. */
stack[c].identifier = identifier;
@@ -184,8 +185,9 @@ static void push_string(const char *identifier, const char *str) {
static void push_long(const char *identifier, long num) {
for (int c = 0; c < 10; c++) {
if (stack[c].identifier != NULL)
if (stack[c].identifier != NULL) {
continue;
}
/* Found a free slot, lets store it here. */
stack[c].identifier = identifier;
stack[c].val.num = num;
@@ -204,18 +206,21 @@ static void push_long(const char *identifier, long num) {
static const char *get_string(const char *identifier) {
for (int c = 0; c < 10; c++) {
if (stack[c].identifier == NULL)
if (stack[c].identifier == NULL) {
break;
if (strcmp(identifier, stack[c].identifier) == 0)
}
if (strcmp(identifier, stack[c].identifier) == 0) {
return stack[c].val.str;
}
}
return NULL;
}
static void clear_stack(void) {
for (int c = 0; c < 10; c++) {
if (stack[c].type == STACK_STR)
if (stack[c].type == STACK_STR) {
free(stack[c].val.str);
}
stack[c].identifier = NULL;
stack[c].val.str = NULL;
stack[c].val.num = 0;
@@ -233,11 +238,13 @@ static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) {
max_keycode = xcb_get_setup(conn)->max_keycode;
for (i = min_keycode; i && i <= max_keycode; i++) {
if (i == except_keycode)
if (i == except_keycode) {
continue;
}
for (int level = 0; level < 4; level++) {
if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym)
if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym) {
continue;
}
return true;
}
}
@@ -269,22 +276,27 @@ static char *next_state(const cmdp_token *token) {
* qwerty (yes, that happens quite often). */
const xkb_keysym_t *syms;
int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, 0, &syms);
if (num == 0)
if (num == 0) {
errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
if (!keysym_used_on_other_key(syms[0], keycode))
}
if (!keysym_used_on_other_key(syms[0], keycode)) {
level = 0;
}
}
const xkb_keysym_t *syms;
int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, level, &syms);
if (num == 0)
if (num == 0) {
errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
if (num > 1)
}
if (num > 1) {
printf("xkb_keymap_key_get_syms_by_level (keycode = %d) returned %d symbolsinstead of 1, using only the first one.\n", keycode, num);
}
char str[4096];
if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1)
if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1) {
errx(EXIT_FAILURE, "xkb_keysym_get_name(%u) failed", syms[0]);
}
const char *release = get_string("release");
char *res;
char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers));
@@ -303,8 +315,9 @@ static char *next_state(const cmdp_token *token) {
/* See if we are jumping back to a state in which we were in previously
* (statelist contains INITIAL) and just move statelist_idx accordingly. */
for (int i = 0; i < statelist_idx; i++) {
if (statelist[i] != _next_state)
if (statelist[i] != _next_state) {
continue;
}
statelist_idx = i + 1;
return NULL;
}
@@ -329,10 +342,9 @@ static char *rewrite_binding(const char *input) {
while ((size_t)(walk - input) <= len) {
/* Skip whitespace before every token, newlines are relevant since they
* separate configuration directives. */
while ((*walk == ' ' || *walk == '\t') && *walk != '\0')
while ((*walk == ' ' || *walk == '\t') && *walk != '\0') {
walk++;
//printf("remaining input: %s\n", walk);
}
cmdp_token_ptr *ptr = &(tokens[state]);
for (c = 0; c < ptr->n; c++) {
@@ -341,11 +353,13 @@ static char *rewrite_binding(const char *input) {
/* A literal. */
if (token->name[0] == '\'') {
if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
if (token->identifier != NULL)
if (token->identifier != NULL) {
push_string(token->identifier, token->name + 1);
}
walk += strlen(token->name) - 1;
if ((result = next_state(token)) != NULL)
if ((result = next_state(token)) != NULL) {
return result;
}
break;
}
continue;
@@ -357,20 +371,24 @@ static char *rewrite_binding(const char *input) {
errno = 0;
long int num = strtol(walk, &end, 10);
if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) ||
(errno != 0 && num == 0))
(errno != 0 && num == 0)) {
continue;
}
/* No valid numbers found */
if (end == walk)
if (end == walk) {
continue;
}
if (token->identifier != NULL)
if (token->identifier != NULL) {
push_long(token->identifier, num);
}
/* Set walk to the first non-number character */
walk = end;
if ((result = next_state(token)) != NULL)
if ((result = next_state(token)) != NULL) {
return result;
}
break;
}
@@ -381,12 +399,14 @@ static char *rewrite_binding(const char *input) {
if (*walk == '"') {
beginning++;
walk++;
while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\'))
while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\')) {
walk++;
}
} else {
if (token->name[0] == 's') {
while (*walk != '\0' && *walk != '\r' && *walk != '\n')
while (*walk != '\0' && *walk != '\r' && *walk != '\n') {
walk++;
}
} else {
/* For a word, the delimiters are white space (' ' or
* '\t'), closing square bracket (]), comma (,) and
@@ -394,8 +414,9 @@ static char *rewrite_binding(const char *input) {
while (*walk != ' ' && *walk != '\t' &&
*walk != ']' && *walk != ',' &&
*walk != ';' && *walk != '\r' &&
*walk != '\n' && *walk != '\0')
*walk != '\n' && *walk != '\0') {
walk++;
}
}
}
if (walk != beginning) {
@@ -408,33 +429,36 @@ static char *rewrite_binding(const char *input) {
/* We only handle escaped double quotes to not break
* backwards compatibility with people using \w in
* regular expressions etc. */
if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"') {
inpos++;
}
str[outpos] = beginning[inpos];
}
if (token->identifier)
if (token->identifier) {
push_string(token->identifier, str);
}
free(str);
/* If we are at the end of a quoted string, skip the ending
* double quote. */
if (*walk == '"')
if (*walk == '"') {
walk++;
if ((result = next_state(token)) != NULL)
}
if ((result = next_state(token)) != NULL) {
return result;
}
break;
}
}
if (strcmp(token->name, "end") == 0) {
//printf("checking for end: *%s*\n", walk);
if (*walk == '\0' || *walk == '\n' || *walk == '\r') {
if ((result = next_state(token)) != NULL)
if ((result = next_state(token)) != NULL) {
return result;
/* To make sure we start with an appropriate matching
* datastructure for commands which do *not* specify any
}
/* To make sure we start with an appropriate matching data
* structure for commands which do *not* specify any
* criteria, we re-initialize the criteria system after
* every command. */
// TODO: make this testable
walk++;
break;
}
@@ -517,17 +541,19 @@ static int handle_expose(void) {
txt(13, 10, "to abort", white, black);
/* the not-selected modifier */
if (modifier == MOD_Mod4)
if (modifier == MOD_Mod4) {
txt(5, 5, "<Alt>", white, black);
else
} else {
txt(5, 4, "<Win>", white, black);
}
/* the selected modifier */
set_font(&bold_font);
if (modifier == MOD_Mod4)
if (modifier == MOD_Mod4) {
txt(2, 4, "-> <Win>", white, black);
else
} else {
txt(2, 5, "-> <Alt>", white, black);
}
set_font(&font);
txt(4, 9, "<Enter>", green, black);
@@ -566,8 +592,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
strlen("i3: generate config"),
"i3: generate config");
xcb_flush(conn);
} else
} else {
finish();
}
}
/* Swap between modifiers when up or down is pressed. */
@@ -577,8 +604,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
}
/* cancel any time */
if (sym == XK_Escape)
if (sym == XK_Escape) {
exit(0);
}
/* Check if this is Mod1 or Mod4. The modmap contains Shift, Lock, Control,
* Mod1, Mod2, Mod3, Mod4, Mod5 (in that order) */
@@ -587,8 +615,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
int mask = 3;
for (int i = 0; i < modmap_reply->keycodes_per_modifier; i++) {
xcb_keycode_t code = modmap[(mask * modmap_reply->keycodes_per_modifier) + i];
if (code == XCB_NONE)
if (code == XCB_NONE) {
continue;
}
printf("Modifier keycode for Mod1: 0x%02x\n", code);
if (code == event->detail) {
modifier = MOD_Mod1;
@@ -600,8 +629,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
mask = 6;
for (int i = 0; i < modmap_reply->keycodes_per_modifier; i++) {
xcb_keycode_t code = modmap[(mask * modmap_reply->keycodes_per_modifier) + i];
if (code == XCB_NONE)
if (code == XCB_NONE) {
continue;
}
printf("Modifier keycode for Mod4: 0x%02x\n", code);
if (code == event->detail) {
modifier = MOD_Mod4;
@@ -618,11 +648,13 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
*
*/
static void handle_button_press(xcb_button_press_event_t *event) {
if (current_step != STEP_GENERATE)
if (current_step != STEP_GENERATE) {
return;
}
if (event->event_x < col_x(5) || event->event_x > col_x(10))
if (event->event_x < col_x(5) || event->event_x > col_x(10)) {
return;
}
if (event->event_y >= row_y(4) && event->event_y <= (row_y(4) + font.height)) {
modifier = MOD_Mod4;
@@ -644,20 +676,24 @@ static void finish(void) {
struct xkb_context *xkb_context;
if ((xkb_context = xkb_context_new(0)) == NULL)
if ((xkb_context = xkb_context_new(0)) == NULL) {
errx(1, "could not create xkbcommon context");
}
int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn);
if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL)
if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL) {
errx(1, "xkb_x11_keymap_new_from_device failed");
}
FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
if (kc_config == NULL)
if (kc_config == NULL) {
err(1, "Could not open input file \"%s\"", SYSCONFDIR "/i3/config.keycodes");
}
FILE *ks_config = fopen(config_path, "w");
if (ks_config == NULL)
if (ks_config == NULL) {
err(1, "Could not open output config file \"%s\"", config_path);
}
free(config_path);
char *line = NULL;
@@ -688,8 +724,9 @@ static void finish(void) {
#endif
/* skip the warning block at the beginning of the input file */
if (head_of_file &&
strncmp("# WARNING", line, strlen("# WARNING")) == 0)
strncmp("# WARNING", line, strlen("# WARNING")) == 0) {
continue;
}
head_of_file = false;
@@ -703,10 +740,11 @@ static void finish(void) {
/* Set the modifier the user chose */
if (strncmp(walk, "set $mod ", strlen("set $mod ")) == 0) {
if (modifier == MOD_Mod1)
if (modifier == MOD_Mod1) {
fputs("set $mod Mod1\n", ks_config);
else
} else {
fputs("set $mod Mod4\n", ks_config);
}
continue;
}
@@ -771,12 +809,13 @@ int main(int argc, char *argv[]) {
return 0;
case 'm':
headless_run = true;
if (strcmp(optarg, "alt") == 0)
if (strcmp(optarg, "alt") == 0) {
modifier = MOD_Mod1;
else if (strcmp(optarg, "win") == 0)
} else if (strcmp(optarg, "win") == 0) {
modifier = MOD_Mod4;
else
} else {
err(EXIT_FAILURE, "Invalid modifier key %s", optarg);
}
break;
case 'h':
printf("i3-config-wizard " I3_VERSION "\n");
@@ -793,8 +832,9 @@ int main(int argc, char *argv[]) {
}
/* Always write to $XDG_CONFIG_HOME/i3/config by default. */
if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) {
xdg_config_home = "~/.config";
}
xdg_config_home = resolve_tilde(xdg_config_home);
sasprintf(&config_path, "%s/i3/config", xdg_config_home);
@@ -803,9 +843,11 @@ int main(int argc, char *argv[]) {
char *config_dir;
struct stat stbuf;
sasprintf(&config_dir, "%s/i3", xdg_config_home);
if (stat(config_dir, &stbuf) != 0)
if (mkdirp(config_dir, DEFAULT_DIR_MODE) != 0)
if (stat(config_dir, &stbuf) != 0) {
if (mkdirp(config_dir, DEFAULT_DIR_MODE) != 0) {
err(EXIT_FAILURE, "mkdirp(%s) failed", config_dir);
}
}
free(config_dir);
free(xdg_config_home);
@@ -819,8 +861,9 @@ int main(int argc, char *argv[]) {
int screen;
if ((conn = xcb_connect(NULL, &screen)) == NULL ||
xcb_connection_has_error(conn))
xcb_connection_has_error(conn)) {
errx(1, "Cannot open display");
}
if (xkb_x11_setup_xkb_extension(conn,
XKB_X11_MIN_MAJOR_XKB_VERSION,
@@ -829,8 +872,9 @@ int main(int argc, char *argv[]) {
NULL,
NULL,
&xkb_base_event,
&xkb_base_error) != 1)
&xkb_base_error) != 1) {
errx(EXIT_FAILURE, "Could not setup XKB extension.");
}
keysyms = xcb_key_symbols_alloc(conn);
xcb_get_modifier_mapping_cookie_t modmap_cookie;
@@ -856,8 +900,9 @@ int main(int argc, char *argv[]) {
root_screen = xcb_aux_get_screen(conn, screen);
root = root_screen->root;
if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL)))
if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL))) {
errx(EXIT_FAILURE, "Could not get modifier mapping");
}
xcb_numlock_mask = get_mod_mask_for(XCB_NUM_LOCK, symbols, modmap_reply);

View File

@@ -46,9 +46,11 @@ sub slurp {
my @entry_types;
my $dmenu_cmd = 'dmenu -i';
my $show_duplicates;
my $result = GetOptions(
'dmenu=s' => \$dmenu_cmd,
'entry-type=s' => \@entry_types,
'show-duplicates' => \$show_duplicates,
'version' => sub {
say "dmenu-desktop 1.5 © 2012 Michael Stapelberg";
exit 0;
@@ -154,6 +156,8 @@ find(
},
no_chdir => 1,
follow_fast => 1,
# Ignore any duplicate files and directories and proceed normally:
follow_skip => 2,
},
@searchdirs
);
@@ -290,7 +294,7 @@ for my $app (keys %apps) {
}
$choices{$name} = $app;
next;
next unless $show_duplicates;
}
if ((scalar grep { $_ eq 'command' } @entry_types) > 0) {
@@ -325,7 +329,7 @@ for my $app (keys %apps) {
if (!(scalar grep { $_ eq lc(basename($command)) } @keys) > 0) {
$choices{basename($command)} = $app;
}
next;
next unless $show_duplicates;
}
if ((scalar grep { $_ eq 'filename' } @entry_types) > 0) {
@@ -364,9 +368,14 @@ binmode $dmenu_in, ':utf8';
binmode $dmenu_out, ':utf8';
# Feed dmenu the possible choices.
# Since the process might have already exited, we ignore SIGPIPE.
$SIG{PIPE} = 'IGNORE';
say $dmenu_in $_ for sort keys %choices;
close($dmenu_in);
$SIG{PIPE} = 'DEFAULT';
waitpid($pid, 0);
my $status = ($? >> 8);
@@ -411,7 +420,7 @@ my $exec = $app->{Exec};
my $location = $app->{_Location};
# Quote as described by “The Exec key”:
# https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
# https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s07.html
sub quote {
my ($str) = @_;
$str =~ s/("|`|\$|\\)/\\$1/g;
@@ -423,6 +432,17 @@ $choice = quote($choice);
$location = quote($location);
$name = quote($name);
# https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s07.html:
#
# Note that the general escape rule for values of type string states that the
# backslash character can be escaped as ("\\") as well and that this escape rule
# is applied before the quoting rule. As such, to unambiguously represent a
# literal backslash character in a quoted argument in a desktop entry file
# requires the use of four successive backslash characters ("\\\\"). Likewise, a
# literal dollar sign in a quoted argument in a desktop entry file is
# unambiguously represented with ("\\$").
$exec =~ s/\\\\/\\/g;
# Remove deprecated field codes, as the spec dictates.
$exec =~ s/%[dDnNvm]//g;
@@ -479,8 +499,10 @@ EOT
# starts with a double quote ("), everything is parsed as-is until the next
# double quote which is NOT preceded by a backslash (\).
#
# Therefore, we escape all double quotes (") by replacing them with \"
$exec =~ s/"/\\"/g;
# Therefore, we escape all double quotes (") by replacing them with \".
# To not change the meaning of any double quote, backslashes need to be
# escaped as well.
$exec =~ s/(["\\])/\\$1/g;
if (exists($app->{StartupNotify}) && !$app->{StartupNotify}) {
$nosn = '--no-startup-id';
@@ -498,7 +520,7 @@ system('i3-msg', $cmd) == 0 or die "Could not launch i3-msg: $?";
=head1 SYNOPSIS
i3-dmenu-desktop [--dmenu='dmenu -i'] [--entry-type=name]
i3-dmenu-desktop [--dmenu='dmenu -i'] [--entry-type=name] [--show-duplicates]
=head1 DESCRIPTION
@@ -549,11 +571,15 @@ version of dmenu.
Display the (localized) "Name" (type = name), the command (type = command) or
the (*.desktop) filename (type = filename) in dmenu. This option can be
specified multiple times.
specified multiple times. Duplicates will be removed, see '--show-duplicates'.
Examples are "GNU Image Manipulation Program" (type = name), "gimp" (type =
command), and "libreoffice-writer" (type = filename).
=item B<--show-duplicates>
Show duplicates if '--entry-type' is specified multiple times.
=back
=head1 VERSION

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-dump-log/main.c: Dumps the i3 SHM log to stdout.
@@ -25,10 +25,10 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#if !defined(__OpenBSD__)
static uint32_t offset_next_write;
#endif
static uint32_t wrap_count;
static i3_shmlog_header *header;
@@ -36,17 +36,12 @@ static char *logbuffer,
*walk;
static int ipcfd = -1;
static volatile bool interrupted = false;
static void sighandler(int signal) {
interrupted = true;
}
static void disable_shmlog(void) {
const char *disablecmd = "debuglog off; shmlog off";
if (ipc_send_message(ipcfd, strlen(disablecmd),
I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)disablecmd) != 0)
I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)disablecmd) != 0) {
err(EXIT_FAILURE, "IPC send");
}
/* Ensure the command was sent by waiting for the reply: */
uint32_t reply_length = 0;
@@ -59,8 +54,9 @@ static void disable_shmlog(void) {
}
static int check_for_wrap(void) {
if (wrap_count == header->wrap_count)
if (wrap_count == header->wrap_count) {
return 0;
}
/* The log wrapped. Print the remaining content and reset walk to the top
* of the log. */
@@ -154,8 +150,9 @@ int main(int argc, char *argv[]) {
ipcfd = ipc_connect(NULL);
const char *enablecmd = "debuglog on; shmlog 5242880";
if (ipc_send_message(ipcfd, strlen(enablecmd),
I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)enablecmd) != 0)
I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)enablecmd) != 0) {
err(EXIT_FAILURE, "IPC send");
}
/* By the time we receive a reply, I3_SHMLOG_PATH is set: */
uint32_t reply_length = 0;
uint8_t *reply = NULL;
@@ -181,29 +178,33 @@ int main(int argc, char *argv[]) {
}
}
if (*shmname == '\0')
if (*shmname == '\0') {
errx(EXIT_FAILURE, "Cannot dump log: SHM logging is disabled in i3.");
}
struct stat statbuf;
/* NB: While we must never write, we need O_RDWR for the pthread condvar. */
int logbuffer_shm = shm_open(shmname, O_RDWR, 0);
if (logbuffer_shm == -1)
if (logbuffer_shm == -1) {
err(EXIT_FAILURE, "Could not shm_open SHM segment for the i3 log (%s)", shmname);
}
if (fstat(logbuffer_shm, &statbuf) != 0)
if (fstat(logbuffer_shm, &statbuf) != 0) {
err(EXIT_FAILURE, "stat(%s)", shmname);
}
/* NB: While we must never write, we need PROT_WRITE for the pthread condvar. */
logbuffer = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
if (logbuffer == MAP_FAILED)
logbuffer = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, logbuffer_shm, 0);
if (logbuffer == MAP_FAILED) {
err(EXIT_FAILURE, "Could not mmap SHM segment for the i3 log");
}
header = (i3_shmlog_header *)logbuffer;
if (verbose)
if (verbose) {
printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n",
header->offset_next_write, header->offset_last_wrap, header->size, shmname);
}
free(shmname);
walk = logbuffer + header->offset_next_write;
@@ -214,8 +215,9 @@ int main(int argc, char *argv[]) {
/* In case there was a write to the buffer already, skip the first
* old line, it very likely is mangled. Not a problem, though, the log
* is chatty enough to have plenty lines left. */
while (*walk != '\n')
while (*walk != '\n') {
walk++;
}
walk++;
}
@@ -233,25 +235,38 @@ int main(int argc, char *argv[]) {
return 0;
}
/* Handle SIGINT gracefully to invoke atexit handlers, if any. */
struct sigaction action;
action.sa_handler = sighandler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGINT, &action, NULL);
char *log_stream_socket_path = root_atom_contents("I3_LOG_STREAM_SOCKET_PATH", NULL, 0);
if (log_stream_socket_path == NULL) {
errx(EXIT_FAILURE, "could not determine i3 log stream socket path: possible i3-dump-log and i3 version mismatch");
}
/* Since pthread_cond_wait() expects a mutex, we need to provide one.
* To not lock i3 (thats bad, mhkay?) we just define one outside of
* the shared memory. */
pthread_mutex_t dummy_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&dummy_mutex);
while (!interrupted) {
pthread_cond_wait(&(header->condvar), &dummy_mutex);
/* If this was not a spurious wakeup, print the new lines. */
if (header->offset_next_write != offset_next_write) {
offset_next_write = header->offset_next_write;
print_till_end();
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (sockfd == -1) {
err(EXIT_FAILURE, "Could not create socket");
}
(void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
struct sockaddr_un addr;
memset(&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family = AF_LOCAL;
strncpy(addr.sun_path, log_stream_socket_path, sizeof(addr.sun_path) - 1);
if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
err(EXIT_FAILURE, "Could not connect to i3 on socket %s", log_stream_socket_path);
}
/* Same size as the buffer used in log.c vlog(): */
char buf[4096];
for (;;) {
const int n = read(sockfd, buf, sizeof(buf));
if (n == -1) {
err(EXIT_FAILURE, "read(log-stream-socket):");
}
if (n == 0) {
exit(0); /* i3 closed the socket */
}
buf[n] = '\0';
swrite(STDOUT_FILENO, buf, n);
}
#endif

View File

@@ -1,6 +1,6 @@
# This list can be used to convert X11 Keysyms to Unicode 2.1 character.
# The list is not checked for correctness by Unicode officials. Use it
# at your own risk and the creator is not responsable for any damage that
# at your own risk and the creator is not responsible for any damage that
# occurred due to using this list.
#
# The list is created by looking at the Keysym names and the Unicode data

View File

@@ -822,21 +822,23 @@ long keysym2ucs(xcb_keysym_t keysym) {
/* first check for Latin-1 characters (1:1 mapping) */
if ((keysym >= 0x0020 && keysym <= 0x007e) ||
(keysym >= 0x00a0 && keysym <= 0x00ff))
(keysym >= 0x00a0 && keysym <= 0x00ff)) {
return keysym;
}
/* also check for directly encoded 24-bit UCS characters */
if ((keysym & 0xff000000) == 0x01000000)
if ((keysym & 0xff000000) == 0x01000000) {
return keysym & 0x00ffffff;
}
/* binary search in table */
while (max >= min) {
mid = (min + max) / 2;
if (keysymtab[mid].keysym < keysym)
if (keysymtab[mid].keysym < keysym) {
min = mid + 1;
else if (keysymtab[mid].keysym > keysym)
} else if (keysymtab[mid].keysym > keysym) {
max = mid - 1;
else {
} else {
/* found it */
return keysymtab[mid].ucs;
}

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-input/main.c: Utility which lets the user input commands and sends them
@@ -163,29 +163,32 @@ static void finish_input(void) {
char *command = (char *)concat_strings(glyphs_utf8, input_position);
/* count the occurrences of %s in the string */
int c;
int len = strlen(format);
int cnt = 0;
for (c = 0; c < (len - 1); c++)
if (format[c] == '%' && format[c + 1] == 's')
const size_t len = strlen(format);
size_t cnt = 0;
for (size_t c = 0; c < (len - 1); c++) {
if (format[c] == '%' && format[c + 1] == 's') {
cnt++;
printf("occurrences = %d\n", cnt);
}
}
printf("occurrences = %zu\n", cnt);
/* allocate space for the output */
int inputlen = strlen(command);
char *full = scalloc(strlen(format) - (2 * cnt) /* format without all %s */
+ (inputlen * cnt) /* replaced %s */
+ 1, /* trailing NUL */
1);
const size_t input_len = strlen(command);
const size_t full_len = MAX(input_len, /* avoid compiler warning */
strlen(format) - (2 * cnt) /* format without all %s */
+ (input_len * cnt) /* replaced %s */
+ 1 /* trailing NUL */
);
char *full = scalloc(full_len, 1);
char *dest = full;
for (c = 0; c < len; c++) {
/* if this is not % or it is % but without a following 's',
* just copy the character */
if (format[c] != '%' || (c == (len - 1)) || format[c + 1] != 's')
for (size_t c = 0; c < len; c++) {
/* if this is not % or it is % but without a following 's', just copy
* the character */
if (format[c] != '%' || (c == (len - 1)) || format[c + 1] != 's') {
*(dest++) = format[c];
else {
strncat(dest, command, inputlen);
dest += inputlen;
} else {
strncat(dest, command, input_len);
dest += input_len;
/* skip the following 's' of '%s' */
c++;
}
@@ -223,8 +226,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
/* If modeswitch is currently active, we need to look in group 2 or 3,
* respectively. */
if (modeswitch_active)
if (modeswitch_active) {
col += 2;
}
xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, col);
if (sym == XK_Mode_switch) {
@@ -233,12 +237,14 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
return 1;
}
if (sym == XK_Return)
if (sym == XK_Return) {
finish_input();
}
if (sym == XK_BackSpace) {
if (input_position == 0)
if (input_position == 0) {
return 1;
}
input_position--;
free(glyphs_utf8[input_position]);
@@ -259,8 +265,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
printf("xcb_is_misc_function_key = %d\n", xcb_is_misc_function_key(sym));
printf("xcb_is_modifier_key = %d\n", xcb_is_modifier_key(sym));
if (xcb_is_modifier_key(sym) || xcb_is_cursor_key(sym))
if (xcb_is_modifier_key(sym) || xcb_is_cursor_key(sym)) {
return 1;
}
printf("sym = %c (%d)\n", sym, sym);
@@ -284,8 +291,9 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
glyphs_utf8[input_position] = out;
input_position++;
if (input_position == limit)
if (input_position == limit) {
finish_input();
}
handle_expose(NULL, conn, NULL);
return 1;
@@ -399,7 +407,7 @@ int main(int argc, char *argv[]) {
socket_path = sstrdup(optarg);
break;
case 'v':
printf("i3-input " I3_VERSION);
printf("i3-input " I3_VERSION "\n");
return EXIT_OK;
case 'p':
/* This option is deprecated, but will still work in i3 v4.1, 4.2 and 4.3 */
@@ -439,8 +447,9 @@ int main(int argc, char *argv[]) {
int screen;
conn = xcb_connect(NULL, &screen);
if (!conn || xcb_connection_has_error(conn))
if (!conn || xcb_connection_has_error(conn)) {
die("Cannot open display");
}
sockfd = ipc_connect(socket_path);
@@ -453,8 +462,9 @@ int main(int argc, char *argv[]) {
font = load_font(pattern ? pattern : "pango:monospace 8", true);
set_font(&font);
if (prompt != NULL)
if (prompt != NULL) {
prompt_offset = predict_text_width(prompt);
}
const xcb_rectangle_t win_pos = get_window_position();

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-msg/main.c: Utility which sends messages to a running i3-instance using
@@ -61,22 +61,24 @@ static int exit_code = 0;
static reply_t last_reply;
static int reply_boolean_cb(void *params, int val) {
if (strcmp(last_key, "success") == 0)
if (strcmp(last_key, "success") == 0) {
last_reply.success = val;
}
return 1;
}
static int reply_string_cb(void *params, const unsigned char *val, size_t len) {
char *str = sstrndup((const char *)val, len);
if (strcmp(last_key, "error") == 0)
if (strcmp(last_key, "error") == 0) {
last_reply.error = str;
else if (strcmp(last_key, "input") == 0)
} else if (strcmp(last_key, "input") == 0) {
last_reply.input = str;
else if (strcmp(last_key, "errorposition") == 0)
} else if (strcmp(last_key, "errorposition") == 0) {
last_reply.errorposition = str;
else
} else {
free(str);
}
return 1;
}
@@ -146,16 +148,13 @@ static yajl_callbacks config_callbacks = {
};
int main(int argc, char *argv[]) {
#if defined(__OpenBSD__)
if (pledge("stdio rpath unix", NULL) == -1)
err(EXIT_FAILURE, "pledge");
#endif
char *socket_path = NULL;
int o, option_index = 0;
uint32_t message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND;
char *payload = NULL;
bool quiet = false;
bool monitor = false;
bool raw_reply = false;
static struct option long_options[] = {
{"socket", required_argument, 0, 's'},
@@ -164,9 +163,10 @@ int main(int argc, char *argv[]) {
{"quiet", no_argument, 0, 'q'},
{"monitor", no_argument, 0, 'm'},
{"help", no_argument, 0, 'h'},
{"raw", no_argument, 0, 'r'},
{0, 0, 0, 0}};
char *options_string = "s:t:vhqm";
char *options_string = "s:t:vhqmr";
while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
if (o == 's') {
@@ -217,6 +217,8 @@ int main(int argc, char *argv[]) {
return 0;
} else if (o == '?') {
exit(EXIT_FAILURE);
} else if (o == 'r') {
raw_reply = true;
}
}
@@ -240,12 +242,14 @@ int main(int argc, char *argv[]) {
optind++;
}
if (!payload)
if (!payload) {
payload = sstrdup("");
}
int sockfd = ipc_connect(socket_path);
if (ipc_send_message(sockfd, strlen(payload), message_type, (uint8_t *)payload) == -1)
if (ipc_send_message(sockfd, strlen(payload), message_type, (uint8_t *)payload) == -1) {
err(EXIT_FAILURE, "IPC: write()");
}
free(payload);
uint32_t reply_length;
@@ -253,48 +257,57 @@ int main(int argc, char *argv[]) {
uint8_t *reply;
int ret;
if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
if (ret == -1)
if (ret == -1) {
err(EXIT_FAILURE, "IPC: read()");
}
exit(1);
}
if (reply_type != message_type)
if (reply_type != message_type) {
errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected %d", reply_type, message_type);
}
/* For the reply of commands, have a look if that command was successful.
* If not, nicely format the error message. */
if (reply_type == I3_IPC_REPLY_TYPE_COMMAND) {
yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL);
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
yajl_free(handle);
if (!raw_reply) {
yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL);
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
yajl_free(handle);
switch (state) {
case yajl_status_ok:
break;
case yajl_status_client_canceled:
case yajl_status_error:
errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
switch (state) {
case yajl_status_ok:
break;
case yajl_status_client_canceled:
case yajl_status_error:
errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
}
}
if (!quiet) {
if (!quiet || raw_reply) {
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);
yajl_free(handle);
if (raw_reply) {
printf("%.*s\n", reply_length, reply);
} else {
yajl_handle handle = yajl_alloc(&config_callbacks, NULL, NULL);
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
yajl_free(handle);
switch (state) {
case yajl_status_ok:
break;
case yajl_status_client_canceled:
case yajl_status_error:
errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
switch (state) {
case yajl_status_ok:
break;
case yajl_status_client_canceled:
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)
if (ret == -1) {
err(EXIT_FAILURE, "IPC: read()");
}
exit(1);
}

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-nagbar is a utility which displays a nag message, for example in the case
@@ -126,9 +126,11 @@ static void start_application(const char *command) {
}
static button_t *get_button_at(int16_t x, int16_t y) {
for (int c = 0; c < buttoncnt; c++)
if (x >= (buttons[c].x) && x <= (buttons[c].x + buttons[c].width))
for (int c = 0; c < buttoncnt; c++) {
if (x >= (buttons[c].x) && x <= (buttons[c].x + buttons[c].width)) {
return &buttons[c];
}
}
return NULL;
}
@@ -148,11 +150,13 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
printf("button released on x = %d, y = %d\n",
event->event_x, event->event_y);
/* If the user hits the close button, we exit(0) */
if (event->event_x >= btn_close.x && event->event_x < btn_close.x + btn_close.width)
if (event->event_x >= btn_close.x && event->event_x < btn_close.x + btn_close.width) {
exit(0);
}
button_t *button = get_button_at(event->event_x, event->event_y);
if (!button)
if (!button) {
return;
}
/* We need to create a custom script containing our actual command
* since not every terminal emulator which is contained in
@@ -266,13 +270,9 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
}
/**
* Return the position and size the i3-nagbar window should use.
* This will be the primary output or a fallback if it cannot be determined.
* Tries to position the rectangle on the primary output.
*/
static xcb_rectangle_t get_window_position(void) {
/* Default values if we cannot determine the primary output or its CRTC info. */
xcb_rectangle_t result = (xcb_rectangle_t){50, 50, 500, font.height + 2 * MSG_PADDING + BAR_BORDER};
static void set_window_position_primary(xcb_rectangle_t *result) {
xcb_randr_get_screen_resources_current_cookie_t rcookie = xcb_randr_get_screen_resources_current(conn, root);
xcb_randr_get_output_primary_cookie_t pcookie = xcb_randr_get_output_primary(conn, root);
@@ -314,14 +314,61 @@ static xcb_rectangle_t get_window_position(void) {
goto free_resources;
}
result.x = crtc->x;
result.y = crtc->y;
result->x = crtc->x;
result->y = crtc->y;
goto free_resources;
free_resources:
free(res);
free(primary);
return result;
}
/**
* Tries to position the rectangle on the output with input focus.
* If unsuccessful, try to position on primary output.
*/
static void set_window_position_focus(xcb_rectangle_t *result) {
bool success = false;
xcb_get_input_focus_reply_t *input_focus = NULL;
xcb_get_geometry_reply_t *geometry = NULL;
xcb_translate_coordinates_reply_t *coordinates = NULL;
/* To avoid the input window disappearing while determining its position */
xcb_grab_server(conn);
input_focus = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL);
if (input_focus == NULL || input_focus->focus == XCB_NONE) {
LOG("Failed to receive the current input focus or no window has the input focus right now.\n");
goto free_resources;
}
geometry = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, input_focus->focus), NULL);
if (geometry == NULL) {
LOG("Failed to received window geometry.\n");
goto free_resources;
}
coordinates = xcb_translate_coordinates_reply(
conn, xcb_translate_coordinates(conn, input_focus->focus, root, geometry->x, geometry->y), NULL);
if (coordinates == NULL) {
LOG("Failed to translate coordinates.\n");
goto free_resources;
}
LOG("Found current focus at x = %i / y = %i.\n", coordinates->dst_x, coordinates->dst_y);
result->x = coordinates->dst_x;
result->y = coordinates->dst_y;
success = true;
free_resources:
xcb_ungrab_server(conn);
free(input_focus);
free(coordinates);
free(geometry);
if (!success) {
LOG("Could not position on focused output, trying to position on primary output.\n");
set_window_position_primary(result);
}
}
int main(int argc, char *argv[]) {
@@ -360,6 +407,7 @@ int main(int argc, char *argv[]) {
argv0 = argv[0];
bool position_on_primary = false;
char *pattern = sstrdup("pango:monospace 8");
int o, option_index = 0;
enum { TYPE_ERROR = 0,
@@ -373,9 +421,10 @@ int main(int argc, char *argv[]) {
{"help", no_argument, 0, 'h'},
{"message", required_argument, 0, 'm'},
{"type", required_argument, 0, 't'},
{"primary", no_argument, 0, 'p'},
{0, 0, 0, 0}};
char *options_string = "b:B:f:m:t:vh";
char *options_string = "b:B:f:m:t:vhp";
prompt = i3string_from_utf8("Please do not run this program.");
@@ -399,8 +448,11 @@ int main(int argc, char *argv[]) {
case 'h':
free(pattern);
printf("i3-nagbar " I3_VERSION "\n");
printf("i3-nagbar [-m <message>] [-b <button> <action>] [-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] [-p]\n");
return 0;
case 'p':
position_on_primary = true;
break;
case 'b':
case 'B':
buttons = srealloc(buttons, sizeof(button_t) * (buttoncnt + 1));
@@ -412,8 +464,9 @@ int main(int argc, char *argv[]) {
buttons[buttoncnt].action);
buttoncnt++;
printf("now %d buttons\n", buttoncnt);
if (optind < argc)
if (optind < argc) {
optind++;
}
break;
}
}
@@ -422,8 +475,9 @@ int main(int argc, char *argv[]) {
int screens;
if ((conn = xcb_connect(NULL, &screens)) == NULL ||
xcb_connection_has_error(conn))
xcb_connection_has_error(conn)) {
die("Cannot open display");
}
/* Place requests for the atoms we need as soon as possible */
#define xmacro(atom) \
@@ -459,12 +513,13 @@ int main(int argc, char *argv[]) {
font = load_font(pattern, true);
set_font(&font);
#if defined(__OpenBSD__)
if (pledge("stdio rpath wpath cpath getpw proc exec", NULL) == -1)
err(EXIT_FAILURE, "pledge");
#endif
xcb_rectangle_t win_pos = get_window_position();
/* Default values if we cannot determine the preferred window position. */
xcb_rectangle_t win_pos = (xcb_rectangle_t){50, 50, 500, font.height + 2 * MSG_PADDING + BAR_BORDER};
if (position_on_primary) {
set_window_position_primary(&win_pos);
} else {
set_window_position_focus(&win_pos);
}
xcb_cursor_context_t *cursor_ctx;
if (xcb_cursor_context_new(conn, root_screen, &cursor_ctx) < 0) {

View File

@@ -8,6 +8,11 @@
# Distributions/packagers can enhance this script with a
# distribution-specific mechanism to find the preferred pager.
# The less -E and -F options exit immediately for short files, strip if present.
case "$LESS" in
*[EF]*) LESS=`echo "$LESS" | tr -d EF`
esac
# Hopefully one of these is installed (no flamewars about preference please!):
# We don't use 'more' because it will exit if the file is too short.
# Worst case scenario we'll open the file in your editor.

View File

@@ -8,7 +8,13 @@
# 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 alacritty hyper; do
#
# Invariants:
# 1. $TERMINAL must come first
# 2. Distribution-specific mechanisms come next, e.g. x-terminal-emulator
# 3. The terminal emulator with best accessibility comes first.
# 4. No order is guaranteed/desired for the remaining terminal emulators.
for terminal in "$TERMINAL" x-terminal-emulator mate-terminal gnome-terminal terminator xfce4-terminal urxvt rxvt termit Eterm aterm uxterm xterm roxterm termite lxterminal terminology st qterminal lilyterm tilix terminix konsole kitty guake tilda alacritty hyper wezterm rio; do
if command -v "$terminal" > /dev/null 2>&1; then
exec "$terminal" "$@"
fi

View File

@@ -11,7 +11,7 @@
#include <config.h>
#include <stdbool.h>
#include <ev.h>
#define STDIN_CHUNK_SIZE 1024
@@ -40,40 +40,88 @@ typedef struct {
*/
bool click_events;
bool click_events_init;
/**
* stdin- and SIGCHLD-watchers
*/
ev_io *stdin_io;
ev_child *child_sig;
int stdin_fd;
/**
* Line read from child that did not include a newline character.
*/
char *pending_line;
} i3bar_child;
/*
* Remove all blocks from the given statusline.
* If free_resources is set, the fields of each status block will be free'd.
*/
void clear_statusline(struct statusline_head *head, bool free_resources);
/*
* Start a child process with the specified command and reroute stdin.
* We actually start a $SHELL to execute the command so we don't have to care
* about arguments and such
* We actually start a shell to execute the command so we don't have to care
* about arguments and such.
*
* If `command' is NULL, such as in the case when no `status_command' is given
* in the bar config, no child will be started.
*
*/
void start_child(char *command);
/*
* Same as start_child but starts the configured client that manages workspace
* buttons.
*
*/
void start_ws_child(char *command);
/*
* Returns true if the status child process is alive.
*
*/
bool status_child_is_alive(void);
/*
* Returns true if the workspace child process is alive.
*
*/
bool ws_child_is_alive(void);
/*
* kill()s the child process (if any). Called when exit()ing.
*
*/
void kill_child_at_exit(void);
void kill_children_at_exit(void);
/*
* kill()s the child process (if any) and closes and
* free()s the stdin- and SIGCHLD-watchers
* kill()s the child process (if any) and closes and free()s the stdin- and
* SIGCHLD-watchers
*
*/
void kill_child(void);
/*
* kill()s the workspace child process (if any) and closes and free()s the
* stdin- and SIGCHLD-watchers.
* Similar to kill_child.
*
*/
void kill_ws_child(void);
/*
* Sends a SIGSTOP to the child process (if existent)
*
*/
void stop_child(void);
void stop_children(void);
/*
* Sends a SIGCONT to the child process (if existent)
*
*/
void cont_child(void);
void cont_children(void);
/*
* Whether or not the child want click events
@@ -86,3 +134,14 @@ bool child_want_click_events(void);
*
*/
void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int out_x, int out_y, int width, int height, int mods);
/*
* When workspace_command is enabled this function is used to re-parse the
* latest received JSON from the client.
*/
void repeat_last_ws_json(void);
/*
* Replaces the workspace buttons with an error message.
*/
void set_workspace_button_error(const char *message);

View File

@@ -46,6 +46,9 @@ struct status_block {
i3String *full_text;
i3String *short_text;
bool use_short;
uint32_t render_length;
char *color;
char *background;
char *border;
@@ -66,7 +69,7 @@ struct status_block {
uint32_t border_left;
bool pango_markup;
/* The amount of pixels necessary to render a separater after the block. */
/* The amount of pixels necessary to render a separator after the block. */
uint32_t sep_block_width;
/* Continuously-updated information on how to render this status block. */

View File

@@ -38,11 +38,21 @@ typedef struct tray_output_t {
TAILQ_ENTRY(tray_output_t) tray_outputs;
} tray_output_t;
/* Matches i3/include/data.h */
struct Rect {
uint32_t x;
uint32_t y;
uint32_t width;
uint32_t height;
};
typedef struct config_t {
uint32_t modifier;
TAILQ_HEAD(bindings_head, binding_t) bindings;
position_t position;
bool verbose;
uint32_t bar_height;
struct Rect padding;
bool transparency;
struct xcb_color_strings_t colors;
bool disable_binding_mode_indicator;
@@ -52,6 +62,7 @@ typedef struct config_t {
bool strip_ws_name;
char *bar_id;
char *command;
char *workspace_command;
char *fontname;
i3String *separator_symbol;
TAILQ_HEAD(tray_outputs_head, tray_output_t) tray_outputs;
@@ -69,10 +80,17 @@ typedef struct config_t {
extern config_t config;
/**
* Start parsing the received bar configuration JSON string
* Parse the received bar configuration JSON string
*
*/
void parse_config_json(char *json);
void parse_config_json(const unsigned char *json, size_t size);
/**
* Parse the received bar configuration list. The only usecase right now is to
* automatically get the first bar id.
*
*/
void parse_get_first_i3bar_config(const unsigned char *json, size_t size);
/**
* free()s the color strings as soon as they are not needed anymore.

View File

@@ -18,7 +18,7 @@
* socket_path must be a valid path to the ipc_socket of i3
*
*/
int init_connection(const char *socket_path);
void init_connection(const char *socket_path);
/*
* Destroy the connection to i3.

View File

@@ -24,7 +24,7 @@ struct mode {
typedef struct mode mode;
/*
* Start parsing the received JSON string
* Parse the received JSON string
*
*/
void parse_mode_json(char *json);
void parse_mode_json(const unsigned char *json, size_t size);

View File

@@ -22,10 +22,10 @@ SLIST_HEAD(outputs_head, i3_output);
extern struct outputs_head* outputs;
/*
* Start parsing the received JSON string
* Parse the received JSON string
*
*/
void parse_outputs_json(char* json);
void parse_outputs_json(const unsigned char* json, size_t size);
/*
* Initiate the outputs list
@@ -65,8 +65,6 @@ struct i3_output {
surface_t statusline_buffer;
/* How much of statusline_buffer's horizontal space was used on last statusline render. */
int statusline_width;
/* Whether statusline block short texts where used on last statusline render. */
bool statusline_short_text;
/* The actual window on which we draw. */
surface_t bar;

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/

View File

@@ -18,10 +18,10 @@ typedef struct i3_ws i3_ws;
TAILQ_HEAD(ws_head, i3_ws);
/*
* Start parsing the received JSON string
* Parse the received JSON string
*
*/
void parse_workspaces_json(char *json);
void parse_workspaces_json(const unsigned char *json, size_t size);
/*
* free() all workspace data structures
@@ -38,7 +38,6 @@ struct i3_ws {
bool visible; /* If the ws is currently visible on an output */
bool focused; /* If the ws is currently focused */
bool urgent; /* If the urgent hint of the ws is set */
rect rect; /* The rect of the ws (not used (yet)) */
struct i3_output *output; /* The current output of the ws */
TAILQ_ENTRY(i3_ws) tailq; /* Pointer for the TAILQ-Macro */

View File

@@ -12,7 +12,6 @@
#include <config.h>
#include <stdint.h>
//#include "outputs.h"
#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1

View File

@@ -8,8 +8,10 @@
*
*/
#include "common.h"
#include "queue.h"
#include "yajl_utils.h"
#include <ctype.h> /* isspace */
#include <err.h>
#include <errno.h>
#include <ev.h>
@@ -20,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -27,14 +30,30 @@
#include <yajl/yajl_parse.h>
/* Global variables for child_*() */
i3bar_child child;
#define DLOG_CHILD DLOG("%s: pid=%ld stopped=%d stop_signal=%d cont_signal=%d click_events=%d click_events_init=%d\n", \
__func__, (long)child.pid, child.stopped, child.stop_signal, child.cont_signal, child.click_events, child.click_events_init)
i3bar_child status_child = {0};
i3bar_child ws_child = {0};
/* stdin- and SIGCHLD-watchers */
ev_io *stdin_io;
int stdin_fd;
ev_child *child_sig;
#define DLOG_CHILD(c) \
do { \
if ((c).pid == 0) { \
DLOG("%s: child pid = 0\n", __func__); \
} else if ((c).pid == status_child.pid) { \
DLOG("%s: status_command: pid=%ld stopped=%d stop_signal=%d cont_signal=%d click_events=%d click_events_init=%d\n", \
__func__, (long)(c).pid, (c).stopped, (c).stop_signal, (c).cont_signal, (c).click_events, (c).click_events_init); \
} else if ((c).pid == ws_child.pid) { \
DLOG("%s: workspace_command: pid=%ld stopped=%d stop_signal=%d cont_signal=%d click_events=%d click_events_init=%d\n", \
__func__, (long)(c).pid, (c).stopped, (c).stop_signal, (c).cont_signal, (c).click_events, (c).click_events_init); \
} else { \
ELOG("%s: unknown child, this should never happen " \
"pid=%ld stopped=%d stop_signal=%d cont_signal=%d click_events=%d click_events_init=%d\n", \
__func__, (long)(c).pid, (c).stopped, (c).stop_signal, (c).cont_signal, (c).click_events, (c).click_events_init); \
} \
} while (0)
#define DLOG_CHILDREN \
do { \
DLOG_CHILD(status_child); \
DLOG_CHILD(ws_child); \
} while (0)
/* JSON parser for stdin */
yajl_handle parser;
@@ -66,7 +85,7 @@ int child_stdin;
* Remove all blocks from the given statusline.
* If free_resources is set, the fields of each status block will be free'd.
*/
static void clear_statusline(struct statusline_head *head, bool free_resources) {
void clear_statusline(struct statusline_head *head, bool free_resources) {
struct status_block *first;
while (!TAILQ_EMPTY(head)) {
first = TAILQ_FIRST(head);
@@ -127,7 +146,7 @@ __attribute__((format(printf, 1, 2))) static void set_statusline_error(const cha
TAILQ_INSERT_TAIL(&statusline_head, message_block, blocks);
finish:
FREE(message);
free(message);
va_end(args);
}
@@ -135,18 +154,27 @@ finish:
* Stop and free() the stdin- and SIGCHLD-watchers
*
*/
static void cleanup(void) {
if (stdin_io != NULL) {
ev_io_stop(main_loop, stdin_io);
FREE(stdin_io);
static void cleanup(i3bar_child *c) {
DLOG_CHILD(*c);
if (c->stdin_io != NULL) {
ev_io_stop(main_loop, c->stdin_io);
FREE(c->stdin_io);
if (c->pid == status_child.pid) {
close(child_stdin);
child_stdin = 0;
}
close(c->stdin_fd);
}
if (child_sig != NULL) {
ev_child_stop(main_loop, child_sig);
FREE(child_sig);
if (c->child_sig != NULL) {
ev_child_stop(main_loop, c->child_sig);
FREE(c->child_sig);
}
memset(&child, 0, sizeof(i3bar_child));
FREE(c->pending_line);
memset(c, 0, sizeof(i3bar_child));
}
/*
@@ -169,10 +197,11 @@ static int stdin_start_map(void *context) {
memset(&(ctx->block), '\0', sizeof(struct status_block));
/* Default width of the separator block. */
if (config.separator_symbol == NULL)
if (config.separator_symbol == NULL) {
ctx->block.sep_block_width = logical_px(9);
else
} else {
ctx->block.sep_block_width = logical_px(8) + separator_symbol_width;
}
/* By default we draw all four borders if a border is set. */
ctx->block.border_top = 1;
@@ -311,10 +340,12 @@ static int stdin_end_map(void *context) {
memcpy(new_block, &(ctx->block), sizeof(struct status_block));
/* Ensure we have a full_text set, so that when it is missing (or null),
* i3bar doesnt crash and the user gets an annoying message. */
if (!new_block->full_text)
if (!new_block->full_text) {
new_block->full_text = i3string_from_utf8("SPEC VIOLATION: full_text is NULL!");
if (new_block->urgent)
}
if (new_block->urgent) {
ctx->has_urgent = true;
}
if (new_block->min_width_str) {
i3String *text = i3string_from_utf8(new_block->min_width_str);
@@ -325,10 +356,13 @@ static int stdin_end_map(void *context) {
i3string_set_markup(new_block->full_text, new_block->pango_markup);
if (new_block->short_text != NULL)
new_block->use_short = false;
if (new_block->short_text != NULL) {
i3string_set_markup(new_block->short_text, new_block->pango_markup);
}
TAILQ_INSERT_TAIL(&statusline_buffer, new_block, blocks);
return 1;
}
@@ -358,15 +392,13 @@ static int stdin_end_array(void *context) {
* Returns NULL on EOF.
*
*/
static unsigned char *get_buffer(ev_io *watcher, int *ret_buffer_len) {
int fd = watcher->fd;
int n = 0;
static unsigned char *get_buffer(int fd, int *ret_buffer_len) {
int rec = 0;
int buffer_len = STDIN_CHUNK_SIZE;
unsigned char *buffer = smalloc(buffer_len + 1);
buffer[0] = '\0';
while (1) {
n = read(fd, buffer + rec, buffer_len - rec);
const ssize_t n = read(fd, buffer + rec, buffer_len - rec);
if (n == -1) {
if (errno == EAGAIN) {
/* finish up */
@@ -386,10 +418,11 @@ static unsigned char *get_buffer(ev_io *watcher, int *ret_buffer_len) {
if (rec == buffer_len) {
buffer_len += STDIN_CHUNK_SIZE;
buffer = srealloc(buffer, buffer_len);
buffer = srealloc(buffer, buffer_len + 1);
}
}
if (*buffer == '\0') {
buffer[rec] = '\0';
if (buffer[0] == '\0') {
FREE(buffer);
rec = -1;
}
@@ -419,8 +452,9 @@ static bool read_json_input(unsigned char *input, int length) {
char *message = (char *)yajl_get_error(parser, 0, input, length);
/* strip the newline yajl adds to the error message */
if (message[strlen(message) - 1] == '\n')
if (message[strlen(message) - 1] == '\n') {
message[strlen(message) - 1] = '\0';
}
fprintf(stderr, "[i3bar] Could not parse JSON input (code = %d, message = %s): %.*s\n",
status, message, length, input);
@@ -439,13 +473,14 @@ static bool read_json_input(unsigned char *input, int length) {
* in statusline
*
*/
static void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
static void stdin_io_cb(int fd) {
int rec;
unsigned char *buffer = get_buffer(watcher, &rec);
if (buffer == NULL)
unsigned char *buffer = get_buffer(fd, &rec);
if (buffer == NULL) {
return;
}
bool has_urgent = false;
if (child.version > 0) {
if (status_child.version > 0) {
has_urgent = read_json_input(buffer, rec);
} else {
read_flat_input((char *)buffer, rec);
@@ -459,22 +494,23 @@ static void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
* whether this is JSON or plain text
*
*/
static void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
static void stdin_io_first_line_cb(int fd) {
int rec;
unsigned char *buffer = get_buffer(watcher, &rec);
if (buffer == NULL)
unsigned char *buffer = get_buffer(fd, &rec);
if (buffer == NULL) {
return;
}
DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer);
/* Detect whether this is JSON or plain text. */
unsigned int consumed = 0;
/* At the moment, we dont care for the version. This might change
* in the future, but for now, we just discard it. */
parse_json_header(&child, buffer, rec, &consumed);
if (child.version > 0) {
/* If hide-on-modifier is set, we start of by sending the
* child a SIGSTOP, because the bars aren't mapped at start */
parse_json_header(&status_child, buffer, rec, &consumed);
if (status_child.version > 0) {
/* If hide-on-modifier is set, we start of by sending the status_child
* a SIGSTOP, because the bars aren't mapped at start */
if (config.hide_on_modifier) {
stop_child();
stop_children();
}
draw_bars(read_json_input(buffer + consumed, rec - consumed));
} else {
@@ -485,9 +521,133 @@ static void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int rev
read_flat_input((char *)buffer, rec);
}
free(buffer);
ev_io_stop(main_loop, stdin_io);
ev_io_init(stdin_io, &stdin_io_cb, stdin_fd, EV_READ);
ev_io_start(main_loop, stdin_io);
}
static bool isempty(char *s) {
while (*s != '\0') {
if (!isspace(*s)) {
return false;
}
s++;
}
return true;
}
static char *append_string(const char *previous, const char *str) {
if (previous != NULL) {
char *result;
sasprintf(&result, "%s%s", previous, str);
return result;
}
return sstrdup(str);
}
static char *ws_last_json;
static void ws_stdin_io_cb(int fd) {
int rec;
unsigned char *buffer = get_buffer(fd, &rec);
if (buffer == NULL) {
return;
}
gchar **strings = g_strsplit((const char *)buffer, "\n", 0);
for (int idx = 0; strings[idx] != NULL; idx++) {
if (ws_child.pending_line == NULL && isempty(strings[idx])) {
/* In the normal case where the buffer ends with '\n', the last
* string should be empty */
continue;
}
if (strings[idx + 1] == NULL) {
/* This is the last string but it is not empty, meaning that we have
* read data that is incomplete, save it for later. */
char *new = append_string(ws_child.pending_line, strings[idx]);
free(ws_child.pending_line);
ws_child.pending_line = new;
continue;
}
free(ws_last_json);
ws_last_json = append_string(ws_child.pending_line, strings[idx]);
FREE(ws_child.pending_line);
parse_workspaces_json((const unsigned char *)ws_last_json, strlen(ws_last_json));
}
g_strfreev(strings);
free(buffer);
draw_bars(false);
}
static void common_stdin_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
if (watcher == status_child.stdin_io) {
if (status_child.version == (uint32_t)-1) {
stdin_io_first_line_cb(watcher->fd);
} else {
stdin_io_cb(watcher->fd);
}
} else if (watcher == ws_child.stdin_io) {
ws_stdin_io_cb(watcher->fd);
} else {
ELOG("Got callback for unknown watcher fd=%d\n", watcher->fd);
}
}
/*
* When workspace_command is enabled this function is used to re-parse the
* latest received JSON from the client.
*/
void repeat_last_ws_json(void) {
if (ws_last_json) {
DLOG("Repeating last workspace JSON\n");
parse_workspaces_json((const unsigned char *)ws_last_json, strlen(ws_last_json));
}
}
/*
* Wrapper around set_workspace_button_error to mimic the call of
* set_statusline_error.
*/
__attribute__((format(printf, 1, 2))) static void set_workspace_button_error_f(const char *format, ...) {
char *message;
va_list args;
va_start(args, format);
if (vasprintf(&message, format, args) == -1) {
goto finish;
}
set_workspace_button_error(message);
finish:
free(message);
va_end(args);
}
/*
* Replaces the workspace buttons with an error message.
*/
void set_workspace_button_error(const char *message) {
free_workspaces();
char *name = NULL;
sasprintf(&name, "Error: %s", message);
i3_output *output;
SLIST_FOREACH (output, outputs, slist) {
i3_ws *fake_ws = scalloc(1, sizeof(i3_ws));
/* Don't set the canonical_name field to make this workspace unfocusable. */
fake_ws->name = i3string_from_utf8(name);
fake_ws->name_width = predict_text_width(fake_ws->name);
fake_ws->num = -1;
fake_ws->urgent = fake_ws->visible = true;
fake_ws->output = output;
TAILQ_INSERT_TAIL(output->workspaces, fake_ws, tailq);
}
free(name);
}
/*
@@ -497,27 +657,45 @@ static void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int rev
*
*/
static void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
int exit_status = WEXITSTATUS(watcher->rstatus);
const int exit_status = WEXITSTATUS(watcher->rstatus);
ELOG("Child (pid: %d) unexpectedly exited with status %d\n",
child.pid,
watcher->pid,
exit_status);
void (*error_function_pointer)(const char *, ...) = NULL;
const char *command_type = "";
i3bar_child *c = NULL;
if (watcher->pid == status_child.pid) {
command_type = "status_command";
error_function_pointer = set_statusline_error;
c = &status_child;
} else if (watcher->pid == ws_child.pid) {
command_type = "workspace_command";
error_function_pointer = set_workspace_button_error_f;
c = &ws_child;
} else {
ELOG("Unknown child pid, this should never happen\n");
return;
}
DLOG_CHILD(*c);
/* this error is most likely caused by a user giving a nonexecutable or
* nonexistent file, so we will handle those cases separately. */
if (exit_status == 126)
set_statusline_error("status_command is not executable (exit %d)", exit_status);
else if (exit_status == 127)
set_statusline_error("status_command not found or is missing a library dependency (exit %d)", exit_status);
else
set_statusline_error("status_command process exited unexpectedly (exit %d)", exit_status);
if (exit_status == 126) {
error_function_pointer("%s is not executable (exit %d)", command_type, exit_status);
} else if (exit_status == 127) {
error_function_pointer("%s not found or is missing a library dependency (exit %d)", command_type, exit_status);
} else {
error_function_pointer("%s process exited unexpectedly (exit %d)", command_type, exit_status);
}
cleanup();
cleanup(c);
draw_bars(false);
}
static void child_write_output(void) {
if (child.click_events) {
if (status_child.click_events) {
const unsigned char *output;
size_t size;
ssize_t n;
@@ -525,13 +703,14 @@ static void child_write_output(void) {
yajl_gen_get_buf(gen, &output, &size);
n = writeall(child_stdin, output, size);
if (n != -1)
if (n != -1) {
n = writeall(child_stdin, "\n", 1);
}
yajl_gen_clear(gen);
if (n == -1) {
child.click_events = false;
status_child.click_events = false;
kill_child();
set_statusline_error("child_write_output failed");
draw_bars(false);
@@ -539,6 +718,41 @@ static void child_write_output(void) {
}
}
static pid_t sfork(void) {
const pid_t pid = fork();
if (pid == -1) {
ELOG("Couldn't fork(): %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
return pid;
}
static void spipe(int pipedes[2]) {
if (pipe(pipedes) == -1) {
err(EXIT_FAILURE, "pipe(pipe_in)");
}
}
static void exec_shell(char *command) {
execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, (char *)NULL);
}
static void setup_child_cb(i3bar_child *child) {
/* We set O_NONBLOCK because blocking is evil in event-driven software */
fcntl(child->stdin_fd, F_SETFL, O_NONBLOCK);
child->stdin_io = smalloc(sizeof(ev_io));
ev_io_init(child->stdin_io, &common_stdin_cb, child->stdin_fd, EV_READ);
ev_io_start(main_loop, child->stdin_io);
/* We must cleanup, if the child unexpectedly terminates */
child->child_sig = smalloc(sizeof(ev_child));
ev_child_init(child->child_sig, &child_sig_cb, child->pid, 0);
ev_child_start(main_loop, child->child_sig);
DLOG_CHILD(*child);
}
/*
* Start a child process with the specified command and reroute stdin.
* We actually start a shell to execute the command so we don't have to care
@@ -549,8 +763,9 @@ static void child_write_output(void) {
*
*/
void start_child(char *command) {
if (command == NULL)
if (command == NULL) {
return;
}
/* Allocate a yajl parser which will be used to parse stdin. */
static yajl_callbacks callbacks = {
@@ -564,69 +779,77 @@ void start_child(char *command) {
.yajl_end_array = stdin_end_array,
};
parser = yajl_alloc(&callbacks, NULL, &parser_context);
gen = yajl_gen_alloc(NULL);
int pipe_in[2]; /* pipe we read from */
int pipe_out[2]; /* pipe we write to */
spipe(pipe_in);
spipe(pipe_out);
if (pipe(pipe_in) == -1)
err(EXIT_FAILURE, "pipe(pipe_in)");
if (pipe(pipe_out) == -1)
err(EXIT_FAILURE, "pipe(pipe_out)");
status_child.pid = sfork();
if (status_child.pid == 0) {
/* Child-process. Reroute streams and start shell */
close(pipe_in[0]);
close(pipe_out[1]);
child.pid = fork();
switch (child.pid) {
case -1:
ELOG("Couldn't fork(): %s\n", strerror(errno));
exit(EXIT_FAILURE);
case 0:
/* Child-process. Reroute streams and start shell */
dup2(pipe_in[1], STDOUT_FILENO);
dup2(pipe_out[0], STDIN_FILENO);
close(pipe_in[0]);
close(pipe_out[1]);
setpgid(status_child.pid, 0);
exec_shell(command);
return;
}
/* Parent-process. Reroute streams */
close(pipe_in[1]);
close(pipe_out[0]);
dup2(pipe_in[1], STDOUT_FILENO);
dup2(pipe_out[0], STDIN_FILENO);
status_child.stdin_fd = pipe_in[0];
child_stdin = pipe_out[1];
status_child.version = -1;
setpgid(child.pid, 0);
execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, (char *)NULL);
return;
default:
/* Parent-process. Reroute streams */
setup_child_cb(&status_child);
}
close(pipe_in[1]);
close(pipe_out[0]);
stdin_fd = pipe_in[0];
child_stdin = pipe_out[1];
break;
/*
* Same as start_child but starts the configured client that manages workspace
* buttons.
*
*/
void start_ws_child(char *command) {
if (command == NULL) {
return;
}
/* We set O_NONBLOCK because blocking is evil in event-driven software */
fcntl(stdin_fd, F_SETFL, O_NONBLOCK);
ws_child.stop_signal = SIGSTOP;
ws_child.cont_signal = SIGCONT;
stdin_io = smalloc(sizeof(ev_io));
ev_io_init(stdin_io, &stdin_io_first_line_cb, stdin_fd, EV_READ);
ev_io_start(main_loop, stdin_io);
int pipe_in[2]; /* pipe we read from */
spipe(pipe_in);
/* We must cleanup, if the child unexpectedly terminates */
child_sig = smalloc(sizeof(ev_child));
ev_child_init(child_sig, &child_sig_cb, child.pid, 0);
ev_child_start(main_loop, child_sig);
ws_child.pid = sfork();
if (ws_child.pid == 0) {
/* Child-process. Reroute streams and start shell */
close(pipe_in[0]);
dup2(pipe_in[1], STDOUT_FILENO);
atexit(kill_child_at_exit);
DLOG_CHILD;
setpgid(ws_child.pid, 0);
exec_shell(command);
return;
}
/* Parent-process. Reroute streams */
close(pipe_in[1]);
ws_child.stdin_fd = pipe_in[0];
setup_child_cb(&ws_child);
}
static void child_click_events_initialize(void) {
DLOG_CHILD;
DLOG_CHILD(status_child);
if (!child.click_events_init) {
if (!status_child.click_events_init) {
yajl_gen_array_open(gen);
child_write_output();
child.click_events_init = true;
status_child.click_events_init = true;
}
}
@@ -635,7 +858,7 @@ static void child_click_events_initialize(void) {
*
*/
void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int out_x, int out_y, int width, int height, int mods) {
if (!child.click_events) {
if (!status_child.click_events) {
return;
}
@@ -658,20 +881,27 @@ void send_block_clicked(int button, const char *name, const char *instance, int
ystr("modifiers");
yajl_gen_array_open(gen);
if (mods & XCB_MOD_MASK_SHIFT)
if (mods & XCB_MOD_MASK_SHIFT) {
ystr("Shift");
if (mods & XCB_MOD_MASK_CONTROL)
}
if (mods & XCB_MOD_MASK_CONTROL) {
ystr("Control");
if (mods & XCB_MOD_MASK_1)
}
if (mods & XCB_MOD_MASK_1) {
ystr("Mod1");
if (mods & XCB_MOD_MASK_2)
}
if (mods & XCB_MOD_MASK_2) {
ystr("Mod2");
if (mods & XCB_MOD_MASK_3)
}
if (mods & XCB_MOD_MASK_3) {
ystr("Mod3");
if (mods & XCB_MOD_MASK_4)
}
if (mods & XCB_MOD_MASK_4) {
ystr("Mod4");
if (mods & XCB_MOD_MASK_5)
}
if (mods & XCB_MOD_MASK_5) {
ystr("Mod5");
}
yajl_gen_array_close(gen);
ystr("x");
@@ -702,35 +932,85 @@ void send_block_clicked(int button, const char *name, const char *instance, int
child_write_output();
}
static bool is_alive(i3bar_child *c) {
return c->pid > 0;
}
/*
* Returns true if the status child process is alive.
*
*/
bool status_child_is_alive(void) {
return is_alive(&status_child);
}
/*
* Returns true if the workspace child process is alive.
*
*/
bool ws_child_is_alive(void) {
return is_alive(&ws_child);
}
/*
* kill()s the child process (if any). Called when exit()ing.
*
*/
void kill_child_at_exit(void) {
DLOG_CHILD;
void kill_children_at_exit(void) {
DLOG_CHILDREN;
cont_children();
if (child.pid > 0) {
if (child.cont_signal > 0 && child.stopped)
killpg(child.pid, child.cont_signal);
killpg(child.pid, SIGTERM);
if (is_alive(&status_child)) {
killpg(status_child.pid, SIGTERM);
}
if (is_alive(&ws_child)) {
killpg(ws_child.pid, SIGTERM);
}
}
static void cont_child(i3bar_child *c) {
if (is_alive(c) && c->cont_signal > 0 && c->stopped) {
c->stopped = false;
killpg(c->pid, c->cont_signal);
}
}
static void kill_and_wait(i3bar_child *c) {
DLOG_CHILD(*c);
if (!is_alive(c)) {
return;
}
cont_child(c);
killpg(c->pid, SIGTERM);
int status;
waitpid(c->pid, &status, 0);
cleanup(c);
}
/*
* kill()s the child process (if existent) and closes and
* free()s the stdin- and SIGCHLD-watchers
* kill()s the child process (if any) and closes and free()s the stdin- and
* SIGCHLD-watchers
*
*/
void kill_child(void) {
DLOG_CHILD;
kill_and_wait(&status_child);
}
if (child.pid > 0) {
if (child.cont_signal > 0 && child.stopped)
killpg(child.pid, child.cont_signal);
killpg(child.pid, SIGTERM);
int status;
waitpid(child.pid, &status, 0);
cleanup();
/*
* kill()s the workspace child process (if any) and closes and free()s the
* stdin- and SIGCHLD-watchers.
* Similar to kill_child.
*
*/
void kill_ws_child(void) {
kill_and_wait(&ws_child);
}
static void stop_child(i3bar_child *c) {
if (c->stop_signal > 0 && !c->stopped) {
c->stopped = true;
killpg(c->pid, c->stop_signal);
}
}
@@ -738,26 +1018,21 @@ void kill_child(void) {
* Sends a SIGSTOP to the child process (if existent)
*
*/
void stop_child(void) {
DLOG_CHILD;
if (child.stop_signal > 0 && !child.stopped) {
child.stopped = true;
killpg(child.pid, child.stop_signal);
}
void stop_children(void) {
DLOG_CHILDREN;
stop_child(&status_child);
stop_child(&ws_child);
}
/*
* Sends a SIGCONT to the child process (if existent)
*
*/
void cont_child(void) {
DLOG_CHILD;
void cont_children(void) {
DLOG_CHILDREN;
if (child.cont_signal > 0 && child.stopped) {
child.stopped = false;
killpg(child.pid, child.cont_signal);
}
cont_child(&status_child);
cont_child(&ws_child);
}
/*
@@ -765,5 +1040,5 @@ void cont_child(void) {
*
*/
bool child_want_click_events(void) {
return child.click_events;
return status_child.click_events;
}

View File

@@ -13,13 +13,13 @@
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <yajl/yajl_parse.h>
config_t config;
static char *cur_key;
static bool parsing_bindings;
static bool parsing_tray_outputs;
static bool parsing_padding;
/*
* Parse a key.
@@ -39,12 +39,17 @@ static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t
parsing_tray_outputs = true;
}
if (strcmp(cur_key, "padding") == 0) {
parsing_padding = true;
}
return 1;
}
static int config_end_array_cb(void *params_) {
parsing_bindings = false;
parsing_tray_outputs = false;
parsing_padding = false;
return 1;
}
@@ -71,8 +76,9 @@ static int config_null_cb(void *params_) {
static int config_string_cb(void *params_, const unsigned char *val, size_t _len) {
int len = (int)_len;
/* The id and socket_path are ignored, we already know them. */
if (!strcmp(cur_key, "id") || !strcmp(cur_key, "socket_path"))
if (!strcmp(cur_key, "id") || !strcmp(cur_key, "socket_path")) {
return 1;
}
if (parsing_bindings) {
if (strcmp(cur_key, "command") == 0) {
@@ -126,31 +132,31 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
}
if (len == strlen("shift") && !strncmp((const char *)val, "shift", strlen("shift"))) {
config.modifier = ShiftMask;
config.modifier = XCB_MOD_MASK_SHIFT;
return 1;
}
if (len == strlen("ctrl") && !strncmp((const char *)val, "ctrl", strlen("ctrl"))) {
config.modifier = ControlMask;
config.modifier = XCB_MOD_MASK_CONTROL;
return 1;
}
if (len == strlen("Mod") + 1 && !strncmp((const char *)val, "Mod", strlen("Mod"))) {
switch (val[3]) {
case '1':
config.modifier = Mod1Mask;
config.modifier = XCB_MOD_MASK_1;
return 1;
case '2':
config.modifier = Mod2Mask;
config.modifier = XCB_MOD_MASK_2;
return 1;
case '3':
config.modifier = Mod3Mask;
config.modifier = XCB_MOD_MASK_3;
return 1;
case '5':
config.modifier = Mod5Mask;
config.modifier = XCB_MOD_MASK_5;
return 1;
}
}
config.modifier = Mod4Mask;
config.modifier = XCB_MOD_MASK_4;
return 1;
}
@@ -183,12 +189,17 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
}
if (!strcmp(cur_key, "status_command")) {
DLOG("command = %.*s\n", len, val);
FREE(config.command);
DLOG("status_command = %.*s\n", len, val);
sasprintf(&config.command, "%.*s", len, val);
return 1;
}
if (!strcmp(cur_key, "workspace_command")) {
DLOG("workspace_command = %.*s\n", len, val);
sasprintf(&config.workspace_command, "%.*s", len, val);
return 1;
}
if (!strcmp(cur_key, "font")) {
DLOG("font = %.*s\n", len, val);
FREE(config.fontname);
@@ -331,6 +342,35 @@ static int config_integer_cb(void *params_, long long val) {
return 0;
}
if (parsing_padding) {
if (strcmp(cur_key, "x") == 0) {
DLOG("padding.x = %lld\n", val);
config.padding.x = (uint32_t)val;
return 1;
}
if (strcmp(cur_key, "y") == 0) {
DLOG("padding.y = %lld\n", val);
config.padding.y = (uint32_t)val;
return 1;
}
if (strcmp(cur_key, "width") == 0) {
DLOG("padding.width = %lld\n", val);
config.padding.width = (uint32_t)val;
return 1;
}
if (strcmp(cur_key, "height") == 0) {
DLOG("padding.height = %lld\n", val);
config.padding.height = (uint32_t)val;
return 1;
}
}
if (!strcmp(cur_key, "bar_height")) {
DLOG("bar_height = %lld\n", val);
config.bar_height = (uint32_t)val;
return 1;
}
if (!strcmp(cur_key, "tray_padding")) {
DLOG("tray_padding = %lld\n", val);
config.tray_padding = val;
@@ -355,26 +395,23 @@ static int config_integer_cb(void *params_, long long val) {
/* A datastructure to pass all these callbacks to yajl */
static yajl_callbacks outputs_callbacks = {
.yajl_null = config_null_cb,
.yajl_boolean = config_boolean_cb,
.yajl_integer = config_integer_cb,
.yajl_boolean = config_boolean_cb,
.yajl_string = config_string_cb,
.yajl_end_array = config_end_array_cb,
.yajl_map_key = config_map_key_cb,
};
/*
* Start parsing the received bar configuration JSON string
* Parse the received bar configuration JSON string
*
*/
void parse_config_json(char *json) {
yajl_handle handle;
yajl_status state;
handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
void parse_config_json(const unsigned char *json, size_t size) {
TAILQ_INIT(&(config.bindings));
TAILQ_INIT(&(config.tray_outputs));
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
yajl_handle handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
yajl_status state = yajl_parse(handle, json, size);
/* FIXME: Proper error handling for JSON parsing */
switch (state) {
@@ -387,6 +424,30 @@ void parse_config_json(char *json) {
break;
}
if (config.disable_ws && config.workspace_command) {
ELOG("You have specified 'workspace_buttons no'. Your 'workspace_command %s' will be ignored.\n", config.workspace_command);
FREE(config.workspace_command);
}
yajl_free(handle);
}
static int i3bar_config_string_cb(void *params_, const unsigned char *val, size_t _len) {
sasprintf(&config.bar_id, "%.*s", (int)_len, val);
return 0; /* Stop parsing */
}
/*
* Parse the received bar configuration list. The only usecase right now is to
* automatically get the first bar id.
*
*/
void parse_get_first_i3bar_config(const unsigned char *json, size_t size) {
yajl_callbacks configs_callbacks = {
.yajl_string = i3bar_config_string_cb,
};
yajl_handle handle = yajl_alloc(&configs_callbacks, NULL, NULL);
yajl_parse(handle, json, size);
yajl_free(handle);
}

View File

@@ -24,14 +24,24 @@ ev_io *i3_connection;
const char *sock_path;
typedef void (*handler_t)(char *);
typedef void (*handler_t)(const unsigned char *, size_t);
/*
* Returns true when i3bar is configured to read workspace information from i3
* via JSON over the i3 IPC interface, as opposed to reading workspace
* information from the workspace_command via JSON over stdout.
*
*/
static bool i3_provides_workspaces(void) {
return !config.disable_ws && config.workspace_command == NULL;
}
/*
* Called, when we get a reply to a command from i3.
* Since i3 does not give us much feedback on commands, we do not much
*
*/
static void got_command_reply(char *reply) {
static void got_command_reply(const unsigned char *reply, size_t size) {
/* TODO: Error handling for command replies */
}
@@ -39,9 +49,9 @@ static void got_command_reply(char *reply) {
* Called, when we get a reply with workspaces data
*
*/
static void got_workspace_reply(char *reply) {
static void got_workspace_reply(const unsigned char *reply, size_t size) {
DLOG("Got workspace data!\n");
parse_workspaces_json(reply);
parse_workspaces_json(reply, size);
draw_bars(false);
}
@@ -50,7 +60,7 @@ static void got_workspace_reply(char *reply) {
* Since i3 does not give us much feedback on commands, we do not much
*
*/
static void got_subscribe_reply(char *reply) {
static void got_subscribe_reply(const unsigned char *reply, size_t size) {
DLOG("Got subscribe reply: %s\n", reply);
/* TODO: Error handling for subscribe commands */
}
@@ -59,12 +69,12 @@ static void got_subscribe_reply(char *reply) {
* Called, when we get a reply with outputs data
*
*/
static void got_output_reply(char *reply) {
static void got_output_reply(const unsigned char *reply, size_t size) {
DLOG("Clearing old output configuration...\n");
free_outputs();
DLOG("Parsing outputs JSON...\n");
parse_outputs_json(reply);
parse_outputs_json(reply, size);
DLOG("Reconfiguring windows...\n");
reconfig_windows(false);
@@ -73,8 +83,19 @@ static void got_output_reply(char *reply) {
kick_tray_clients(o_walk);
}
if (!config.disable_ws) {
if (i3_provides_workspaces()) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
} else if (config.workspace_command) {
/* Communication with the workspace child is one-way. Since we called
* free_outputs() and free_workspaces() we have lost our workspace
* information which will result in no workspace buttons. A
* well-behaving client should be subscribed to output events as well
* and re-send the output information to i3bar. Even in that case
* though there is a race condition where the child can send the new
* workspace information after the output change before i3bar receives
* the output event from i3. For this reason, we re-parse the latest
* received JSON. */
repeat_last_ws_json();
}
draw_bars(false);
@@ -84,7 +105,21 @@ static void got_output_reply(char *reply) {
* Called when we get the configuration for our bar instance
*
*/
static void got_bar_config(char *reply) {
static void got_bar_config(const unsigned char *reply, size_t size) {
if (!config.bar_id) {
DLOG("Received bar list \"%s\"\n", reply);
parse_get_first_i3bar_config(reply, size);
if (!config.bar_id) {
ELOG("No bar configuration found, please configure a bar block in your i3 config file.\n");
exit(EXIT_FAILURE);
}
LOG("Using first bar config: %s. Use --bar_id to manually select a different bar configuration.\n", config.bar_id);
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG, config.bar_id);
return;
}
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-
@@ -92,13 +127,14 @@ static void got_bar_config(char *reply) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
free_colors(&(config.colors));
parse_config_json(reply);
parse_config_json(reply, size);
/* Now we can actually use 'config', so let's subscribe to the appropriate
* events and request the workspaces if necessary. */
subscribe_events();
if (!config.disable_ws)
if (i3_provides_workspaces()) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
}
/* Initialize the rest of XCB */
init_xcb_late(config.fontname);
@@ -107,6 +143,7 @@ static void got_bar_config(char *reply) {
init_colors(&(config.colors));
start_child(config.command);
start_ws_child(config.workspace_command);
}
/* Data structure to easily call the reply handlers later */
@@ -129,7 +166,7 @@ handler_t reply_handlers[] = {
* Called, when a workspace event arrives (i.e. the user changed the workspace)
*
*/
static void got_workspace_event(char *event) {
static void got_workspace_event(const unsigned char *event, size_t size) {
DLOG("Got workspace event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
}
@@ -138,36 +175,46 @@ static void got_workspace_event(char *event) {
* Called, when an output event arrives (i.e. the screen configuration changed)
*
*/
static void got_output_event(char *event) {
static void got_output_event(const unsigned char *event, size_t size) {
DLOG("Got output event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
if (!config.disable_ws) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
}
}
/*
* Called, when a mode event arrives (i3 changed binding mode).
*
*/
static void got_mode_event(char *event) {
static void got_mode_event(const unsigned char *event, size_t size) {
DLOG("Got mode event!\n");
parse_mode_json(event);
parse_mode_json(event, size);
draw_bars(false);
}
static bool strings_differ(char *a, char *b) {
const bool a_null = (a == NULL);
const bool b_null = (b == NULL);
if (a_null != b_null) {
return true;
}
if (a_null && b_null) {
return false;
}
return strcmp(a, b) != 0;
}
/*
* Called, when a barconfig_update event arrives (i.e. i3 changed the bar hidden_state or mode)
*
*/
static void got_bar_config_update(char *event) {
static void got_bar_config_update(const unsigned char *event, size_t size) {
/* check whether this affect this bar instance by checking the bar_id */
char *expected_id;
sasprintf(&expected_id, "\"id\":\"%s\"", config.bar_id);
char *found_id = strstr(event, expected_id);
char *found_id = strstr((const char *)event, expected_id);
FREE(expected_id);
if (found_id == NULL)
if (found_id == NULL) {
return;
}
/* reconfigure the bar based on the current outputs */
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
@@ -176,9 +223,14 @@ static void got_bar_config_update(char *event) {
/* update the configuration with the received settings */
DLOG("Received bar config update \"%s\"\n", event);
char *old_command = config.command ? sstrdup(config.command) : NULL;
char *old_command = config.command;
char *old_workspace_command = config.workspace_command;
config.command = NULL;
config.workspace_command = NULL;
bar_display_mode_t old_mode = config.hide_on_modifier;
parse_config_json(event);
parse_config_json(event, size);
if (old_mode != config.hide_on_modifier) {
reconfig_windows(true);
}
@@ -188,12 +240,21 @@ static void got_bar_config_update(char *event) {
init_colors(&(config.colors));
/* restart status command process */
if (old_command && strcmp(old_command, config.command) != 0) {
if (!status_child_is_alive() || strings_differ(old_command, config.command)) {
kill_child();
clear_statusline(&statusline_head, true);
start_child(config.command);
}
free(old_command);
/* restart workspace command process */
if (!ws_child_is_alive() || strings_differ(old_workspace_command, config.workspace_command)) {
free_workspaces();
kill_ws_child();
start_ws_child(config.workspace_command);
}
free(old_workspace_command);
draw_bars(false);
}
@@ -257,7 +318,7 @@ static void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
/* Now that we know, what to expect, we can start read()ing the rest
* of the message */
char *buffer = smalloc(size + 1);
unsigned char *buffer = smalloc(size + 1);
rec = 0;
while (rec < size) {
@@ -277,10 +338,11 @@ static void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
/* And call the callback (indexed by the type) */
if (type & (1UL << 31)) {
type ^= 1UL << 31;
event_handlers[type](buffer);
event_handlers[type](buffer, size);
} else {
if (reply_handlers[type])
reply_handlers[type](buffer);
if (reply_handlers[type]) {
reply_handlers[type](buffer, size);
}
}
FREE(header);
@@ -313,8 +375,9 @@ int i3_send_msg(uint32_t type, const char *payload) {
memcpy(walk, &type, sizeof(uint32_t));
walk += sizeof(uint32_t);
if (payload != NULL)
strncpy(walk, payload, len);
if (payload != NULL) {
memcpy(walk, payload, len);
}
swrite(i3_connection->fd, buffer, to_write);
@@ -328,13 +391,12 @@ int i3_send_msg(uint32_t type, const char *payload) {
* socket_path must be a valid path to the ipc_socket of i3
*
*/
int init_connection(const char *socket_path) {
void init_connection(const char *socket_path) {
sock_path = socket_path;
int sockfd = ipc_connect(socket_path);
i3_connection = smalloc(sizeof(ev_io));
ev_io_init(i3_connection, &got_data, sockfd, EV_READ);
ev_io_start(main_loop, i3_connection);
return 1;
}
/*
@@ -350,9 +412,9 @@ void destroy_connection(void) {
*
*/
void subscribe_events(void) {
if (config.disable_ws) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\", \"barconfig_update\" ]");
} else {
if (i3_provides_workspaces()) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\", \"barconfig_update\" ]");
} else {
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\", \"barconfig_update\" ]");
}
}

View File

@@ -56,9 +56,9 @@ static char *expand_path(char *path) {
}
static void print_usage(char *elf_name) {
printf("Usage: %s -b bar_id [-s sock_path] [-t] [-h] [-v]\n", elf_name);
printf("Usage: %s [-b bar_id] [-s sock_path] [-t] [-h] [-v] [-V]\n", elf_name);
printf("\n");
printf("-b, --bar_id <bar_id>\tBar ID for which to get the configuration\n");
printf("-b, --bar_id <bar_id>\tBar ID for which to get the configuration, defaults to the first bar from the i3 config\n");
printf("-s, --socket <sock_path>\tConnect to i3 via <sock_path>\n");
printf("-t, --transparency Enable transparency (RGBA colors)\n");
printf("-h, --help Display this help message and exit\n");
@@ -128,17 +128,12 @@ int main(int argc, char **argv) {
break;
default:
print_usage(argv[0]);
exit(EXIT_SUCCESS);
exit(EXIT_FAILURE);
break;
}
}
if (!config.bar_id) {
/* TODO: maybe we want -f which will automatically ask i3 for the first
* configured bar (and error out if there are too many)? */
ELOG("No bar_id passed. Please let i3 start i3bar or specify --bar_id\n");
exit(EXIT_FAILURE);
}
LOG("i3bar version " I3_VERSION "\n");
main_loop = ev_default_loop(0); /* needed in init_xcb_early */
char *atom_sock_path = init_xcb_early();
@@ -166,10 +161,13 @@ int main(int argc, char **argv) {
init_dpi();
init_outputs();
if (init_connection(socket_path)) {
/* Request the bar configuration. When it arrives, we fill the config array. */
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG, config.bar_id);
}
init_connection(socket_path);
/* Request the bar configuration. When it arrives, we fill the config
* array. In case that config.bar_id is empty, we will receive a list of
* available configs and then request the configuration for the first bar.
* See got_bar_config for more. */
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG, config.bar_id);
free(socket_path);
/* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main loop.
@@ -187,12 +185,12 @@ int main(int argc, char **argv) {
ev_signal_start(main_loop, sig_int);
ev_signal_start(main_loop, sig_hup);
atexit(kill_children_at_exit);
/* From here on everything should run smooth for itself, just start listening for
* events. We stop simply stop the event loop, when we are finished */
ev_loop(main_loop, 0);
kill_child();
clean_xcb();
ev_default_destroy();

View File

@@ -16,7 +16,6 @@
/* A datatype to pass through the callbacks to save the state */
struct mode_json_params {
char *json;
char *cur_key;
char *name;
bool pango_markup;
@@ -96,26 +95,17 @@ static yajl_callbacks mode_callbacks = {
};
/*
* Start parsing the received JSON string
* Parse the received JSON string
*
*/
void parse_mode_json(char *json) {
/* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
* JSON in chunks */
void parse_mode_json(const unsigned char *json, size_t size) {
struct mode_json_params params;
mode binding;
params.cur_key = NULL;
params.json = json;
params.mode = &binding;
yajl_handle handle;
yajl_status state;
handle = yajl_alloc(&mode_callbacks, NULL, (void *)&params);
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
yajl_handle handle = yajl_alloc(&mode_callbacks, NULL, (void *)&params);
yajl_status state = yajl_parse(handle, json, size);
/* FIXME: Proper error handling for JSON parsing */
switch (state) {
@@ -129,8 +119,9 @@ void parse_mode_json(char *json) {
}
/* We don't want to indicate default binding mode */
if (strcmp("default", i3string_as_utf8(params.mode->name)) == 0)
if (strcmp("default", i3string_as_utf8(params.mode->name)) == 0) {
I3STRING_FREE(params.mode->name);
}
/* Set the new binding mode */
set_current_mode(&binding);

View File

@@ -18,10 +18,8 @@
/* A datatype to pass through the callbacks to save the state */
struct outputs_json_params {
struct outputs_head *outputs;
i3_output *outputs_walk;
char *cur_key;
char *json;
bool in_rect;
};
@@ -114,8 +112,9 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
errno = 0;
long parsed_num = strtol(copy, &end, 10);
if (errno == 0 &&
(end && *end == '\0'))
(end && *end == '\0')) {
params->outputs_walk->ws = parsed_num;
}
FREE(copy);
FREE(params->cur_key);
@@ -148,7 +147,6 @@ static int outputs_start_map_cb(void *params_) {
new_output->visible = false;
new_output->ws = 0,
new_output->statusline_width = 0;
new_output->statusline_short_text = false;
memset(&new_output->rect, 0, sizeof(rect));
memset(&new_output->bar, 0, sizeof(surface_t));
memset(&new_output->buffer, 0, sizeof(surface_t));
@@ -263,21 +261,17 @@ void init_outputs(void) {
}
/*
* Start parsing the received JSON string
* Parse the received JSON string
*
*/
void parse_outputs_json(char *json) {
void parse_outputs_json(const unsigned char *json, size_t size) {
struct outputs_json_params params;
params.outputs_walk = NULL;
params.cur_key = NULL;
params.json = json;
params.in_rect = false;
yajl_handle handle;
yajl_status state;
handle = yajl_alloc(&outputs_callbacks, NULL, (void *)&params);
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
yajl_handle handle = yajl_alloc(&outputs_callbacks, NULL, (void *)&params);
yajl_status state = yajl_parse(handle, json, size);
/* FIXME: Proper errorhandling for JSON-parsing */
switch (state) {
@@ -291,6 +285,7 @@ void parse_outputs_json(char *json) {
}
yajl_free(handle);
free(params.cur_key);
}
/*
@@ -319,12 +314,14 @@ void free_outputs(void) {
*
*/
i3_output *get_output_by_name(char *name) {
i3_output *walk;
if (name == NULL) {
return NULL;
}
const bool is_primary = !strcasecmp(name, "primary");
i3_output *walk;
SLIST_FOREACH (walk, outputs, slist) {
if (!strcmp(walk->name, name)) {
if ((is_primary && walk->primary) || !strcmp(walk->name, name)) {
break;
}
}

View File

@@ -106,11 +106,13 @@ void parse_json_header(i3bar_child *child, const unsigned char *buffer, int leng
yajl_status state = yajl_parse(handle, buffer, length);
if (state != yajl_status_ok) {
child_init(child);
if (consumed != NULL)
if (consumed != NULL) {
*consumed = 0;
}
} else {
if (consumed != NULL)
if (consumed != NULL) {
*consumed = yajl_get_bytes_consumed(handle);
}
}
yajl_free(handle);

View File

@@ -19,7 +19,8 @@ struct workspaces_json_params {
struct ws_head *workspaces;
i3_ws *workspaces_walk;
char *cur_key;
char *json;
bool need_output;
bool parsing_rect;
};
/*
@@ -71,26 +72,23 @@ static int workspaces_integer_cb(void *params_, long long val) {
return 1;
}
/* rect is unused, so we don't bother to save it */
if (!strcmp(params->cur_key, "x")) {
params->workspaces_walk->rect.x = (int)val;
FREE(params->cur_key);
return 1;
}
if (!strcmp(params->cur_key, "y")) {
params->workspaces_walk->rect.y = (int)val;
FREE(params->cur_key);
return 1;
}
if (!strcmp(params->cur_key, "width")) {
params->workspaces_walk->rect.w = (int)val;
FREE(params->cur_key);
return 1;
}
if (!strcmp(params->cur_key, "height")) {
params->workspaces_walk->rect.h = (int)val;
FREE(params->cur_key);
return 1;
}
@@ -120,8 +118,9 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
size_t offset = strspn(ws_name, ws_num);
/* Also strip off the conventional ws name delimiter */
if (offset && ws_name[offset] == ':')
if (offset && ws_name[offset] == ':') {
offset += 1;
}
if (config.strip_ws_numbers) {
/* Offset may be equal to length, in which case display the number */
@@ -156,15 +155,16 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
sasprintf(&output_name, "%.*s", len, val);
i3_output *target = get_output_by_name(output_name);
i3_ws *ws = params->workspaces_walk;
if (target != NULL) {
params->workspaces_walk->output = target;
TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces,
params->workspaces_walk,
tailq);
ws->output = target;
TAILQ_INSERT_TAIL(ws->output->workspaces, ws, tailq);
}
params->need_output = false;
FREE(output_name);
FREE(params->cur_key);
return 1;
}
@@ -172,28 +172,42 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
}
/*
* We hit the start of a JSON map (rect or a new output)
* We hit the start of a JSON map (rect or a new workspace)
*
*/
static int workspaces_start_map_cb(void *params_) {
struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
i3_ws *new_workspace = NULL;
if (params->cur_key == NULL) {
new_workspace = smalloc(sizeof(i3_ws));
i3_ws *new_workspace = scalloc(1, sizeof(i3_ws));
new_workspace->num = -1;
new_workspace->name = NULL;
new_workspace->visible = 0;
new_workspace->focused = 0;
new_workspace->urgent = 0;
memset(&new_workspace->rect, 0, sizeof(rect));
new_workspace->output = NULL;
params->workspaces_walk = new_workspace;
params->need_output = true;
params->parsing_rect = false;
} else {
params->parsing_rect = true;
}
return 1;
}
static int workspaces_end_map_cb(void *params_) {
struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
i3_ws *ws = params->workspaces_walk;
const bool parsing_rect = params->parsing_rect;
params->parsing_rect = false;
if (parsing_rect || !ws || !ws->name || !params->need_output) {
return 1;
}
ws->output = get_output_by_name("primary");
if (ws->output == NULL) {
ws->output = SLIST_FIRST(outputs);
}
TAILQ_INSERT_TAIL(ws->output->workspaces, ws, tailq);
return 1;
}
@@ -216,43 +230,42 @@ static yajl_callbacks workspaces_callbacks = {
.yajl_integer = workspaces_integer_cb,
.yajl_string = workspaces_string_cb,
.yajl_start_map = workspaces_start_map_cb,
.yajl_end_map = workspaces_end_map_cb,
.yajl_map_key = workspaces_map_key_cb,
};
/*
* Start parsing the received JSON string
* Parse the received JSON string
*
*/
void parse_workspaces_json(char *json) {
/* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
* JSON in chunks */
struct workspaces_json_params params;
void parse_workspaces_json(const unsigned char *json, size_t size) {
free_workspaces();
params.workspaces_walk = NULL;
params.cur_key = NULL;
params.json = json;
yajl_handle handle;
yajl_status state;
handle = yajl_alloc(&workspaces_callbacks, NULL, (void *)&params);
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
struct workspaces_json_params params = {0};
yajl_handle handle = yajl_alloc(&workspaces_callbacks, NULL, (void *)&params);
yajl_status state = yajl_parse(handle, json, size);
/* FIXME: Proper error handling for JSON parsing */
switch (state) {
case yajl_status_ok:
break;
case yajl_status_client_canceled:
case yajl_status_error:
ELOG("Could not parse workspaces reply!\n");
exit(EXIT_FAILURE);
case yajl_status_error: {
unsigned char *err = yajl_get_error(handle, 1, json, size);
ELOG("Could not parse workspaces reply, error:\n%s\njson:---%s---\n", err, json);
yajl_free_error(handle, err);
if (config.workspace_command) {
kill_ws_child();
set_workspace_button_error("Could not parse workspace_command's JSON");
} else {
exit(EXIT_FAILURE);
}
break;
}
}
yajl_free(handle);
FREE(params.cur_key);
}
@@ -261,14 +274,14 @@ void parse_workspaces_json(char *json) {
*
*/
void free_workspaces(void) {
i3_output *outputs_walk;
if (outputs == NULL) {
return;
}
i3_ws *ws_walk;
i3_output *outputs_walk;
SLIST_FOREACH (outputs_walk, outputs, slist) {
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
i3_ws *ws_walk;
TAILQ_FOREACH (ws_walk, outputs_walk->workspaces, tailq) {
I3STRING_FREE(ws_walk->name);
FREE(ws_walk->canonical_name);

View File

@@ -12,6 +12,7 @@
#include <err.h>
#include <ev.h>
#include <i3/ipc.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
@@ -59,7 +60,7 @@ xcb_visualtype_t *visual_type;
uint8_t depth;
xcb_colormap_t colormap;
/* Overall height of the bar (based on font size) */
/* Overall height of the bar */
int bar_height;
/* These are only relevant for XKB, which we only need for grabbing modifiers */
@@ -138,8 +139,9 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
}
static uint32_t get_sep_offset(struct status_block *block) {
if (!block->no_separator && block->sep_block_width > 0)
if (!block->no_separator && block->sep_block_width > 0) {
return block->sep_block_width / 2 + block->sep_block_width % 2;
}
return 0;
}
@@ -147,12 +149,14 @@ static int get_tray_width(struct tc_head *trayclients) {
trayclient *trayclient;
int tray_width = 0;
TAILQ_FOREACH_REVERSE (trayclient, trayclients, tc_head, tailq) {
if (!trayclient->mapped)
if (!trayclient->mapped) {
continue;
}
tray_width += icon_size + logical_px(config.tray_padding);
}
if (tray_width > 0)
if (tray_width > 0) {
tray_width += logical_px(tray_loff_px);
}
return tray_width;
}
@@ -165,8 +169,9 @@ static void draw_separator(i3_output *output, uint32_t x, struct status_block *b
color_t bar_bg = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
uint32_t sep_offset = get_sep_offset(block);
if (TAILQ_NEXT(block, blocks) == NULL || sep_offset == 0)
if (TAILQ_NEXT(block, blocks) == NULL || sep_offset == 0) {
return;
}
uint32_t center_x = x - sep_offset;
if (config.separator_symbol == NULL) {
@@ -180,54 +185,107 @@ static void draw_separator(i3_output *output, uint32_t x, struct status_block *b
/* Draw a custom separator. */
uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2);
draw_util_text(config.separator_symbol, &output->statusline_buffer, sep_fg, bar_bg,
separator_x, logical_px(ws_voff_px), x - separator_x);
separator_x, bar_height / 2 - font.height / 2, x - separator_x);
}
}
static uint32_t predict_statusline_length(bool use_short_text) {
static void predict_block_length(struct status_block *block) {
i3String *text = block->full_text;
struct status_block_render_desc *render = &block->full_render;
if (block->use_short && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
}
if (i3string_get_num_bytes(text) == 0) {
block->render_length = 0;
return;
}
render->width = predict_text_width(text);
if (block->border) {
render->width += logical_px(block->border_left + block->border_right);
}
/* Compute offset and append for text alignment in min_width. */
if (block->min_width <= render->width) {
render->x_offset = 0;
render->x_append = 0;
} else {
uint32_t padding_width = block->min_width - render->width;
switch (block->align) {
case ALIGN_LEFT:
render->x_append = padding_width;
break;
case ALIGN_RIGHT:
render->x_offset = padding_width;
break;
case ALIGN_CENTER:
render->x_offset = padding_width / 2;
render->x_append = padding_width / 2 + padding_width % 2;
break;
}
}
block->render_length = render->width + render->x_offset + render->x_append;
}
static uint32_t predict_statusline_length(void) {
uint32_t width = 0;
struct status_block *block;
TAILQ_FOREACH (block, &statusline_head, blocks) {
i3String *text = block->full_text;
struct status_block_render_desc *render = &block->full_render;
if (use_short_text && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
}
if (i3string_get_num_bytes(text) == 0)
predict_block_length(block);
uint32_t block_width = block->render_length;
if (block_width == 0) {
continue;
render->width = predict_text_width(text);
if (block->border)
render->width += logical_px(block->border_left + block->border_right);
/* Compute offset and append for text aligment in min_width. */
if (block->min_width <= render->width) {
render->x_offset = 0;
render->x_append = 0;
} else {
uint32_t padding_width = block->min_width - render->width;
switch (block->align) {
case ALIGN_LEFT:
render->x_append = padding_width;
break;
case ALIGN_RIGHT:
render->x_offset = padding_width;
break;
case ALIGN_CENTER:
render->x_offset = padding_width / 2;
render->x_append = padding_width / 2 + padding_width % 2;
break;
}
}
width += render->width + render->x_offset + render->x_append;
width += block_width;
/* If this is not the last block, add some pixels for a separator. */
if (TAILQ_NEXT(block, blocks) != NULL)
if (TAILQ_NEXT(block, blocks) != NULL) {
width += block->sep_block_width;
}
}
return width;
}
static uint32_t switch_block_to_short(struct status_block *block) {
/* Skip blocks that have no short form or are already in short form */
if (block->short_text == NULL || block->use_short) {
return 0;
}
uint32_t full = block->render_length;
block->use_short = true;
predict_block_length(block);
return full - block->render_length;
}
static uint32_t adjust_statusline_length(uint32_t max_length) {
uint32_t width = predict_statusline_length();
/* Progressively switch the blocks to short mode */
struct status_block *block;
TAILQ_FOREACH (block, &statusline_head, blocks) {
if (width < max_length) {
break;
}
width -= switch_block_to_short(block);
/* Provide support for representing a single logical block using multiple
* JSON blocks: if one block is shortened, ensure that all other blocks
* with the same name are also shortened such that the entire logical block uses
* the short form text. */
if (block->name) {
struct status_block *other;
TAILQ_FOREACH (other, &statusline_head, blocks) {
if (other->name && !strcmp(other->name, block->name)) {
width -= switch_block_to_short(other);
}
}
}
}
return width;
@@ -236,7 +294,7 @@ static uint32_t predict_statusline_length(bool use_short_text) {
/*
* Redraws the statusline to the output's statusline_buffer
*/
static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors, bool use_short_text) {
static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors) {
struct status_block *block;
color_t bar_color = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
@@ -254,13 +312,14 @@ static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focu
TAILQ_FOREACH (block, &statusline_head, blocks) {
i3String *text = block->full_text;
struct status_block_render_desc *render = &block->full_render;
if (use_short_text && block->short_text != NULL) {
if (block->use_short && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
}
if (i3string_get_num_bytes(text) == 0)
if (i3string_get_num_bytes(text) == 0) {
continue;
}
color_t fg_color;
if (block->urgent) {
@@ -284,10 +343,12 @@ static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focu
border_color = colors.urgent_ws_border;
bg_color = colors.urgent_ws_bg;
} else {
if (block->border)
if (block->border) {
border_color = draw_util_hex_to_color(block->border);
if (block->background)
}
if (block->background) {
bg_color = draw_util_hex_to_color(block->background);
}
}
/* Draw the border. */
@@ -334,7 +395,7 @@ static void hide_bars(void) {
}
xcb_unmap_window(xcb_connection, walk->bar.id);
}
stop_child();
stop_children();
}
/*
@@ -351,7 +412,7 @@ static void unhide_bars(void) {
uint32_t mask;
uint32_t values[5];
cont_child();
cont_children();
SLIST_FOREACH (walk, outputs, slist) {
if (walk->bar.id == XCB_NONE) {
@@ -363,10 +424,11 @@ static void unhide_bars(void) {
XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_STACK_MODE;
values[0] = walk->rect.x;
if (config.position == POS_TOP)
if (config.position == POS_TOP) {
values[1] = walk->rect.y;
else
} else {
values[1] = walk->rect.y + walk->rect.h - bar_height;
}
values[2] = walk->rect.w;
values[3] = bar_height;
values[4] = XCB_STACK_MODE_ABOVE;
@@ -434,8 +496,9 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
static bool execute_custom_command(xcb_keycode_t input_code, bool event_is_release) {
binding_t *binding;
TAILQ_FOREACH (binding, &(config.bindings), bindings) {
if ((binding->input_code != input_code) || (binding->release != event_is_release))
if ((binding->input_code != input_code) || (binding->release != event_is_release)) {
continue;
}
i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, binding->command);
return true;
@@ -454,7 +517,7 @@ static void child_handle_button(xcb_button_press_event_t *event, i3_output *outp
TAILQ_FOREACH (block, &statusline_head, blocks) {
i3String *text;
struct status_block_render_desc *render;
if (output->statusline_short_text && block->short_text != NULL) {
if (block->use_short && block->short_text != NULL) {
text = block->short_text;
render = &block->short_render;
} else {
@@ -500,6 +563,49 @@ static int predict_button_width(int name_width) {
logical_px(config.ws_min_width));
}
static char *quote_workspace_name(const char *in) {
/* To properly handle workspace names with double quotes in them, we need
* to escape the double quotes. We allocate a large enough buffer (twice
* the unescaped size is always enough), then we copy character by
* character. */
const size_t namelen = strlen(in);
const size_t len = namelen + strlen("workspace \"\"") + 1;
char *out = scalloc(2 * len, 1);
memcpy(out, "workspace \"", strlen("workspace \""));
size_t inpos, outpos;
for (inpos = 0, outpos = strlen("workspace \"");
inpos < namelen;
inpos++, outpos++) {
if (in[inpos] == '"' || in[inpos] == '\\') {
out[outpos] = '\\';
outpos++;
}
out[outpos] = in[inpos];
}
out[outpos] = '"';
return out;
}
static void focus_workspace(i3_ws *ws) {
char *buffer = NULL;
if (ws->id != 0) {
/* Workspace ID has higher precedence since the workspace_command is
* allowed to change workspace names as long as it provides a valid ID. */
sasprintf(&buffer, "[con_id=%lld] focus workspace", ws->id);
goto done;
}
if (ws->canonical_name == NULL) {
return;
}
buffer = quote_workspace_name(ws->canonical_name);
done:
i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, buffer);
free(buffer);
}
/*
* Handle a button press event (i.e. a mouse click on one of our bars).
* We determine, whether the click occurred on a workspace button or if the scroll-
@@ -526,19 +632,27 @@ static void handle_button(xcb_button_press_event_t *event) {
/* During button release events, only check for custom commands. */
const bool event_is_release = (event->response_type & ~0x80) == XCB_BUTTON_RELEASE;
int32_t x = event->event_x >= 0 ? event->event_x : 0;
const int x = (event->event_x >= 0 ? event->event_x : 0) - logical_px(config.padding.x);
if (x < 0) {
/* Ignore clicks in padding */
return;
}
int workspace_width = 0;
i3_ws *cur_ws = NULL, *clicked_ws = NULL, *ws_walk;
TAILQ_FOREACH (ws_walk, walk->workspaces, tailq) {
int w = predict_button_width(ws_walk->name_width);
if (x >= workspace_width && x <= workspace_width + w)
if (x >= workspace_width && x <= workspace_width + w) {
clicked_ws = ws_walk;
if (ws_walk->visible)
}
if (ws_walk->visible) {
cur_ws = ws_walk;
}
workspace_width += w;
if (TAILQ_NEXT(ws_walk, tailq) != NULL)
if (TAILQ_NEXT(ws_walk, tailq) != NULL) {
workspace_width += logical_px(ws_spacing_px);
}
}
if (child_want_click_events() && x > workspace_width) {
@@ -583,8 +697,9 @@ static void handle_button(xcb_button_press_event_t *event) {
* If there is no more workspace, dont even send the workspace
* command, otherwise (with workspace auto_back_and_forth) wed end
* up on the wrong workspace. */
if (cur_ws == TAILQ_FIRST(walk->workspaces))
if (cur_ws == TAILQ_FIRST(walk->workspaces)) {
return;
}
cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
break;
@@ -594,8 +709,9 @@ static void handle_button(xcb_button_press_event_t *event) {
* If there is no more workspace, dont even send the workspace
* command, otherwise (with workspace auto_back_and_forth) wed end
* up on the wrong workspace. */
if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head))
if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head)) {
return;
}
cur_ws = TAILQ_NEXT(cur_ws, tailq);
break;
@@ -606,51 +722,23 @@ static void handle_button(xcb_button_press_event_t *event) {
* workspace if it is not already focused */
if (cur_ws == NULL) {
TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) {
if (cur_ws->visible && !cur_ws->focused)
if (cur_ws->visible && !cur_ws->focused) {
break;
}
}
}
/* if there is nothing to focus, we are done */
if (cur_ws == NULL)
if (cur_ws == NULL) {
return;
}
break;
default:
return;
}
/* To properly handle workspace names with double quotes in them, we need
* to escape the double quotes. Unfortunately, thats rather ugly in C: We
* first count the number of double quotes, then we allocate a large enough
* buffer, then we copy character by character. */
int num_quotes = 0;
size_t namelen = 0;
const char *utf8_name = cur_ws->canonical_name;
for (const char *walk = utf8_name; *walk != '\0'; walk++) {
if (*walk == '"' || *walk == '\\')
num_quotes++;
/* While were looping through the name anyway, we can save one
* strlen(). */
namelen++;
}
const size_t len = namelen + strlen("workspace \"\"") + 1;
char *buffer = scalloc(len + num_quotes, 1);
memcpy(buffer, "workspace \"", strlen("workspace \""));
size_t inpos, outpos;
for (inpos = 0, outpos = strlen("workspace \"");
inpos < namelen;
inpos++, outpos++) {
if (utf8_name[inpos] == '"' || utf8_name[inpos] == '\\') {
buffer[outpos] = '\\';
outpos++;
}
buffer[outpos] = utf8_name[inpos];
}
buffer[outpos] = '"';
i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, buffer);
free(buffer);
focus_workspace(cur_ws);
}
/*
@@ -674,9 +762,9 @@ static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
}
if (num_visible == 0) {
stop_child();
stop_children();
} else {
cont_child();
cont_children();
}
}
@@ -872,8 +960,9 @@ static void handle_client_message(xcb_client_message_event_t *event) {
DLOG("xembed flags = %d\n", xembed[1]);
map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED);
xe_version = xembed[0];
if (xe_version > 1)
if (xe_version > 1) {
xe_version = 1;
}
free(xembedr);
} else {
ELOG("Window %08x violates the XEMBED protocol, _XEMBED_INFO not set\n", client);
@@ -891,8 +980,9 @@ static void handle_client_message(xcb_client_message_event_t *event) {
output_for_tray->bar.id,
output_for_tray->rect.w - icon_size - logical_px(config.tray_padding),
logical_px(config.tray_padding));
if (xcb_request_failed(rcookie, "Could not reparent window. Maybe it is using an incorrect depth/visual?"))
if (xcb_request_failed(rcookie, "Could not reparent window. Maybe it is using an incorrect depth/visual?")) {
return;
}
/* We reconfigure the window to use a reasonable size. The systray
* specification explicitly says:
@@ -1091,17 +1181,20 @@ static void handle_configuration_change(xcb_window_t window) {
trayclient *trayclient;
i3_output *output;
SLIST_FOREACH (output, outputs, slist) {
if (!output->active)
if (!output->active) {
continue;
}
int clients = 0;
TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) {
if (!trayclient->mapped)
if (!trayclient->mapped) {
continue;
}
clients++;
if (trayclient->win != window)
if (trayclient->win != window) {
continue;
}
xcb_rectangle_t rect;
rect.x = output->rect.w - (clients * (icon_size + logical_px(config.tray_padding)));
@@ -1348,23 +1441,45 @@ static void deregister_xkb_keyevents(void) {
*
*/
void init_xcb_late(char *fontname) {
if (fontname == NULL)
if (fontname == NULL) {
fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
}
/* Load the font */
font = load_font(fontname, true);
set_font(&font);
DLOG("Calculated font height: %d\n", font.height);
bar_height = font.height + 2 * logical_px(ws_voff_px);
DLOG("Calculated font-height: %d\n", font.height);
/*
* If the bar height was explicitly set (but padding was not set), use
* it. Otherwise, calculate it based on the font size.
*/
const int default_px = font.height + 2 * logical_px(ws_voff_px);
int padding_scaled =
logical_px(config.padding.y) +
logical_px(config.padding.height);
if (config.bar_height > 0 &&
config.padding.x == 0 &&
config.padding.y == 0 &&
config.padding.width == 0 &&
config.padding.height == 0) {
padding_scaled = config.bar_height - default_px;
DLOG("setting padding_scaled=%d based on bar_height=%d\n", padding_scaled, config.bar_height);
} else {
DLOG("padding: x=%d, y=%d -> padding_scaled=%d\n", config.padding.x, config.padding.y, padding_scaled);
}
bar_height = default_px + padding_scaled;
icon_size = bar_height - 2 * logical_px(config.tray_padding);
if (config.separator_symbol)
if (config.separator_symbol) {
separator_symbol_width = predict_text_width(config.separator_symbol);
}
xcb_flush(xcb_connection);
if (config.hide_on_modifier == M_HIDE)
if (config.hide_on_modifier == M_HIDE) {
register_xkb_keyevents();
}
}
/*
@@ -1400,11 +1515,15 @@ static void send_tray_clientmessage(void) {
static void init_tray(void) {
DLOG("Initializing system tray functionality\n");
/* request the tray manager atom for the X11 display we are running on */
char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
snprintf(atomname, strlen("_NET_SYSTEM_TRAY_S") + 11, "_NET_SYSTEM_TRAY_S%d", screen);
/* The following line cannot use strlen as that makes compilation fail with
* some versions of clang (-Wgnu-folding-constant): */
const size_t systray_len = strlen("_NET_SYSTEM_TRAY_S") + 11;
char atomname[systray_len];
snprintf(atomname, systray_len, "_NET_SYSTEM_TRAY_S%d", screen);
xcb_intern_atom_cookie_t tray_cookie;
if (tray_reply == NULL)
if (tray_reply == NULL) {
tray_cookie = xcb_intern_atom(xcb_connection, 0, strlen(atomname), atomname);
}
/* tray support: we need a window to own the selection */
selwin = xcb_generate_id(xcb_connection);
@@ -1532,7 +1651,6 @@ void clean_xcb(void) {
free_font();
xcb_free_cursor(xcb_connection, cursor);
xcb_flush(xcb_connection);
xcb_aux_sync(xcb_connection);
xcb_disconnect(xcb_connection);
@@ -1572,8 +1690,9 @@ void get_atoms(void) {
*
*/
void kick_tray_clients(i3_output *output) {
if (TAILQ_EMPTY(output->trayclients))
if (TAILQ_EMPTY(output->trayclients)) {
return;
}
trayclient *trayclient;
while (!TAILQ_EMPTY(output->trayclients)) {
@@ -1868,10 +1987,11 @@ void reconfig_windows(bool redraw_bars) {
XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_STACK_MODE;
values[0] = walk->rect.x;
if (config.position == POS_TOP)
if (config.position == POS_TOP) {
values[1] = walk->rect.y;
else
} else {
values[1] = walk->rect.y + walk->rect.h - bar_height;
}
values[2] = walk->rect.w;
values[3] = bar_height;
values[4] = XCB_STACK_MODE_ABOVE;
@@ -1927,10 +2047,10 @@ void reconfig_windows(bool redraw_bars) {
/* Unmap the window, and draw it again when in dock mode */
umap_cookie = xcb_unmap_window_checked(xcb_connection, walk->bar.id);
if (config.hide_on_modifier == M_DOCK) {
cont_child();
cont_children();
map_cookie = xcb_map_window_checked(xcb_connection, walk->bar.id);
} else {
stop_child();
stop_children();
}
if (config.hide_on_modifier == M_HIDE) {
@@ -1974,7 +2094,7 @@ void reconfig_windows(bool redraw_bars) {
*/
static void draw_button(surface_t *surface, color_t fg_color, color_t bg_color, color_t border_color,
int x, int width, int text_width, i3String *text) {
int height = font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1);
int height = bar_height - 2 * logical_px(1);
/* Draw the border of the button. */
draw_util_rectangle(surface, border_color, x, logical_px(1), width, height);
@@ -1984,7 +2104,7 @@ static void draw_button(surface_t *surface, color_t fg_color, color_t bg_color,
width - 2 * logical_px(1), height - 2 * logical_px(1));
draw_util_text(text, surface, fg_color, bg_color, x + (width - text_width) / 2,
logical_px(ws_voff_px), text_width);
bar_height / 2 - font.height / 2, text_width);
}
/*
@@ -1994,12 +2114,9 @@ static void draw_button(surface_t *surface, color_t fg_color, color_t bg_color,
void draw_bars(bool unhide) {
DLOG("Drawing bars...\n");
uint32_t full_statusline_width = predict_statusline_length(false);
uint32_t short_statusline_width = predict_statusline_length(true);
i3_output *outputs_walk;
SLIST_FOREACH (outputs_walk, outputs, slist) {
int workspace_width = 0;
int workspace_width = logical_px(config.padding.x);
if (!outputs_walk->active) {
DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
@@ -2047,8 +2164,9 @@ void draw_bars(bool unhide) {
workspace_width, w, ws_walk->name_width, ws_walk->name);
workspace_width += w;
if (TAILQ_NEXT(ws_walk, tailq) != NULL)
if (TAILQ_NEXT(ws_walk, tailq) != NULL) {
workspace_width += logical_px(ws_spacing_px);
}
}
}
@@ -2070,26 +2188,28 @@ void draw_bars(bool unhide) {
uint32_t hoff = logical_px(((workspace_width > 0) + (tray_width > 0)) * sb_hoff_px);
uint32_t max_statusline_width = outputs_walk->rect.w - workspace_width - tray_width - hoff;
uint32_t clip_left = 0;
uint32_t statusline_width = full_statusline_width;
bool use_short_text = false;
/* Reset short mode between outputs */
struct status_block *block;
TAILQ_FOREACH (block, &statusline_head, blocks) {
block->use_short = false;
}
uint32_t statusline_width = adjust_statusline_length(max_statusline_width);
if (statusline_width > max_statusline_width) {
statusline_width = short_statusline_width;
use_short_text = true;
if (statusline_width > max_statusline_width) {
clip_left = statusline_width - max_statusline_width;
}
clip_left = statusline_width - max_statusline_width;
}
int16_t visible_statusline_width = MIN(statusline_width, max_statusline_width);
int x_dest = outputs_walk->rect.w - tray_width - logical_px((tray_width > 0) * sb_hoff_px) - visible_statusline_width;
x_dest -= logical_px(config.padding.width);
draw_statusline(outputs_walk, clip_left, use_focus_colors, use_short_text);
draw_statusline(outputs_walk, clip_left, use_focus_colors);
draw_util_copy_surface(&outputs_walk->statusline_buffer, &outputs_walk->buffer, 0, 0,
x_dest, 0, visible_statusline_width, (int16_t)bar_height);
outputs_walk->statusline_width = statusline_width;
outputs_walk->statusline_short_text = use_short_text;
}
}

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* This header file includes all relevant files of i3 and the most often used
@@ -46,6 +46,7 @@
#include "click.h"
#include "key_press.h"
#include "floating.h"
#include "gaps.h"
#include "drag.h"
#include "configuration.h"
#include "handlers.h"
@@ -56,9 +57,9 @@
#include "render.h"
#include "window.h"
#include "match.h"
#include "cmdparse.h"
#include "xcursor.h"
#include "resize.h"
#include "tiling_drag.h"
#include "sighandler.h"
#include "move.h"
#include "output.h"

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* assignments.c: Assignments for specific windows (for_window).

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* bindings.h: Functions for configuring, finding, and running bindings.

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* click.c: Button press (mouse click) events.

View File

@@ -1,14 +0,0 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* cmdparse.y: the parser for commands you send to i3 (or bind on keys)
*
*/
#pragma once
#include <config.h>
char *parse_cmd(const char *new);

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* commands.c: all command functions (see commands_parser.c)
@@ -138,7 +138,7 @@ void cmd_mode(I3_CMD, const char *mode);
* Implementation of 'move [window|container] [to] output <str>'.
*
*/
void cmd_move_con_to_output(I3_CMD, const char *name);
void cmd_move_con_to_output(I3_CMD, const char *name, bool move_workspace);
/**
* Implementation of 'move [window|container] [to] mark <str>'.
@@ -152,12 +152,6 @@ void cmd_move_con_to_mark(I3_CMD, const char *mark);
*/
void cmd_floating(I3_CMD, const char *floating_mode);
/**
* Implementation of 'move workspace to [output] <str>'.
*
*/
void cmd_move_workspace_to_output(I3_CMD, const char *name);
/**
* Implementation of 'split v|h|t|vertical|horizontal|toggle'.
*
@@ -204,7 +198,7 @@ void cmd_focus_level(I3_CMD, const char *level);
* Implementation of 'focus'.
*
*/
void cmd_focus(I3_CMD);
void cmd_focus(I3_CMD, bool focus_workspace);
/**
* Implementation of 'fullscreen [enable|disable|toggle] [global]'.
@@ -337,3 +331,15 @@ void cmd_shmlog(I3_CMD, const char *argument);
*
*/
void cmd_debuglog(I3_CMD, const char *argument);
/**
* Implementation of 'gaps inner|outer|top|right|bottom|left|horizontal|vertical current|all set|plus|minus|toggle <px>'
*
*/
void cmd_gaps(I3_CMD, const char *type, const char *scope, const char *mode, const char *value);
/**
* Implementation of 'title_window_icon <yes|no|toggle>' and 'title_window_icon <padding|toggle> <px>'
*
*/
void cmd_title_window_icon(I3_CMD, const char *enable, int padding);

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* commands.c: all command functions (see commands_parser.c)
@@ -14,7 +14,7 @@
#include <yajl/yajl_gen.h>
/**
* Holds an intermediate represenation of the result of a call to any command.
* Holds an intermediate representation 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

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* con.c: Functions which deal with containers directly (creating containers,
@@ -83,6 +83,22 @@ bool con_is_split(Con *con);
*/
bool con_is_hidden(Con *con);
/**
* Returns true if the container is maximized in the given orientation.
*
* If the container is floating or fullscreen, it is not considered maximized.
* Otherwise, it is maximized if it doesn't share space with any other
* container in the given orientation. For example, if a workspace contains
* a single splitv container with three children, none of them are considered
* vertically maximized, but they are all considered horizontally maximized.
*
* Passing "maximized" hints to the application can help it make the right
* choices about how to draw its borders. See discussion in
* https://github.com/i3/i3/pull/2380.
*
*/
bool con_is_maximized(Con *con, orientation_t orientation);
/**
* Returns whether the container or any of its children is sticky.
*
@@ -208,6 +224,14 @@ Con *con_by_frame_id(xcb_window_t frame);
*/
Con *con_by_mark(const char *mark);
/**
* Start from a container and traverse the transient_for linked list. Returns
* true if target window is found in the list. Protects againsts potential
* cycles.
*
*/
bool con_find_transient_for_window(Con *start, xcb_window_t target);
/**
* Returns true if and only if the given containers holds the mark.
*
@@ -363,6 +387,7 @@ void con_move_to_output(Con *con, Output *output, bool fix_coordinates);
*/
bool con_move_to_output_name(Con *con, const char *name, bool fix_coordinates);
bool con_move_to_target(Con *con, Con *target);
/**
* Moves the given container to the given mark.
*
@@ -411,6 +436,14 @@ Con *con_descend_tiling_focused(Con *con);
*/
Con *con_descend_direction(Con *con, direction_t direction);
/**
* Returns whether the window decoration (title bar) should be drawn into the
* X11 frame window of this container (default) or into the X11 frame window of
* the parent container (for stacked/tabbed containers).
*
*/
bool con_draw_decoration_into_frame(Con *con);
/**
* Returns a "relative" Rect which contains the amount of pixels that need to
* be added to the original Rect to get the final position (obviously the
@@ -442,7 +475,7 @@ int con_border_style(Con *con);
* floating window.
*
*/
void con_set_border_style(Con *con, int border_style, int border_width);
void con_set_border_style(Con *con, border_style_t border_style, int border_width);
/**
* This function changes the layout of a given container. Use it to handle
@@ -548,3 +581,9 @@ uint32_t con_rect_size_in_orientation(Con *con);
*
*/
void con_merge_into(Con *old, Con *new);
/**
* Returns true if the container is within any stacked/tabbed split container.
*
*/
bool con_inside_stacked_or_tabbed(Con *con);

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* config_directives.h: all config storing functions (see config_parser.c)
@@ -39,9 +39,13 @@ CFGFUN(criteria_init, int _state);
CFGFUN(criteria_add, const char *ctype, const char *cvalue);
CFGFUN(criteria_pop_state);
CFGFUN(include, const char *pattern);
CFGFUN(font, const char *font);
CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command);
CFGFUN(for_window, const char *command);
CFGFUN(gaps, const char *workspace, const char *type, const long value);
CFGFUN(smart_borders, const char *enable);
CFGFUN(smart_gaps, const char *enable);
CFGFUN(floating_minimum_size, const long width, const long height);
CFGFUN(floating_maximum_size, const long width, const long height);
CFGFUN(default_orientation, const char *orientation);
@@ -64,6 +68,8 @@ 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(tiling_drag, const char *value);
CFGFUN(tiling_drag_swap_modifier, const char *modifiers);
CFGFUN(restart_state, const char *path);
CFGFUN(popup_during_fullscreen, const char *value);
CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator, const char *child_border);
@@ -83,6 +89,11 @@ 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_height, const long height);
CFGFUN(bar_padding_one, const long all);
CFGFUN(bar_padding_two, const long top_and_bottom, const long right_and_left);
CFGFUN(bar_padding_three, const long top, const long right_and_left, const long bottom);
CFGFUN(bar_padding_four, const long top, const long right, const long bottom, const long left);
CFGFUN(bar_modifier, const char *modifiers);
CFGFUN(bar_wheel_up_cmd, const char *command);
CFGFUN(bar_wheel_down_cmd, const char *command);
@@ -95,6 +106,7 @@ CFGFUN(bar_tray_output, const char *output);
CFGFUN(bar_tray_padding, const long spacing_px);
CFGFUN(bar_color_single, const char *colorclass, const char *color);
CFGFUN(bar_status_command, const char *command);
CFGFUN(bar_workspace_command, const char *command);
CFGFUN(bar_binding_mode_indicator, const char *value);
CFGFUN(bar_workspace_buttons, const char *value);
CFGFUN(bar_workspace_min_width, const long width);

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* config_parser.h: config parser-related definitions
@@ -16,29 +16,84 @@
SLIST_HEAD(variables_head, Variable);
extern pid_t config_error_nagbar_pid;
struct stack_entry {
/* Just a pointer, not dynamically allocated. */
const char *identifier;
enum {
STACK_STR = 0,
STACK_LONG = 1,
} type;
union {
char *str;
long num;
} val;
};
struct stack {
struct stack_entry stack[10];
};
struct parser_ctx {
bool use_nagbar;
int state;
Match current_match;
/* A list which contains the states that lead to the current state, e.g.
* INITIAL, WORKSPACE_LAYOUT.
* When jumping back to INITIAL, statelist_idx will simply be set to 1
* (likewise for other states, e.g. MODE or BAR).
* This list is used to process the nearest error token. */
int statelist[10];
/* NB: statelist_idx points to where the next entry will be inserted */
int statelist_idx;
/*******************************************************************************
* The (small) stack where identified literals are stored during the parsing
* of a single config directive (like $workspace).
******************************************************************************/
struct stack *stack;
struct variables_head variables;
bool has_errors;
};
/**
* An intermediate reprsentation of the result of a parse_config call.
* An intermediate representation 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.
*
*/
struct ConfigResultIR {
/* The JSON generator to append a reply to. */
yajl_gen json_gen;
struct parser_ctx *ctx;
/* The next state to transition to. Passed to the function so that we can
* determine the next state as a result of a function call, like
* cfg_criteria_pop_state() does. */
int next_state;
};
struct ConfigResultIR *parse_config(const char *input, struct context *context);
/* Whether any error happened while processing this config directive. */
bool has_errors;
};
/**
* launch nagbar to indicate errors in the configuration file.
*/
void start_config_error_nagbar(const char *configpath, bool has_errors);
/**
* Releases the memory of all variables in ctx.
*
*/
void free_variables(struct parser_ctx *ctx);
typedef enum {
PARSE_FILE_FAILED = -1,
PARSE_FILE_SUCCESS = 0,
PARSE_FILE_CONFIG_ERRORS = 1,
} parse_file_result_t;
/**
* Parses the given file by first replacing the variables, then calling
* parse_config and launching i3-nagbar if use_nagbar is true.
@@ -47,4 +102,4 @@ void start_config_error_nagbar(const char *configpath, bool has_errors);
* parsing.
*
*/
bool parse_file(const char *f, bool use_nagbar);
parse_file_result_t parse_file(struct parser_ctx *ctx, const char *f, IncludedFile *included_file);

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* include/configuration.h: Contains all structs/variables for the configurable
@@ -14,7 +14,9 @@
#include "queue.h"
#include "i3.h"
#include "tiling_drag.h"
typedef struct IncludedFile IncludedFile;
typedef struct Config Config;
typedef struct Barconfig Barconfig;
extern char *current_configpath;
@@ -22,6 +24,7 @@ extern char *current_config;
extern Config config;
extern SLIST_HEAD(modes_head, Mode) modes;
extern TAILQ_HEAD(barconfig_head, Barconfig) barconfigs;
extern TAILQ_HEAD(includedfiles_head, IncludedFile) included_files;
/**
* Used during the config file lexing/parsing to keep the state of the lexer
@@ -69,6 +72,18 @@ struct Variable {
SLIST_ENTRY(Variable) variables;
};
/**
* List entry struct for an included file.
*
*/
struct IncludedFile {
char *path;
char *raw_contents;
char *variable_replaced_contents;
TAILQ_ENTRY(IncludedFile) files;
};
/**
* The configuration file can contain multiple sets of bindings. Apart from the
* default set (name == "default"), you can specify other sets and change the
@@ -212,6 +227,9 @@ struct Config {
/** The modifier which needs to be pressed in combination with your mouse
* buttons to do things with floating windows (move, resize) */
uint32_t floating_modifier;
/** The modifier which needs to be pressed in combination with the floating
* modifier and your mouse buttons to swap containers during tiling drag */
uint32_t swap_modifier;
/** Maximum and minimum dimensions of a floating window */
int32_t floating_maximum_width;
@@ -224,9 +242,11 @@ struct Config {
color_t background;
struct Colortriple focused;
struct Colortriple focused_inactive;
struct Colortriple focused_tab_title;
struct Colortriple unfocused;
struct Colortriple urgent;
struct Colortriple placeholder;
bool got_focused_tab_title;
} client;
struct config_bar {
struct Colortriple focused;
@@ -245,10 +265,21 @@ struct Config {
/* just ignore the popup, that is, dont map it */
PDF_IGNORE = 2,
/* display all floating windows */
PDF_ALL = 3,
} popup_during_fullscreen;
/* The number of currently parsed barconfigs */
int number_barconfigs;
tiling_drag_t tiling_drag;
/* Gap sizes */
gaps_t gaps;
/* Disable gaps if there is only one container on the workspace */
smart_gaps_t smart_gaps;
};
/**
@@ -307,6 +338,10 @@ struct Barconfig {
* Will be passed to the shell. */
char *status_command;
/** Command that should be run to get the workspace buttons. Will be passed
* to the shell. */
char *workspace_command;
/** Font specification for all text rendered on the bar. */
char *font;
@@ -336,6 +371,11 @@ struct Barconfig {
/** Enable verbose mode? Useful for debugging purposes. */
bool verbose;
/** Defines the height of the bar in pixels. */
uint32_t bar_height;
struct Rect padding;
struct bar_colors {
char *background;
char *statusline;

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* include/data.h: This file defines all data structures used by i3
@@ -9,12 +9,15 @@
*/
#pragma once
#define PCRE2_CODE_UNIT_WIDTH 8
#define SN_API_NOT_YET_FROZEN 1
#include <libsn/sn-launcher.h>
#include <xcb/randr.h>
#include <pcre.h>
#include <pcre2.h>
#include <sys/time.h>
#include <cairo/cairo.h>
#include "queue.h"
@@ -44,6 +47,7 @@ typedef struct Con Con;
typedef struct Match Match;
typedef struct Assignment Assignment;
typedef struct Window i3Window;
typedef struct gaps_t gaps_t;
typedef struct mark_t mark_t;
/******************************************************************************
@@ -58,9 +62,11 @@ typedef enum { NO_ORIENTATION = 0,
VERT } orientation_t;
typedef enum { BEFORE,
AFTER } position_t;
typedef enum { BS_NORMAL = 0,
BS_NONE = 1,
BS_PIXEL = 2 } border_style_t;
typedef enum {
BS_NONE = 0,
BS_PIXEL = 1,
BS_NORMAL = 2,
} border_style_t;
/** parameter to specify whether tree_close_internal() and x_window_kill() should kill
* only this specific window or the whole X11 client */
@@ -75,11 +81,16 @@ typedef enum { ADJ_NONE = 0,
ADJ_UPPER_SCREEN_EDGE = (1 << 2),
ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
typedef enum { SMART_GAPS_OFF,
SMART_GAPS_ON,
SMART_GAPS_INVERSE_OUTER } smart_gaps_t;
typedef enum { HEBM_NONE = ADJ_NONE,
HEBM_VERTICAL = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE,
HEBM_HORIZONTAL = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE,
HEBM_BOTH = HEBM_VERTICAL | HEBM_HORIZONTAL,
HEBM_SMART = (1 << 5) } hide_edge_borders_mode_t;
HEBM_SMART = (1 << 5),
HEBM_SMART_NO_GAPS = (1 << 6) } hide_edge_borders_mode_t;
typedef enum { MM_REPLACE,
MM_ADD } mark_mode_t;
@@ -132,6 +143,25 @@ typedef enum {
POINTER_WARPING_NONE = 1
} warping_t;
struct gaps_t {
int inner;
int top;
int right;
int bottom;
int left;
};
typedef enum {
GAPS_INNER = (1 << 0),
GAPS_TOP = (1 << 1),
GAPS_RIGHT = (1 << 2),
GAPS_BOTTOM = (1 << 3),
GAPS_LEFT = (1 << 4),
GAPS_VERTICAL = (GAPS_TOP | GAPS_BOTTOM),
GAPS_HORIZONTAL = (GAPS_RIGHT | GAPS_LEFT),
GAPS_OUTER = (GAPS_VERTICAL | GAPS_HORIZONTAL),
} gaps_mask_t;
/**
* Focus wrapping modes.
*/
@@ -199,12 +229,14 @@ struct deco_render_params {
};
/**
* Stores which workspace (by name or number) goes to which output.
* Stores which workspace (by name or number) goes to which output and its gaps config.
*
*/
struct Workspace_Assignment {
char *name;
char *output;
gaps_t gaps;
gaps_mask_t gaps_mask;
TAILQ_ENTRY(Workspace_Assignment) ws_assignments;
};
@@ -247,8 +279,7 @@ struct Startup_Sequence {
*/
struct regex {
char *pattern;
pcre *regex;
pcre_extra *extra;
pcre2_code *regex;
};
/**
@@ -414,6 +445,9 @@ struct Window {
* for_window. */
char *role;
/** WM_CLIENT_MACHINE of the window */
char *machine;
/** Flag to force re-rendering the decoration upon changes */
bool name_x_changed;
@@ -468,6 +502,9 @@ struct Window {
double min_aspect_ratio;
double max_aspect_ratio;
/** Window icon, as Cairo surface */
cairo_surface_t *icon;
/** The window has a nonrectangular shape. */
bool shaped;
/** The window has a nonrectangular input shape. */
@@ -500,6 +537,7 @@ struct Match {
struct regex *mark;
struct regex *window_role;
struct regex *workspace;
struct regex *machine;
xcb_atom_t window_type;
enum {
U_DONTCHECK = -1,
@@ -522,6 +560,7 @@ struct Match {
WM_FLOATING_USER,
WM_FLOATING } window_mode;
Con *con_id;
bool match_all_windows;
/* Where the window looking for a match should be inserted:
*
@@ -633,6 +672,9 @@ struct Con {
* workspace is not a named workspace (for named workspaces, num == -1) */
int num;
/** Only applicable for containers of type CT_WORKSPACE. */
gaps_t gaps;
struct Con *parent;
/* The position and size for this con. These coordinates are absolute. Note
@@ -652,6 +694,11 @@ struct Con {
/** The format with which the window's name should be displayed. */
char *title_format;
/** Whether the window icon should be displayed, and with what padding. -1
* means display no window icon (default behavior), 0 means display without
* any padding, 1 means display with 1 pixel of padding and so on. */
int window_icon_padding;
/* a sticky-group is an identifier which bundles several containers to a
* group. The contents are shared between all of them, that is they are
* displayed on whichever of the containers is currently visible */
@@ -706,7 +753,16 @@ struct Con {
* layout in workspace_layout and creates a new split container with that
* layout whenever a new container is attached to the workspace. */
layout_t layout, last_split_layout, workspace_layout;
border_style_t border_style;
/* When the border style of a con changes because of motif hints, we don't
* want to set more decoration that the user wants. The user's preference is determined by these:
* 1. For new tiling windows, as set by `default_border`
* 2. For new floating windows, as set by `default_floating_border`
* 3. For all windows that the user runs the `border` command, whatever is
* the result of that command for that window. */
border_style_t max_user_border_style;
/** floating? (= not in tiling layout) This cannot be simply a bool
* because we want to keep track of whether the status was set by the
* application (by setting _NET_WM_WINDOW_TYPE appropriately) or by the

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* display_version.c: displays the running i3 version, runs as part of
@@ -14,7 +14,7 @@
/**
* Connects to i3 to find out the currently running version. Useful since it
* might be different from the version compiled into this binary (maybe the
* user didnt correctly install i3 or forgot te restart it).
* user didnt correctly install i3 or forgot to restart it).
*
* The output looks like this:
* Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804)

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* drag.c: click and drag.

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* ewmh.c: Get/set certain EWMH properties easily.

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* Faking outputs is useful in pathological situations (like network X servers

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* floating.c: Floating windows.

41
include/gaps.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
*/
#pragma once
#include <stdbool.h>
#include "data.h"
/**
* Calculates the effective gap sizes for a container.
*/
gaps_t calculate_effective_gaps(Con *con);
/*
* Decides whether the container should be inset.
*/
bool gaps_should_inset_con(Con *con, int children);
/*
* Returns whether the given container has an adjacent container in the
* specified direction. In other words, this returns true if and only if
* the container is not touching the edge of the screen in that direction.
*/
bool gaps_has_adjacent_container(Con *con, direction_t direction);
/**
* Returns the configured gaps for this workspace based on the workspace name,
* number, and configured workspace gap assignments.
*/
gaps_t gaps_for_workspace(Con *ws);
/**
* Re-applies all workspace gap assignments to existing workspaces after
* reloading the configuration file.
*
*/
void gaps_reapply_workspace_assignments(void);

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* handlers.c: Small handlers for various events (keypresses, focus changes,

View File

@@ -11,6 +11,8 @@ xmacro(_NET_WM_STATE_DEMANDS_ATTENTION) \
xmacro(_NET_WM_STATE_MODAL) \
xmacro(_NET_WM_STATE_HIDDEN) \
xmacro(_NET_WM_STATE_FOCUSED) \
xmacro(_NET_WM_STATE_MAXIMIZED_VERT) \
xmacro(_NET_WM_STATE_MAXIMIZED_HORZ) \
xmacro(_NET_WM_STATE) \
xmacro(_NET_WM_WINDOW_TYPE) \
xmacro(_NET_WM_WINDOW_TYPE_NORMAL) \

View File

@@ -3,6 +3,7 @@
xmacro(_NET_WM_USER_TIME) \
xmacro(_NET_STARTUP_ID) \
xmacro(_NET_WORKAREA) \
xmacro(_NET_WM_ICON) \
xmacro(WM_PROTOCOLS) \
xmacro(WM_DELETE_WINDOW) \
xmacro(UTF8_STRING) \
@@ -15,8 +16,10 @@ xmacro(I3_CONFIG_PATH) \
xmacro(I3_SYNC) \
xmacro(I3_SHMLOG_PATH) \
xmacro(I3_PID) \
xmacro(I3_LOG_STREAM_SOCKET_PATH) \
xmacro(I3_FLOATING_WINDOW) \
xmacro(_NET_REQUEST_FRAME_EXTENTS) \
xmacro(_NET_FRAME_EXTENTS) \
xmacro(_MOTIF_WM_HINTS) \
xmacro(WM_CHANGE_STATE)
xmacro(WM_CHANGE_STATE) \
xmacro(MANAGER)

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3.h: global variables that are used all over i3.
@@ -39,6 +39,7 @@ extern bool debug_build;
/** The number of file descriptors passed via socket activation. */
extern int listen_fds;
extern int conn_screen;
extern xcb_atom_t wm_sn;
/**
* The EWMH support window that is used to indicate that an EWMH-compliant
* window manager is present. This window is created when i3 starts and
@@ -55,7 +56,6 @@ extern xcb_timestamp_t last_timestamp;
extern SnDisplay *sndisplay;
extern xcb_key_symbols_t *keysyms;
extern char **start_argv;
extern Display *xlibdpy, *xkbdpy;
extern int xkb_current_group;
extern TAILQ_HEAD(bindings_head, Binding) *bindings;
extern const char *current_binding_mode;

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* This public header defines the different constants and message types to use

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
@@ -80,13 +80,6 @@ void ipc_new_client(EV_P_ struct ev_io *w, int revents);
*/
ipc_client *ipc_new_client_on_fd(EV_P_ int fd);
/**
* Creates the UNIX domain socket at the given path, sets it to non-blocking
* mode, bind()s and listen()s on it.
*
*/
int ipc_create_socket(const char *filename);
/**
* Sends the specified event to all IPC clients which are currently connected
* and subscribed to this kind of event.
@@ -140,7 +133,7 @@ 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);
void ipc_send_binding_event(const char *event_type, Binding *bind, const char *modename);
/**
* Set the maximum duration that we allow for a connection with an unwriteable

View File

@@ -1,7 +1,7 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* i3 - an improved tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* key_press.c: key press handler

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