<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
<meta name="generator" content="AsciiDoc 8.6.10" />
<title>Layout saving in i3</title>
<style type="text/css">
/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
/* Default font. */
body {
font-family: Georgia,serif;
}
/* Title font. */
h1, h2, h3, h4, h5, h6,
div.title, caption.title,
thead, p.table.header,
#toctitle,
#author, #revnumber, #revdate, #revremark,
#footer {
font-family: Arial,Helvetica,sans-serif;
}
body {
margin: 1em 5% 1em 5%;
}
a {
color: blue;
text-decoration: underline;
}
a:visited {
color: fuchsia;
}
em {
font-style: italic;
color: navy;
}
strong {
font-weight: bold;
color: #083194;
}
h1, h2, h3, h4, h5, h6 {
color: #527bbd;
margin-top: 1.2em;
margin-bottom: 0.5em;
line-height: 1.3;
}
h1, h2, h3 {
border-bottom: 2px solid silver;
}
h2 {
padding-top: 0.5em;
}
h3 {
float: left;
}
h3 + * {
clear: left;
}
h5 {
font-size: 1.0em;
}
div.sectionbody {
margin-left: 0;
}
hr {
border: 1px solid silver;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
ul, ol, li > p {
margin-top: 0;
}
ul > li { color: #aaa; }
ul > li > * { color: black; }
.monospaced, code, pre {
font-family: "Courier New", Courier, monospace;
font-size: inherit;
color: navy;
padding: 0;
margin: 0;
}
pre {
white-space: pre-wrap;
}
#author {
color: #527bbd;
font-weight: bold;
font-size: 1.1em;
}
#email {
}
#revnumber, #revdate, #revremark {
}
#footer {
font-size: small;
border-top: 2px solid silver;
padding-top: 0.5em;
margin-top: 4.0em;
}
#footer-text {
float: left;
padding-bottom: 0.5em;
}
#footer-badges {
float: right;
padding-bottom: 0.5em;
}
#preamble {
margin-top: 1.5em;
margin-bottom: 1.5em;
}
div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.admonitionblock {
margin-top: 2.0em;
margin-bottom: 2.0em;
margin-right: 10%;
color: #606060;
}
div.content { /* Block element content. */
padding: 0;
}
/* Block element titles. */
div.title, caption.title {
color: #527bbd;
font-weight: bold;
text-align: left;
margin-top: 1.0em;
margin-bottom: 0.5em;
}
div.title + * {
margin-top: 0;
}
td div.title:first-child {
margin-top: 0.0em;
}
div.content div.title:first-child {
margin-top: 0.0em;
}
div.content + div.title {
margin-top: 0.0em;
}
div.sidebarblock > div.content {
background: #ffffee;
border: 1px solid #dddddd;
border-left: 4px solid #f0f0f0;
padding: 0.5em;
}
div.listingblock > div.content {
border: 1px solid #dddddd;
border-left: 5px solid #f0f0f0;
background: #f8f8f8;
padding: 0.5em;
}
div.quoteblock, div.verseblock {
padding-left: 1.0em;
margin-left: 1.0em;
margin-right: 10%;
border-left: 5px solid #f0f0f0;
color: #888;
}
div.quoteblock > div.attribution {
padding-top: 0.5em;
text-align: right;
}
div.verseblock > pre.content {
font-family: inherit;
font-size: inherit;
}
div.verseblock > div.attribution {
padding-top: 0.75em;
text-align: left;
}
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
div.verseblock + div.attribution {
text-align: left;
}
div.admonitionblock .icon {
vertical-align: top;
font-size: 1.1em;
font-weight: bold;
text-decoration: underline;
color: #527bbd;
padding-right: 0.5em;
}
div.admonitionblock td.content {
padding-left: 0.5em;
border-left: 3px solid #dddddd;
}
div.exampleblock > div.content {
border-left: 3px solid #dddddd;
padding-left: 0.5em;
}
div.imageblock div.content { padding-left: 0; }
span.image img { border-style: none; vertical-align: text-bottom; }
a.image:visited { color: white; }
dl {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
dt {
margin-top: 0.5em;
margin-bottom: 0;
font-style: normal;
color: navy;
}
dd > *:first-child {
margin-top: 0.1em;
}
ul, ol {
list-style-position: outside;
}
ol.arabic {
list-style-type: decimal;
}
ol.loweralpha {
list-style-type: lower-alpha;
}
ol.upperalpha {
list-style-type: upper-alpha;
}
ol.lowerroman {
list-style-type: lower-roman;
}
ol.upperroman {
list-style-type: upper-roman;
}
div.compact ul, div.compact ol,
div.compact p, div.compact p,
div.compact div, div.compact div {
margin-top: 0.1em;
margin-bottom: 0.1em;
}
tfoot {
font-weight: bold;
}
td > div.verse {
white-space: pre;
}
div.hdlist {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
div.hdlist tr {
padding-bottom: 15px;
}
dt.hdlist1.strong, td.hdlist1.strong {
font-weight: bold;
}
td.hdlist1 {
vertical-align: top;
font-style: normal;
padding-right: 0.8em;
color: navy;
}
td.hdlist2 {
vertical-align: top;
}
div.hdlist.compact tr {
margin: 0;
padding-bottom: 0;
}
.comment {
background: yellow;
}
.footnote, .footnoteref {
font-size: 0.8em;
}
span.footnote, span.footnoteref {
vertical-align: super;
}
#footnotes {
margin: 20px 0 20px 0;
padding: 7px 0 0 0;
}
#footnotes div.footnote {
margin: 0 0 5px 0;
}
#footnotes hr {
border: none;
border-top: 1px solid silver;
height: 1px;
text-align: left;
margin-left: 0;
width: 20%;
min-width: 100px;
}
div.colist td {
padding-right: 0.5em;
padding-bottom: 0.3em;
vertical-align: top;
}
div.colist td img {
margin-top: 0.3em;
}
@media print {
#footer-badges { display: none; }
}
#toc {
margin-bottom: 2.5em;
}
#toctitle {
color: #527bbd;
font-size: 1.1em;
font-weight: bold;
margin-top: 1.0em;
margin-bottom: 0.1em;
}
div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
margin-top: 0;
margin-bottom: 0;
}
div.toclevel2 {
margin-left: 2em;
font-size: 0.9em;
}
div.toclevel3 {
margin-left: 4em;
font-size: 0.9em;
}
div.toclevel4 {
margin-left: 6em;
font-size: 0.9em;
}
span.aqua { color: aqua; }
span.black { color: black; }
span.blue { color: blue; }
span.fuchsia { color: fuchsia; }
span.gray { color: gray; }
span.green { color: green; }
span.lime { color: lime; }
span.maroon { color: maroon; }
span.navy { color: navy; }
span.olive { color: olive; }
span.purple { color: purple; }
span.red { color: red; }
span.silver { color: silver; }
span.teal { color: teal; }
span.white { color: white; }
span.yellow { color: yellow; }
span.aqua-background { background: aqua; }
span.black-background { background: black; }
span.blue-background { background: blue; }
span.fuchsia-background { background: fuchsia; }
span.gray-background { background: gray; }
span.green-background { background: green; }
span.lime-background { background: lime; }
span.maroon-background { background: maroon; }
span.navy-background { background: navy; }
span.olive-background { background: olive; }
span.purple-background { background: purple; }
span.red-background { background: red; }
span.silver-background { background: silver; }
span.teal-background { background: teal; }
span.white-background { background: white; }
span.yellow-background { background: yellow; }
span.big { font-size: 2em; }
span.small { font-size: 0.6em; }
span.underline { text-decoration: underline; }
span.overline { text-decoration: overline; }
span.line-through { text-decoration: line-through; }
div.unbreakable { page-break-inside: avoid; }
/*
* xhtml11 specific
*
* */
div.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
div.tableblock > table {
border: 3px solid #527bbd;
}
thead, p.table.header {
font-weight: bold;
color: #527bbd;
}
p.table {
margin-top: 0;
}
/* Because the table frame attribute is overriden by CSS in most browsers. */
div.tableblock > table[frame="void"] {
border-style: none;
}
div.tableblock > table[frame="hsides"] {
border-left-style: none;
border-right-style: none;
}
div.tableblock > table[frame="vsides"] {
border-top-style: none;
border-bottom-style: none;
}
/*
* html5 specific
*
* */
table.tableblock {
margin-top: 1.0em;
margin-bottom: 1.5em;
}
thead, p.tableblock.header {
font-weight: bold;
color: #527bbd;
}
p.tableblock {
margin-top: 0;
}
table.tableblock {
border-width: 3px;
border-spacing: 0px;
border-style: solid;
border-color: #527bbd;
border-collapse: collapse;
}
th.tableblock, td.tableblock {
border-width: 1px;
padding: 4px;
border-style: solid;
border-color: #527bbd;
}
table.tableblock.frame-topbot {
border-left-style: hidden;
border-right-style: hidden;
}
table.tableblock.frame-sides {
border-top-style: hidden;
border-bottom-style: hidden;
}
table.tableblock.frame-none {
border-style: hidden;
}
th.tableblock.halign-left, td.tableblock.halign-left {
text-align: left;
}
th.tableblock.halign-center, td.tableblock.halign-center {
text-align: center;
}
th.tableblock.halign-right, td.tableblock.halign-right {
text-align: right;
}
th.tableblock.valign-top, td.tableblock.valign-top {
vertical-align: top;
}
th.tableblock.valign-middle, td.tableblock.valign-middle {
vertical-align: middle;
}
th.tableblock.valign-bottom, td.tableblock.valign-bottom {
vertical-align: bottom;
}
/*
* manpage specific
*
* */
body.manpage h1 {
padding-top: 0.5em;
padding-bottom: 0.5em;
border-top: 2px solid silver;
border-bottom: 2px solid silver;
}
body.manpage h2 {
border-style: none;
}
body.manpage div.sectionbody {
margin-left: 3em;
}
@media print {
body.manpage div#toc { display: none; }
}
</style>
<script type="text/javascript">
/*<![CDATA[*/
var asciidoc = { // Namespace.
/////////////////////////////////////////////////////////////////////
// Table Of Contents generator
/////////////////////////////////////////////////////////////////////
/* Author: Mihai Bazon, September 2002
* http://students.infoiasi.ro/~mishoo
*
* Table Of Content generator
* Version: 0.4
*
* Feel free to use this script under the terms of the GNU General Public
* License, as long as you do not remove or alter this notice.
*/
/* modified by Troy D. Hanson, September 2006. License: GPL */
/* modified by Stuart Rackham, 2006, 2009. License: GPL */
// toclevels = 1..4.
toc: function (toclevels) {
function getText(el) {
var text = "";
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
text += i.data;
else if (i.firstChild != null)
text += getText(i);
}
return text;
}
function TocEntry(el, text, toclevel) {
this.element = el;
this.text = text;
this.toclevel = toclevel;
}
function tocEntries(el, toclevels) {
var result = new Array;
var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
// Function that scans the DOM tree for header elements (the DOM2
// nodeIterator API would be a better technique but not supported by all
// browsers).
var iterate = function (el) {
for (var i = el.firstChild; i != null; i = i.nextSibling) {
if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
var mo = re.exec(i.tagName);
if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
}
iterate(i);
}
}
}
iterate(el);
return result;
}
var toc = document.getElementById("toc");
if (!toc) {
return;
}
// Delete existing TOC entries in case we're reloading the TOC.
var tocEntriesToRemove = [];
var i;
for (i = 0; i < toc.childNodes.length; i++) {
var entry = toc.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div'
&& entry.getAttribute("class")
&& entry.getAttribute("class").match(/^toclevel/))
tocEntriesToRemove.push(entry);
}
for (i = 0; i < tocEntriesToRemove.length; i++) {
toc.removeChild(tocEntriesToRemove[i]);
}
// Rebuild TOC entries.
var entries = tocEntries(document.getElementById("content"), toclevels);
for (var i = 0; i < entries.length; ++i) {
var entry = entries[i];
if (entry.element.id == "")
entry.element.id = "_toc_" + i;
var a = document.createElement("a");
a.href = "#" + entry.element.id;
a.appendChild(document.createTextNode(entry.text));
var div = document.createElement("div");
div.appendChild(a);
div.className = "toclevel" + entry.toclevel;
toc.appendChild(div);
}
if (entries.length == 0)
toc.parentNode.removeChild(toc);
},
/////////////////////////////////////////////////////////////////////
// Footnotes generator
/////////////////////////////////////////////////////////////////////
/* Based on footnote generation code from:
* http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
*/
footnotes: function () {
// Delete existing footnote entries in case we're reloading the footnodes.
var i;
var noteholder = document.getElementById("footnotes");
if (!noteholder) {
return;
}
var entriesToRemove = [];
for (i = 0; i < noteholder.childNodes.length; i++) {
var entry = noteholder.childNodes[i];
if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
entriesToRemove.push(entry);
}
for (i = 0; i < entriesToRemove.length; i++) {
noteholder.removeChild(entriesToRemove[i]);
}
// Rebuild footnote entries.
var cont = document.getElementById("content");
var spans = cont.getElementsByTagName("span");
var refs = {};
var n = 0;
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnote") {
n++;
var note = spans[i].getAttribute("data-note");
if (!note) {
// Use [\s\S] in place of . so multi-line matches work.
// Because JavaScript has no s (dotall) regex flag.
note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
spans[i].innerHTML =
"[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
spans[i].setAttribute("data-note", note);
}
noteholder.innerHTML +=
"<div class='footnote' id='_footnote_" + n + "'>" +
"<a href='#_footnoteref_" + n + "' title='Return to text'>" +
n + "</a>. " + note + "</div>";
var id =spans[i].getAttribute("id");
if (id != null) refs["#"+id] = n;
}
}
if (n == 0)
noteholder.parentNode.removeChild(noteholder);
else {
// Process footnoterefs.
for (i=0; i<spans.length; i++) {
if (spans[i].className == "footnoteref") {
var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
href = href.match(/#.*/)[0]; // Because IE return full URL.
n = refs[href];
spans[i].innerHTML =
"[<a href='#_footnote_" + n +
"' title='View footnote' class='footnote'>" + n + "</a>]";
}
}
}
},
install: function(toclevels) {
var timerId;
function reinstall() {
asciidoc.footnotes();
if (toclevels) {
asciidoc.toc(toclevels);
}
}
function reinstallAndRemoveTimer() {
clearInterval(timerId);
reinstall();
}
timerId = setInterval(reinstall, 500);
if (document.addEventListener)
document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
else
window.onload = reinstallAndRemoveTimer;
}
}
asciidoc.install(2);
/*]]>*/
</script>
</head>
<body class="article">
<div id="header">
<h1>Layout saving in i3</h1>
<span id="author">Michael Stapelberg</span><br />
<span id="email"><code><<a href="mailto:[email protected]">[email protected]</a>></code></span><br />
<span id="revdate">April 2014</span>
<div id="toc">
<div id="toctitle">Table of Contents</div>
<noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph"><p>Layout saving/restoring is a feature that was introduced in i3 v4.8.</p></div>
<div class="paragraph"><p>Layout saving/restoring allows you to load a JSON layout file so that you can
have a base layout to start working with after powering on your computer.
Dynamic use-cases also come to mind: if you frequently (but not always!) need a
grid layout of terminals with ping/traceroute commands to diagnose network
issues, you can easily automate opening these windows in just the right layout.</p></div>
</div>
</div>
<div class="sect1">
<h2 id="_saving_the_layout">1. Saving the layout</h2>
<div class="sectionbody">
<div class="paragraph"><p>You can save the layout of either a single workspace or an entire output (e.g.
LVDS1). Of course, you can repeat this step multiple times if you want to
save/restore multiple workspaces/outputs.</p></div>
<div class="paragraph"><p><code>i3-save-tree(1)</code> is a tool to save the layout. It will print a JSON
representation of i3’s internal layout data structures to stdout. Typically,
you may want to take a quick look at the output, then save it to a file and
tweak it a little bit:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>i3-save-tree --workspace 1 > ~/.i3/workspace-1.json</code></pre>
</div></div>
<div class="paragraph"><p>Please note that the output of <code>i3-save-tree(1)</code> is <strong>NOT useful</strong> until you
manually modify it — you need to tell i3 how to match/distinguish windows (for
example based on their WM_CLASS, title, etc.). By default, all the different
window properties are included in the output, but commented out. This is partly
to avoid relying on heuristics and partly to make you aware how i3 works so
that you can easily solve layout restoring problems.</p></div>
<div class="paragraph"><p>How to modify the file manually is described in <a href="#EditingLayoutFiles">[EditingLayoutFiles]</a>.</p></div>
</div>
</div>
<div class="sect1">
<h2 id="_restoring_the_layout">2. Restoring the layout</h2>
<div class="sectionbody">
<div class="paragraph"><p>After restoring the example layout from <a href="#EditingLayoutFiles">[EditingLayoutFiles]</a>, i3 will open
placeholder windows for all the windows that were specified in the layout file.
You can recognize the placeholder windows by the watch symbol
<span class="footnote"><br />[Depending on the font you are using, a placeholder symbol may show up
instead of the watch symbol.]<br /></span> in the center of the window, and by the swallow
criteria specification at the top of the window:</p></div>
<div class="paragraph"><p><span class="image">
<a class="image" href="layout-saving-1.png">
<img src="layout-saving-1.png" alt="Restored layout" width="400" />
</a>
</span></p></div>
<div class="paragraph"><p>When an application opens a window that matches the specified swallow criteria,
it will be placed in the corresponding placeholder window. We say it gets
<strong>swallowed</strong> by the placeholder container, hence the term.</p></div>
<div class="paragraph"><p>Note: Swallowing windows into unsatisfied placeholder windows takes precedence
over
<a href="https://i3wm.org/docs/userguide.html#_automatically_putting_clients_on_specific_workspaces">assignment
rules</a>. For example, if you assign all Emacs windows to workspace 1 in your i3
configuration file, but there is a placeholder window on workspace 2 which
matches Emacs as well, your newly started Emacs window will end up in the
placeholder window on workspace 2.</p></div>
<div class="paragraph"><p>The placeholder windows are just regular windows, so feel free to move them
around or close them, for example.</p></div>
<div class="sect2">
<h3 id="_append_layout_command">2.1. append_layout command</h3>
<div class="paragraph"><p>The <code>append_layout</code> command is used to load a layout file into i3. It accepts a
path (relative to i3’s current working directory or absolute) to a JSON file.</p></div>
<div class="paragraph"><p><strong>Syntax</strong>:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>append_layout <path></code></pre>
</div></div>
<div class="paragraph"><p><strong>Examples</strong>:</p></div>
<div class="listingblock">
<div class="content">
<pre><code># From a terminal or script:
i3-msg "workspace 1; append_layout /home/michael/.i3/workspace-1.json"
# In your i3 configuration file, you can autostart i3-msg like this:
# (Note that those lines will quickly become long, so typically you would store
# them in a script with proper indentation.)
exec --no-startup-id "i3-msg 'workspace 1; append_layout /home/michael/.i3/workspace-1.json'"</code></pre>
</div></div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_editing_layout_files">3. Editing layout files</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="EditingLayoutFiles">3.1. Anatomy of a layout file</h3>
<div class="paragraph"><p>Here is an example layout file that we’ll discuss:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>{
// splitv split container with 2 children
"layout": "splitv",
"percent": 0.4,
"type": "con",
"nodes": [
{
"border": "none",
"name": "irssi",
"percent": 0.5,
"type": "con",
"swallows": [
{
"class": "^URxvt$",
"instance": "^irssi$"
}
]
},
{
// stacked split container with 2 children
"layout": "stacked",
"percent": 0.5,
"type": "con",
"nodes": [
{
"name": "notmuch",
"percent": 0.5,
"type": "con",
"swallows": [
{
"class": "^Emacs$",
"instance": "^notmuch$"
}
]
},
{
"name": "midna: ~",
"percent": 0.5,
"type": "con"
}
]
}
]
}
{
// stacked split container with 1 children
"layout": "stacked",
"percent": 0.6,
"type": "con",
"nodes": [
{
"name": "chrome",
"type": "con",
"swallows": [
{
"class": "^Google-chrome$"
}
]
}
]
}</code></pre>
</div></div>
<div class="paragraph"><p>In this layout, the screen is divided into two columns. In the left column,
which covers 40% of the screen, there is a terminal emulator running irssi on
the top, and a stacked split container with an Emacs window and a terminal
emulator on the bottom. In the right column, there is a stacked container with
a Chrome window:</p></div>
<div class="paragraph"><p><span class="image">
<a class="image" href="layout-saving-1.png">
<img src="layout-saving-1.png" alt="Restored layout" width="400" />
</a>
</span></p></div>
<div class="paragraph"><p>The structure of this JSON file looks a lot like the <code>TREE</code> reply, see
<a href="https://build.i3wm.org/docs/ipc.html#_tree_reply">https://build.i3wm.org/docs/ipc.html#_tree_reply</a> for documentation on that. Some
properties are excluded because they are not relevant when restoring a layout.</p></div>
<div class="paragraph"><p>Most importantly, look at the "swallows" section of each window. This is where
you need to be more or less specific. As an example, remember the section about
the Emacs window:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>"swallows": [
{
"class": "^Emacs$",
"instance": "^notmuch$"
}
]</code></pre>
</div></div>
<div class="paragraph"><p>Here you can see that i3 will require both the class and the instance to match.
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 (<code>emacs24
--name notmuch</code>), it will get swallowed.</p></div>
<div class="paragraph"><p>You can match on "class", "instance", "window_role" and "title". All values are
case-sensitive regular expressions (PCRE). Use <code>xprop(1)</code> and click into a
window to see its properties:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>$ xprop
WM_WINDOW_ROLE(STRING) = "gimp-toolbox-color-dialog"
WM_CLASS(STRING) = "gimp-2.8", "Gimp-2.8"
_NET_WM_NAME(UTF8_STRING) = "Change Foreground Color"</code></pre>
</div></div>
<div class="paragraph"><p>The first part of <code>WM_CLASS</code> is the "instance" (gimp-2.8 in this case), the
second part is the "class" (Gimp-2.8 in this case). "title" matches against
<code>_NET_WM_NAME</code> and "window_role" matches against <code>WM_WINDOW_ROLE</code>.</p></div>
<div class="paragraph"><p>In general, you should try to be as specific as possible in your swallow
criteria. Try to use criteria that match one window and only one window, to
have a reliable startup procedure.</p></div>
<div class="paragraph"><p>If you specify multiple swallow criteria, the placeholder will be replaced by
the window which matches any of the criteria. As an example:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>// Matches either Emacs or Gvim, whichever one is started first.
"swallows": [
{"class": "^Emacs$"},
{"class": "^Gvim$"}
]</code></pre>
</div></div>
</div>
<div class="sect2">
<h3 id="_json_standard_non_compliance">3.2. JSON standard non-compliance</h3>
<div class="paragraph"><p>A layout file as generated by <code>i3-save-tree(1)</code> is not strictly valid JSON:</p></div>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Layout files contain multiple “JSON texts” at the top level. The JSON
standard doesn’t prohibit this, but in practice most JSON parsers only
allow precisely one “text” per document/file, and will mark multiple texts
as invalid JSON.
</p>
</li>
<li>
<p>
Layout files contain comments which are not allowed by the JSON standard,
but are understood by many parsers.
</p>
</li>
</ol></div>
<div class="paragraph"><p>Both of these deviations from the norm are to make manual editing by humans
easier. In case you are writing a more elaborate tool for manipulating these
layouts, you can either use a JSON parser that supports these deviations (for
example libyajl), transform the layout file to a JSON-conforming file, or
<a href="https://github.com/i3/i3/blob/next/.github/CONTRIBUTING.md">submit a patch</a>
to make <code>i3-save-tree(1)</code> optionally output standard-conforming JSON.</p></div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_troubleshooting">4. Troubleshooting</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_restoring_a_vertically_split_workspace">4.1. Restoring a vertically split workspace</h3>
<div class="paragraph"><p>When using <code>i3-save-tree</code> with the <code>--workspace</code> switch, only the <strong>contents</strong> of
the workspace will be dumped. This means that properties of the workspace
itself will be lost.</p></div>
<div class="paragraph"><p>This is relevant for, e.g., a vertically split container as the base container of
a workspace. Since the split mode is a property of the workspace, it will not be
stored. In this case, you will have to manually wrap your layout in such a
container:</p></div>
<div class="listingblock">
<div class="content">
<pre><code>// vim:ts=4:sw=4:et
{
// this is a manually added container to restore the vertical split
"layout": "splitv",
"percent": 0.5,
"type": "con",
"nodes": [
// the dumped workspace layout goes here
]
}</code></pre>
</div></div>
</div>
<div class="sect2">
<h3 id="_placeholders_using_window_title_matches_don_8217_t_swallow_the_window">4.2. Placeholders using window title matches don’t swallow the window</h3>
<div class="paragraph"><p>If you use the <code>title</code> attribute to match a window and find that it doesn’t
work or only works sometimes, the reason might be that the application sets the
title only after making the window visible. This will be especially true for
programs running inside terminal emulators, e.g., <code>urxvt -e irssi</code> when
matching on <code>title: "irssi"</code>.</p></div>
<div class="paragraph"><p>One way to deal with this is to not rely on the title, but instead use, e.g.,
the <code>instance</code> attribute and running the program to set this window instance to
that value:</p></div>
<div class="listingblock">
<div class="content">
<pre><code># Run irssi via
# urxvt -name "irssi-container" -e irssi
"swallows": [
{
"class": "URxvt",
"instance": "irssi-container"
}
]</code></pre>
</div></div>
</div>
</div>
</div>
</div>
<div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
Last updated
2019-08-30 23:06:47 CEST
</div>
</div>
</body>
</html>