Merge pull request #4057 from orestisfl/hacking-howto

Update first 1/3 of hacking-howto document
This commit is contained in:
Orestis Floros 2020-05-06 17:30:58 +02:00 committed by GitHub
commit e4629d678e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 175 additions and 177 deletions

View File

@ -38,4 +38,4 @@ Note that bug reports and feature requests for related projects should be filed
* Find a [reproducible bug](https://github.com/i3/i3/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3Areproducible+label%3Abug+) from the issue tracker. These issues have been reviewed and confirmed by a project contributor.
* Find an [accepted enhancement](https://github.com/i3/i3/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3Aaccepted+label%3Aenhancement) from the issue tracker. These have been approved and are ok to start working on.
There's a very good [overview of the codebase](https://i3wm.org/docs/hacking-howto.html) available to get you started.
There's an [overview of the codebase](https://i3wm.org/docs/hacking-howto.html) available to get you started.

19
docs/bigpicture.asy Normal file
View File

@ -0,0 +1,19 @@
import drawtree;
treeLevelStep = 2cm;
TreeNode n94457831379296 = makeNode("``root'' (splith) []");
TreeNode n94457831380944 = makeNode(n94457831379296, "``\_\_i3'' (output) []");
TreeNode n94457831384048 = makeNode(n94457831380944, "``content'' (splith) []");
TreeNode n94457831387184 = makeNode(n94457831384048, "``\_\_i3\_scratch'' (splith) []");
TreeNode n94457831390576 = makeNode(n94457831379296, "``eDP-1'' (output) []");
TreeNode n94457831393744 = makeNode(n94457831390576, "``topdock'' (dockarea) []");
TreeNode n94457831396992 = makeNode(n94457831390576, "``content'' (splith) []");
TreeNode n94457831628304 = makeNode(n94457831396992, "``1'' (splith) []");
TreeNode n94457831571040 = makeNode(n94457831628304, "``Hacking i3: How To - Mozilla Firefox'' (leaf) []");
TreeNode n94457831246384 = makeNode(n94457831628304, "``vim'' (leaf) []");
TreeNode n94457831461088 = makeNode(n94457831396992, "``Named workspace'' (splith) []");
TreeNode n94457831471424 = makeNode(n94457831461088, "``[Empty]'' (tabbed) []");
TreeNode n94457831570576 = makeNode(n94457831471424, "``contrib/dump-asy.pl --no-gv'' (leaf) [Marks go here]");
TreeNode n94457831645488 = makeNode(n94457831471424, "``ipython'' (leaf) []");
TreeNode n94457831400192 = makeNode(n94457831390576, "``bottomdock'' (dockarea) []");
TreeNode n94457831424848 = makeNode(n94457831400192, "``i3bar for output eDP-1'' (leaf) []");
draw(n94457831379296, (0, 0));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

View File

@ -8,6 +8,19 @@ touching i3s source code. It should contain all important information to help
you understand why things are like they are. If it does not mention something
you find necessary, please do not hesitate to contact me.
++++
<div style="background-color:red; color:white; padding:20px;">
<strong style="color:white;">WARNING!</strong>
<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
outdated information.
++++
</p>
</div>
++++
== Building i3
You can build i3 like you build any other software package which uses autotools.
@ -18,60 +31,51 @@ Heres a memory refresher:
$ ../configure
$ make -j8
(The autoreconf -fi step is unnecessary if you are building from a release tarball,
but shouldnt hurt either.)
The autoreconf -fi step is unnecessary if you are building from a release
tarball, but shouldnt hurt either.
=== 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
* 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.
* “make check” runs the i3 testsuite. See docs/testsuite for details.
* +make check+ runs the i3 testsuite. See docs/testsuite for details.
* “make distcheck” (runs testsuite on “make dist” result, tiny bit quicker
* +make distcheck+ (runs testsuite on +make dist+ result, tiny bit quicker
feedback cycle than waiting for the travis build to catch the issue).
* “make uninstall” (occasionally requested by users who compile from source)
* +make uninstall+ (occasionally requested by users who compile from source)
* “make” will build manpages/docs by default if the tools are installed.
* +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.
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.
+--disable-sanitizers+ configure option to turn off all sanitizers, and see
+--help+ for available sanitizers.
* Support for pre-compiled headers (PCH) has been dropped for now in the
interest of simplicity. If you need support for PCH, please open an issue.
* Coverage reports are now generated using +make check-code-coverage+, which
requires specifying +--enable-code-coverage+ when calling configure.
* Coverage reports are now generated using “make check-code-coverage”, which
requires specifying --enable-code-coverage when calling configure.
== Using git / sending patches
For a short introduction into using git, see
https://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy
or, for more documentation, see https://git-scm.com/documentation
== Pull requests
Please talk to us before working on new features to see whether they will be
accepted. A good way for this is to open an issue and asking for opinions on it.
Even for accepted features, this can be a good way to refine an idea upfront. However,
we don't want to see certain features in i3, e.g., switching window focus in an
Alt+Tab like way.
Even for accepted features, this can be a good way to refine an idea upfront.
However, we don't want to see certain features in i3, e.g., switching window
focus in an Alt+Tab like way.
When working on bugfixes, please make sure you mention that you are working on
it in the corresponding bug report at https://github.com/i3/i3/issues. In case
there is no bug report yet, please create one.
When working on bugfixes, please make sure you mention that you are working on it
in the corresponding bug report at https://github.com/i3/i3/issues. In case there
is no bug report yet, please create one.
After you are done, please submit your work for review as a pull request at
https://github.com/i3/i3.
Do not send emails to the mailing list or any author directly, and dont submit
them in the bugtracker, since all reviews should be done in public at
https://github.com/i3/i3. In order to make your review go as fast as possible, you
could have a look at previous reviews and see what the common mistakes are.
https://github.com/i3/i3. In order to make your review go as fast as possible,
you could have a look at previous reviews and see what the common mistakes are.
=== Which branch to use?
@ -82,9 +86,8 @@ repository).
The contents of “master” are always stable. That is, it contains the source code
of the latest release, plus any bugfixes that were applied since that release.
New features are only found in the “next” branch. Therefore, if you are working
on a new feature, use the “next” branch. If you are working on a bugfix, use the
“next” branch, too, but make sure your code also works on “master”.
New features are only found in the “next” branch. Always use this branch when
writing new code (both bugfixes and features).
== Window Managers
@ -106,9 +109,9 @@ In the case of i3, the tasks (and order of them) are the following:
the first client of X) and manage them (reparent them, create window
decorations, etc.)
. When new windows are created, manage them
. Handle the clients `_WM_STATE` property, but only `_WM_STATE_FULLSCREEN` and
`_NET_WM_STATE_DEMANDS_ATTENTION`
. Handle the clients `WM_NAME` property
. Handle the clients +_WM_STATE+ property, but only +_WM_STATE_FULLSCREEN+ and
+_NET_WM_STATE_DEMANDS_ATTENTION+
. Handle the clients +WM_NAME+ property
. Handle the clients size hints to display them proportionally
. Handle the clients urgency hint
. Handle enter notifications (focus follows mouse)
@ -123,11 +126,11 @@ will be discussed.
=== Tiling window managers
Traditionally, there are two approaches to managing windows: The most common
one nowadays is floating, which means the user can freely move/resize the
windows. The other approach is called tiling, which means that your window
manager distributes windows to use as much space as possible while not
overlapping each other.
Traditionally, there are two approaches to managing windows: The most common one
nowadays is stacking (or floating, using i3's terminology), which means the user
can freely move/resize the windows, potentially overlapping them. The other
approach is called tiling, which means that the window manager distributes
windows to use as much space as possible while not overlapping each other.
The idea behind tiling is that you should not need to waste your time
moving/resizing windows while you usually want to get some work done. After
@ -161,63 +164,67 @@ example.
== Files
include/atoms.xmacro::
A file containing all X11 atoms which i3 uses. This file will be included
various times (for defining, requesting and receiving the atoms), each time
with a different definition of xmacro().
i3's source code is in the +src+ folder while header files reside in +include+.
Other tools such as i3bar and i3-nagbar have their own folders. i3 and its tools
share an internal library called ``libi3'' which also has its own folder.
The following list gives an overview of the codebase, explaining the
functionality of the most important, core source code files. Other files in the
tree that are not mentioned here implement specific functionalities: for example,
+src/scratchpad.c+ is obviously about the scratchpad functionality.
include/data.h::
Contains data definitions used by nearly all files. You really need to read
this first.
Contains data definitions used by nearly all files.
include/*.h::
Contains forward definitions for all public functions, as well as
doxygen-compatible comments (so if you want to get a bit more of the big
picture, either browse all header files or use doxygen if you prefer that).
src/config_parser.c::
Contains a custom configuration parser. See src/command_parser.c for rationale
on why we use a custom parser.
src/click.c::
Contains all functions which handle mouse button clicks (right mouse button
clicks initiate resizing and thus are relatively complex).
src/command_parser.c::
Contains a hand-written parser to parse commands (commands are what
you bind on keys and what you can send to i3 using the IPC interface, like
'move left' or 'workspace 4').
src/config_directives.c::
src/commands.c::
Contain the definitions for all high-level config and command directives. These
are excellent places to start with a top-to-bottom approach to understand
specific i3 behavior. For example, if you want to investigate a bug that happens
for the +move to mark+ command, you can use gdb to pause in
+cmd_move_con_to_mark+ and then work your way from there, stepping into
lower-level functions.
src/con.c::
Contains all functions which deal with containers directly (creating
containers, searching containers, getting specific properties from containers,
…).
Contains all functions which deal with containers directly (creating containers,
searching containers, getting specific properties from containers, …). Contains
abstractions and auxiliary functions necessary to work with the container
structure which is used in almost all parts of the codebase.
src/config.c::
Contains all functions handling the configuration file (calling the parser
src/config_parser.c) with the correct path, switching key bindings mode).
src/tree.c::
Contains functions which deal with the tree abstraction. However, be aware that
+src/con.c+ also contains functions that heavily interact with the tree
structure. Some functions that are included in +str/tree.c+ are those that handle
opening and closing containers in the tree, finding the container that should be
focused next and flattening the tree. See also +src/move.c+ for other
move-specific functions that interact with the tree, which were moved into their
own file because they are so long.
src/ewmh.c::
Functions to get/set certain EWMH properties easily.
src/floating.c::
Contains functions for floating mode (mostly resizing/dragging).
src/workspace.c::
Contains functions which deal with workspaces. Includes code that creates new
workspaces, shows existing ones and deals with workspace assignments.
src/handlers.c::
Contains all handlers for all kinds of X events (new window title, new hints,
unmapping, key presses, button presses, …).
unmapping, key presses, button presses, …). This is a very important file to
understand how i3 interacts with changes to its environment.
src/ipc.c::
Contains code for the IPC interface.
src/command_parser.c::
src/config_parser.c::
Contain a hand-written parser to parse commands and configuration (commands are what
you bind on keys and what you can send to i3 using the IPC interface, like
+move left+ or +workspace 4+). +src/config.c+ is responsible for calling the
configuration parser.
src/load_layout.c::
Contains code for loading layouts from JSON files.
src/log.c::
Contains the logging functions.
src/main.c::
Initializes the window manager.
src/click.c::
src/resize.c::
Contain functions which handle mouse button clicks (right mouse button
clicks initiate resizing and thus are relatively complex).
src/manage.c::
Looks at existing or new windows and decides whether to manage them. If so, it
@ -226,93 +233,66 @@ reparents the window and inserts it into our data structures.
src/match.c::
A "match" is a data structure which acts like a mask or expression to match
certain windows or not. For example, when using commands, you can specify a
command like this: [title="*Firefox*"] kill. The title member of the match
command like this: +[title="*Firefox*"] kill+. The title member of the match
data structure will then be filled and i3 will check each window using
match_matches_window() to find the windows affected by this command.
src/move.c::
Contains code to move a container in a specific direction.
src/output.c::
Functions to handle CT_OUTPUT cons.
+match_matches_window()+ to find the windows affected by this command.
src/randr.c::
The RandR API is used to get (and re-query) the configured outputs (monitors,
…).
…). Legacy Xinerama support resides in +src/xinerama.c+.
src/render.c::
Renders the tree data structure by assigning coordinates to every node. These
values will later be pushed to X11 in +src/x.c+.
src/resize.c::
Contains the functions to resize containers.
src/restore_layout.c::
Everything for restored containers that is not pure state parsing (which can be
found in load_layout.c).
src/sighandler.c::
Handles +SIGSEGV+, +SIGABRT+ and +SIGFPE+ by showing a dialog that i3 crashed.
You can chose to let it dump core, to restart it in-place or to restart it
in-place but forget about the layout.
src/tree.c::
Contains functions which open or close containers in the tree, change focus or
cleanup ("flatten") the tree. See also +src/move.c+ for another similar
function, which was moved into its own file because it is so long.
src/util.c::
Contains useful functions which are not really dependent on anything.
You can choose to let it dump core and restart i3 in-place (either trying to
preserve layout or forget about it).
src/window.c::
Handlers to update X11 window properties like +WM_CLASS+, +_NET_WM_NAME+,
+CLIENT_LEADER+, etc.
src/workspace.c::
Contains all functions related to workspaces (displaying, hiding, renaming…)
src/x.c::
Transfers our in-memory tree (see +src/render.c+) to X11.
src/xcb.c::
Contains wrappers to use xcb more easily.
src/xcursor.c::
XCursor functions (for cursor themes).
src/xinerama.c::
Legacy support for Xinerama. See +src/randr.c+ for the preferred API.
include/atoms.xmacro::
A file containing all X11 atoms which i3 uses. This file will be included
various times (for defining, requesting and receiving the atoms), each time
with a different definition of xmacro().
[[data_structures]]
== Data structures
See +include/data.h+ for documented data structures. The most important ones are
explained here.
See include/data.h for documented data structures. The most important ones are
explained right here.
The following picture is generated by the +contrib/dump-asy.pl+ script.
/////////////////////////////////////////////////////////////////////////////////
// TODO: update image
image:bigpicture.png["The Big Picture",width=1000,link="bigpicture.png"]
image:bigpicture.png[The Big Picture]
The hierarchy is:
/////////////////////////////////////////////////////////////////////////////////
So, the hierarchy is:
. *X11 root window*, the root container
. *Output container* (LVDS1 in this example)
. *Content container* (there are also containers for dock windows)
. *Workspaces* (Workspace 1 in this example, with horizontal orientation)
. *Split container* (vertically split)
. *X11 window containers*
. *Root container*
. *Output containers*: +eDP-1+ in this example and the internal +__i3++ output
. *Content and 2 dockarea containers*
. *Workspaces*: Numbered workspace ``1'' and a ``Named workspace''
. *Split containers*: One horizontal in the first workspace and a tabbed one in
the named one.
. *Leaf containers*: Windows like vim and an i3bar dock.
The data type is +Con+, in all cases.
=== X11 root window
=== Root container
The X11 root window is a single window per X11 display (a display is identified
by +:0+ or +:1+ etc.). The root window is what you draw your background image
on. It spans all the available outputs, e.g. +VGA1+ is a specific part of the
root window and +LVDS1+ is a specific part of the root window.
The root container (global variable +croot+) is the up-most ascendant of every i3
container. It can be used to iterate over the whole tree structure. E.g., it is
used to reply to the +GET_WORKSPACES+ request, iterating over it's children to
find all workspaces. This is different from the X11 root window.
The X11 root window (global variable +root+) is a single window per X11 display
(a display is identified by +:0+ or +:1+ etc.). The root window is what you draw
your background image on. It spans all the available outputs, e.g. +VGA1+ is a
specific part of the root window and +LVDS1+ is a specific part of the root
window.
=== Output container
@ -334,8 +314,8 @@ currently on.
=== Content container
Each output has multiple children. Two of them are dock containers which hold
dock clients. The other one is the content container, which holds the actual
content (workspaces) of this output.
the top and bottom dock clients. The other one is the content container, which
holds the actual content (workspaces) of this output.
=== Workspace
@ -354,21 +334,20 @@ vertical) and a layout.
Split containers (and X11 window containers, which are a subtype of split
containers) can have different border styles.
=== X11 window container
=== Leaf containers
An X11 window container holds exactly one X11 window. These are the leaf nodes
of the layout tree, they cannot have any children.
A leaf container holds exactly one X11 window. They can't have any children.
== List/queue macros
i3 makes heavy use of the list macros defined in BSD operating systems. To
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).
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
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.
@ -378,10 +357,10 @@ selected window to the window above/below.
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.
* +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())
@ -430,7 +409,7 @@ The bound command is parsed by the cmdparse lexer/parser, see +parse_cmd+ in
== Manage windows (src/main.c, manage_window() and reparent_window())
`manage_window()` does some checks to decide whether the window should be
+manage_window()+ does some checks to decide whether the window should be
managed at all:
* Windows have to be mapped, that is, visible on screen
@ -438,18 +417,18 @@ managed at all:
not be managed by a window manager
Afterwards, i3 gets the initial geometry and reparents the window (see
`reparent_window()`) if it wasnt already managed.
+reparent_window()+) if it wasnt already managed.
Reparenting means that for each window which is reparented, a new window,
slightly larger than the original one, is created. The original window is then
reparented to the bigger one (called "frame").
After reparenting, the window type (`_NET_WM_WINDOW_TYPE`) is checked to see
whether this window is a dock (`_NET_WM_WINDOW_TYPE_DOCK`), like dzen2 for
After reparenting, the window type (+_NET_WM_WINDOW_TYPE+) is checked to see
whether this window is a dock (+_NET_WM_WINDOW_TYPE_DOCK+), like dzen2 for
example. Docks are handled differently, they dont have decorations and are not
assigned to a specific container. Instead, they are positioned at the bottom
or top of the screen (in the appropriate dock area containers). To get the
height which needs to be reserved for the window, the `_NET_WM_STRUT_PARTIAL`
height which needs to be reserved for the window, the +_NET_WM_STRUT_PARTIAL+
property is used.
Furthermore, the list of assignments (to other workspaces, which may be on
@ -460,10 +439,10 @@ target workspace is not visible, the window will not be mapped.
== What happens when an application is started?
i3 does not care about applications. All it notices is when new windows are
mapped (see `src/handlers.c`, `handle_map_request()`). The window is then
mapped (see +src/handlers.c+, +handle_map_request()+). The window is then
reparented (see section "Manage windows").
After reparenting the window, `render_tree()` is called which renders the
After reparenting the window, +render_tree()+ is called which renders the
internal layout table. The new window has been placed in the currently focused
container and therefore the new window and the old windows (if any) need to be
moved/resized so that the currently active layout (default/stacking/tabbed mode)
@ -482,7 +461,7 @@ can reconfigure themselves).
Only the _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION atoms
are handled.
The former calls ``toggle_fullscreen()'' for the specific client which just
The former calls +toggle_fullscreen()+ for the specific client which just
configures the client to use the whole screen on which it currently is.
Also, it is set as fullscreen_client for the i3Screen.
@ -539,7 +518,7 @@ container) to the bottom.
=== Rendering the root container
The i3 root container (`con->type == CT_ROOT`) represents the X11 root window.
The i3 root container (+con->type == CT_ROOT+) represents the X11 root window.
It contains one child container for every output (like LVDS1, VGA1, …), which
is available on your computer.
@ -558,7 +537,7 @@ only called for the global fullscreen window.
=== Rendering an output
Output containers (`con->layout == L_OUTPUT`) represent a hardware output like
Output containers (+con->layout == L_OUTPUT+) represent a hardware output like
LVDS1, VGA1, etc. An output container has three children (at the moment): One
content container (having workspaces as children) and the top/bottom dock area
containers.
@ -566,7 +545,7 @@ containers.
The rendering happens in the function +render_l_output()+ in the following
steps:
1. Find the content container (`con->type == CT_CON`)
1. Find the content container (+con->type == CT_CON+)
2. Get the currently visible workspace (+con_get_fullscreen_con(content,
CF_OUTPUT)+).
3. If there is a fullscreened window on that workspace, directly render it and
@ -574,22 +553,22 @@ steps:
4. Sum up the space used by all the dock windows (they have a variable height
only).
5. Set the workspace rects (x/y/width/height) based on the position of the
output (stored in `con->rect`) and the usable space
(`con->rect.{width,height}` without the space used for dock windows).
output (stored in +con->rect+) and the usable space
(+con->rect.{width,height}+ without the space used for dock windows).
6. Recursively raise and render the outputs child containers (meaning dock
area containers and the content container).
=== Rendering a workspace or split container
From here on, there really is no difference anymore. All containers are of
`con->type == CT_CON` (whether workspace or split container) and some of them
have a `con->window`, meaning they represent an actual window instead of a
+con->type == CT_CON+ (whether workspace or split container) and some of them
have a +con->window+, meaning they represent an actual window instead of a
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
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.
@ -835,8 +814,8 @@ workspace <direction>::
the beginning. +
NOTE: Note that you can specify multiple literals in the same line. This has
exactly the same effect as if you specified `direction =
'next_on_output' -> call cmd_workspace($direction)` and so forth. +
exactly the same effect as if you specified +direction =
'next_on_output' -> call cmd_workspace($direction)+ and so forth. +
NOTE: Also note that the order of literals is important here: If 'next' were
ordered before 'next_on_output', then 'next_on_output' would never
@ -1020,11 +999,11 @@ Without much ado, here is the list of cases which need to be considered:
== Gotchas
* Forgetting to call `xcb_flush(conn);` after sending a request. This usually
* Forgetting to call +xcb_flush(conn);+ after sending a request. This usually
leads to code which looks like it works fine but which does not work under
certain conditions.
* Forgetting to call `floating_fix_coordinates(con, old_rect, new_rect)` after
* Forgetting to call +floating_fix_coordinates(con, old_rect, new_rect)+ after
moving workspaces across outputs. Coordinates for floating containers are
not relative to workspace boundaries, so you must correct their coordinates
or those containers will show up in the wrong workspace or not at all.