]> git.sur5r.net Git - i3/i3/blob - docs/layout-saving
Merge pull request #3319 from Stunkymonkey/format_placholders-case_sensitive
[i3/i3] / docs / layout-saving
1 Layout saving in i3
2 ===================
3 Michael Stapelberg <michael@i3wm.org>
4 April 2014
5
6 Layout saving/restoring is a feature that was introduced in i3 v4.8.
7
8 Layout saving/restoring allows you to load a JSON layout file so that you can
9 have a base layout to start working with after powering on your computer.
10 Dynamic use-cases also come to mind: if you frequently (but not always!) need a
11 grid layout of terminals with ping/traceroute commands to diagnose network
12 issues, you can easily automate opening these windows in just the right layout.
13
14 == Saving the layout
15
16 You can save the layout of either a single workspace or an entire output (e.g.
17 LVDS1). Of course, you can repeat this step multiple times if you want to
18 save/restore multiple workspaces/outputs.
19
20 +i3-save-tree(1)+ is a tool to save the layout. It will print a JSON
21 representation of i3’s internal layout data structures to stdout. Typically,
22 you may want to take a quick look at the output, then save it to a file and
23 tweak it a little bit:
24
25 ---------------------------------------------------
26 i3-save-tree --workspace 1 > ~/.i3/workspace-1.json
27 ---------------------------------------------------
28
29 Please note that the output of +i3-save-tree(1)+ is *NOT useful* until you
30 manually modify it — you need to tell i3 how to match/distinguish windows (for
31 example based on their WM_CLASS, title, etc.). By default, all the different
32 window properties are included in the output, but commented out. This is partly
33 to avoid relying on heuristics and partly to make you aware how i3 works so
34 that you can easily solve layout restoring problems.
35
36 How to modify the file manually is described in <<EditingLayoutFiles>>.
37
38 == Restoring the layout
39
40 After restoring the example layout from <<EditingLayoutFiles>>, i3 will open
41 placeholder windows for all the windows that were specified in the layout file.
42 You can recognize the placeholder windows by the watch symbol
43 footnote:[Depending on the font you are using, a placeholder symbol may show up
44 instead of the watch symbol.] in the center of the window, and by the swallow
45 criteria specification at the top of the window:
46
47 image:layout-saving-1.png["Restored layout",width=400,link="layout-saving-1.png"]
48
49 When an application opens a window that matches the specified swallow criteria,
50 it will be placed in the corresponding placeholder window. We say it gets
51 *swallowed* by the placeholder container, hence the term.
52
53 Note: Swallowing windows into unsatisfied placeholder windows takes precedence
54 over
55 link:https://i3wm.org/docs/userguide.html#_automatically_putting_clients_on_specific_workspaces[assignment
56 rules]. For example, if you assign all Emacs windows to workspace 1 in your i3
57 configuration file, but there is a placeholder window on workspace 2 which
58 matches Emacs as well, your newly started Emacs window will end up in the
59 placeholder window on workspace 2.
60
61 The placeholder windows are just regular windows, so feel free to move them
62 around or close them, for example.
63
64 === append_layout command
65
66 The +append_layout+ command is used to load a layout file into i3. It accepts a
67 path (relative to i3’s current working directory or absolute) to a JSON file.
68
69 *Syntax*:
70 --------------------------------------------------------------------------------
71 append_layout <path>
72 --------------------------------------------------------------------------------
73
74 *Examples*:
75 --------------------------------------------------------------------------------
76 # From a terminal or script:
77 i3-msg "workspace 1; append_layout /home/michael/.i3/workspace-1.json"
78
79 # In your i3 configuration file, you can autostart i3-msg like this:
80 # (Note that those lines will quickly become long, so typically you would store
81 #  them in a script with proper indentation.)
82 exec --no-startup-id "i3-msg 'workspace 1; append_layout /home/michael/.i3/workspace-1.json'"
83 --------------------------------------------------------------------------------
84
85 == Editing layout files
86
87 [[EditingLayoutFiles]]
88
89 === Anatomy of a layout file
90
91 Here is an example layout file that we’ll discuss:
92
93 --------------------------------------------------------------------------------
94 {
95     // splitv split container with 2 children
96     "layout": "splitv",
97     "percent": 0.4,
98     "type": "con",
99     "nodes": [
100         {
101             "border": "none",
102             "name": "irssi",
103             "percent": 0.5,
104             "type": "con",
105             "swallows": [
106                 {
107                     "class": "^URxvt$",
108                     "instance": "^irssi$"
109                 }
110             ]
111         },
112         {
113             // stacked split container with 2 children
114             "layout": "stacked",
115             "percent": 0.5,
116             "type": "con",
117             "nodes": [
118                 {
119                     "name": "notmuch",
120                     "percent": 0.5,
121                     "type": "con",
122                     "swallows": [
123                         {
124                             "class": "^Emacs$",
125                             "instance": "^notmuch$"
126                         }
127                     ]
128                 },
129                 {
130                     "name": "midna: ~",
131                     "percent": 0.5,
132                     "type": "con"
133                 }
134             ]
135         }
136     ]
137 }
138
139 {
140     // stacked split container with 1 children
141     "layout": "stacked",
142     "percent": 0.6,
143     "type": "con",
144     "nodes": [
145         {
146             "name": "chrome",
147             "type": "con",
148             "swallows": [
149                 {
150                     "class": "^Google-chrome$"
151                 }
152             ]
153         }
154     ]
155 }
156 --------------------------------------------------------------------------------
157
158 In this layout, the screen is divided into two columns. In the left column,
159 which covers 40% of the screen, there is a terminal emulator running irssi on
160 the top, and a stacked split container with an Emacs window and a terminal
161 emulator on the bottom. In the right column, there is a stacked container with
162 a Chrome window:
163
164 image:layout-saving-1.png["Restored layout",width=400,link="layout-saving-1.png"]
165
166 The structure of this JSON file looks a lot like the +TREE+ reply, see
167 https://build.i3wm.org/docs/ipc.html#_tree_reply for documentation on that. Some
168 properties are excluded because they are not relevant when restoring a layout.
169
170 Most importantly, look at the "swallows" section of each window. This is where
171 you need to be more or less specific. As an example, remember the section about
172 the Emacs window:
173
174 --------------------------------------------------------------------------------
175 "swallows": [
176     {
177         "class": "^Emacs$",
178         "instance": "^notmuch$"
179     }
180 ]
181 --------------------------------------------------------------------------------
182
183 Here you can see that i3 will require both the class and the instance to match.
184 Therefore, if you just start Emacs via dmenu, it will not get swallowed by that
185 container. Only if you start Emacs with the proper instance name (+emacs24
186 --name notmuch+), it will get swallowed.
187
188 You can match on "class", "instance", "window_role" and "title". All values are
189 case-sensitive regular expressions (PCRE). Use +xprop(1)+ and click into a
190 window to see its properties:
191
192 --------------------------------------------------------------------------------
193 $ xprop
194 WM_WINDOW_ROLE(STRING) = "gimp-toolbox-color-dialog"
195 WM_CLASS(STRING) = "gimp-2.8", "Gimp-2.8"
196 _NET_WM_NAME(UTF8_STRING) = "Change Foreground Color"
197 --------------------------------------------------------------------------------
198
199 The first part of +WM_CLASS+ is the "instance" (gimp-2.8 in this case), the
200 second part is the "class" (Gimp-2.8 in this case). "title" matches against
201 +_NET_WM_NAME+ and "window_role" matches against +WM_WINDOW_ROLE+.
202
203 In general, you should try to be as specific as possible in your swallow
204 criteria. Try to use criteria that match one window and only one window, to
205 have a reliable startup procedure.
206
207 If you specify multiple swallow criteria, the placeholder will be replaced by
208 the window which matches any of the criteria. As an example:
209
210 --------------------------------------------------------------------------------
211 // Matches either Emacs or Gvim, whichever one is started first.
212 "swallows": [
213     {"class": "^Emacs$"},
214     {"class": "^Gvim$"}
215 ]
216 --------------------------------------------------------------------------------
217
218 === JSON standard non-compliance
219
220 A layout file as generated by +i3-save-tree(1)+ is not strictly valid JSON:
221
222 1. Layout files contain multiple “JSON documents” on the top level, whereas the
223    JSON standard only allows precisely one “document” (array or hash).
224
225 2. Layout files contain comments which are not standardized, but understood by
226    many parsers.
227
228 Both deviations from the JSON standard are to make manual editing by humans
229 easier. In case you are writing a more elaborate tool for manipulating these
230 layouts, you can either use a JSON parser that supports these deviations (for
231 example libyajl), transform the layout file to a JSON-conforming file, or
232 link:https://github.com/i3/i3/blob/next/.github/CONTRIBUTING.md[submit a patch]
233 to make +i3-save-tree(1)+ optionally output standard-conforming JSON.
234
235 == Troubleshooting
236
237 === Restoring a vertically split workspace
238
239 When using +i3-save-tree+ with the +--workspace+ switch, only the *contents* of
240 the workspace will be dumped. This means that properties of the workspace
241 itself will be lost.
242
243 This is relevant for, e.g., a vertically split container as the base container of
244 a workspace. Since the split mode is a property of the workspace, it will not be
245 stored. In this case, you will have to manually wrap your layout in such a
246 container:
247
248 --------------------------------------------------------------------------------
249 // vim:ts=4:sw=4:et
250 {
251     // this is a manually added container to restore the vertical split
252     "layout": "splitv",
253     "percent": 0.5,
254     "type": "con",
255     "nodes": [
256
257         // the dumped workspace layout goes here
258
259     ]
260 }
261 --------------------------------------------------------------------------------
262
263 === Placeholders using window title matches don't swallow the window
264
265 If you use the +title+ attribute to match a window and find that it doesn't
266 work or only works sometimes, the reason might be that the application sets the
267 title only after making the window visible. This will be especially true for
268 programs running inside terminal emulators, e.g., +urxvt -e irssi+ when
269 matching on +title: "irssi"+.
270
271 One way to deal with this is to not rely on the title, but instead use, e.g.,
272 the +instance+ attribute and running the program to set this window instance to
273 that value:
274
275 --------------------------------------------------------------------------------
276 # Run irssi via
277 # urxvt -name "irssi-container" -e irssi
278
279 "swallows": [
280     {
281         "class": "URxvt",
282         "instance": "irssi-container"
283     }
284 ]
285 --------------------------------------------------------------------------------