Swallow - bakkeby/dusk GitHub Wiki
The Swallow
functionality allows terminal windows marked with the Terminal flag to
"swallow" X windows being started from the command line.
The idea of window swallowing is known from Plan 9's windowing system rio
.
As an example running xclock
from a terminal will normally open an X window showing a clock
leaving the terminal in a "useless" state where it is just waiting on the command to finish. The
Swallow
functionality in this scenario allows the xclock
window to take the place of the
terminal.
For programs where window swallowing is undesirable the feature can be disabled on a per client basis by setting the NoSwallow flag via client rules.
A typical example of this being the xev
(Event Tester) program where you still need to see what
the application outputs to the terminal.
The size hints of the terminal being replaced does not affect the size hints of the new window and vice versa. This is achieved by keeping the two as entirely separate clients and one just takes the other's place in the client list.
When a floating terminal window is replaced by another window then by default the window will inherit the size and position of the terminal window.
Note that when this happens the size hints of the new window may result in a size that is different than what the terminal window was, and additionally the new window may be sending ConfigureRequest events to specify another size and/or position.
It is possible to ignore configure events fully or partially using flags such as:
- IgnoreCfgReq - to ignore all configure requests for the client
- IgnoreCfgReqPos - to ignore the x and y coordinates of the request
- IgnoreCfgReqSize - to ignore the size attributes of the request
If the SwallowFloating functionality is disabled then windows that are floating will not replace the terminal unless they are also ruled.
When the window is closed and the terminal window returns then the terminal window will by default (again) inherit the size and position of the previous window.
If desirable then the size of the terminal window and the size of the new window can be completely independent of each other by ensuring that either of the two have the SwallowRetainSize flag set via client rules. This will allow both windows to retain their original size when swallowed / unswallowed.
In general the window swallowing functionality should be intuitive with the floating, tiled and fullscreen states seamlessly transitioning between clients on swallow and unswallow giving the illusion that it is the same window.
A swallowed window can be restored without killing the new window by triggering the unswallow function.
The fundamental requirement for window swallowing to work is that there is a clear relation between the two windows that are to share the same space.
Typically this is done using process IDs (PIDs) and if a new window (xclock
) is found to be a
decendant of the terminal process (i.e. the terminal process is a parent process of the xclock
process) then we have a clear link. This requires the window manager to walk the process tree,
which inherently is an OS-specific task.
Another problem with this is that there is no clear link or registry that ties window IDs with
process IDs. Thankfully most programs expose the process ID by setting the _NET_WM_PID
property
on the window. Some programs do not, however, which means that the corresponding windows can't
swallow the terminal window.
Only terminals created by local processes can swallow windows, and only windows created by local processes can be swallowed automatically.
As an example it is not possible to simply mark a file manager like Nautilus as being a terminal and expecting it be swallowed by windows when opening documents or other files using it. This is because in most cases file managers like this act as daemons which means that new windows will be opened as orphans and will not be descendants of the file manager. In other words the file manager will not be the parent process of the new window, hence the swallow mechanism will not work automatically.
A file manager will typically open files in orphaned processes which means that there will be no link between the file manager process and the new window.
For window swallowing to work in this situation we need a way to create a relation between the file manager and the new window.
One way to do this is to set an environment variable for the file manager when it starts - this because a child process will normally inherit the environment variables of its parent process.
So we have the example command that runs the nemo
file manager with the environment variable of
SWALLOWKEY
set to N
.
$ SWALLOWKEY=N nemo
To mark the file manager as a window that can be swallowed we need a client rule setting the
Terminal
flag as well as the matching swallow key of N
.
{ .class = "Nemo", .flags = Terminal, .swallowkey = 'N'},
Now when the file manager opens a file, e.g. a photo in an image viewer, then that program window should replace the file manager. When the image viewer is closed then the file manager is revealed again.
Technically this works by identifying the PID of the window by looking at the _NET_WM_PID
property
if present, then trying to read the SWALLOWKEY
environment variable from /proc/<pid>/environ
provided that we have permission to do so. If found then the value of the environment variable
(N
in this case) is stored in the swallowedby
field of the client which is used when identifying
the terminal for the window.
This has been tested successfully with PCManFM
and Nemo
, but has found to not work with Thunar
because spawned processes appear to not inherit the environment variables of the parent process.
Another approach is to create client rules that explicitly set the swallowedby
value for clients.
The downside of this is that you need to set up rules for all programs that you may want to use that
should swallow the file manager.
Here is a list of functions relating to window swallowing.
Function | Description |
---|---|
swallow | Force all marked clients to be swallowed by the selected client |
swallowmouse | Allows for one window to swallow another using the mouse |
unswallow | Force the selected client to unswallow the most recently swallowed client |
Here is a list of functionality related to window swallowing.
Functionality | Description |
---|---|
Swallow |
Enables window swallowing |
Here is a list of flags that are related to window swallowing.
Flag | Description |
---|---|
NoSwallow | Indicates that the client should never swallow a terminal window |
SwallowRetainSize | Allow swallowed windows to retain their own size |
SwallowNoInheritFullScreen | Prevents the client from inheriting the fullscreen property when swallowed / unswallowed |
Terminal | Indicates that the client is a terminal, used by swallow |
Additional notes
1) When it comes to window swallowing the terminology used is not always obvious or intuitive.
There are two views:
a) the terminal window swallows the xclock
window
b) the xclock
window swallows the terminal window
Which of the two is more correct is up for debate. To avoid confusion this wiki tries to use simpler terminology such as one window replaces another, but otherwise it should adhere to view b).
2) You may also want to look at devour which is a WM agnostic tool for window swallowing.
Back to Functionality.