2 # vim:ts=4:sw=4:expandtab
4 # script to migrate an old config file (i3 < 4.0) to the new format (>= 4.0)
5 # this script only uses modules which come with perl 5.10
7 # reads an i3 v3 config from stdin and spits out a v4 config on stdout
9 # 0 = the input was a v3 config
10 # 1 = the input was already a v4 config
11 # (the config is printed to stdout nevertheless)
13 # © 2011 Michael Stapelberg and contributors, see LICENSE
20 # is this a version 3 config file? disables auto-detection
22 my $result = GetOptions('v3' => \$v3);
41 client.focused_inactive
48 my $workspace_bar = 1;
51 my @lines = split /\n/, $input;
53 # remove whitespaces in the beginning of lines
54 @lines = map { s/^[ \t]*//g; $_ } @lines;
56 # Try to auto-detect if this is a v3 config file.
58 # If the user passed --v3, we need to convert in any case
61 for my $line (@lines) {
62 # only v4 configfiles can use bindcode or workspace_layout
63 return 0 if $line =~ /^bindcode/ ||
64 $line =~ /^workspace_layout/ ||
65 $line =~ /^force_focus_wrapping/;
67 # have a look at bindings
68 next unless $line =~ /^bind/;
70 my ($statement, $key, $command) = ($line =~ /([a-zA-Z_-]+)\s+([^\s]+)\s+(.*)/);
71 return 0 if $command =~ /^layout/ ||
72 $command =~ /^floating/ ||
73 $command =~ /^workspace/ ||
74 $command =~ /^focus (left|right|up|down)/ ||
75 $command =~ /^border (normal|1pixel|none)/;
81 if (!need_to_convert()) {
82 # If this is already a v4 config file, we will spit out the lines
83 # and exit with return code 1
88 # first pass: get workspace names
89 for my $line (@lines) {
90 next if $line =~ /^#/ || $line =~ /^$/;
92 my ($statement, $parameters) = ($line =~ /([a-zA-Z._-]+)(.*)/);
94 # skip everything but workspace lines
95 next unless defined($statement) and $statement eq 'workspace';
97 my ($number, $params) = ($parameters =~ /\s+([0-9]+)\s+(.+)/);
99 # save workspace name (unless the line is actually a workspace assignment)
100 $workspace_names{$number} = $params unless $params =~ /^output/;
103 for my $line (@lines) {
104 # directly use comments and empty lines
105 if ($line =~ /^#/ || $line =~ /^$/) {
110 my ($statement, $parameters) = ($line =~ /([a-zA-Z._-]+)(.*)/);
112 # directly use lines which have not changed between 3.x and 4.x
113 if (!defined($statement) || (lc $statement ~~ @unchanged)) {
118 # new_container changed only the statement name to workspace_layout
119 if ($statement eq 'new_container') {
120 # TODO: new_container stack-limit
121 print "workspace_layout$parameters\n";
125 # workspace_bar is gone, you should use i3bar now
126 if ($statement eq 'workspace_bar') {
127 $workspace_bar = ($parameters =~ /\s+(yes|true|on|enable|active)/);
128 print "# XXX: REMOVED workspace_bar line. There is no internal workspace bar in v4.\n";
132 # new_window changed the parameters from bb to none etc.
133 if ($statement eq 'new_window') {
134 if ($parameters =~ /bb/) {
135 print "new_window none\n";
136 } elsif ($parameters =~ /bp/) {
137 print "new_window 1pixel\n";
138 } elsif ($parameters =~ /bn/) {
139 print "new_window normal\n";
141 print "# XXX: Invalid parameter for new_window, not touching line:\n";
147 # bar colors are obsolete, need to be configured in i3bar
148 if ($statement =~ /^bar\./) {
149 print "# XXX: REMOVED $statement, configure i3bar instead.\n";
150 print "# Old line: $line\n";
154 # one form of this is still ok (workspace assignments), the other (named workspaces) isn’t
155 if ($statement eq 'workspace') {
156 my ($number, $params) = ($parameters =~ /\s+([0-9]+)\s+(.+)/);
157 if ($params =~ /^output/) {
161 print "# XXX: workspace name will end up in the corresponding bindings.\n";
166 if ($statement eq 'bind' || $statement eq 'bindsym') {
167 convert_command($line);
171 print "# XXX: migration script could not handle line: $line\n";
175 # Converts a command (after bind/bindsym)
177 sub convert_command {
180 my @unchanged_cmds = qw(
190 my ($statement, $key, $command) = ($line =~ /([a-zA-Z_-]+)\s+([^\s]+)\s+(.*)/);
192 # turn 'bind' to 'bindcode'
193 $statement = 'bindcode' if $statement eq 'bind';
195 # check if it’s one of the unchanged commands
196 my ($cmd) = ($command =~ /([a-zA-Z_-]+)/);
197 if ($cmd ~~ @unchanged_cmds) {
198 print "$statement $key $command\n";
202 # simple replacements
204 qr/^s/ => 'layout stacking',
205 qr/^d/ => 'layout toggle split',
206 qr/^T/ => 'layout tabbed',
207 qr/^f($|[^go])/ => 'fullscreen',
208 qr/^fg/ => 'fullscreen global',
209 qr/^t/ => 'floating toggle',
210 qr/^h/ => 'focus left',
211 qr/^j($|[^u])/ => 'focus down',
212 qr/^k/ => 'focus up',
213 qr/^l/ => 'focus right',
214 qr/^mh/ => 'move left',
215 qr/^mj/ => 'move down',
216 qr/^mk/ => 'move up',
217 qr/^ml/ => 'move right',
218 qr/^bn/ => 'border normal',
219 qr/^bp/ => 'border 1pixel',
220 qr/^bb/ => 'border none',
221 qr/^bt/ => 'border toggle',
222 qr/^pw/ => 'workspace prev',
223 qr/^nw/ => 'workspace next',
227 for (my $c = 0; $c < @replace; $c += 2) {
228 if ($command =~ $replace[$c]) {
229 $command = $replace[$c+1];
235 # goto command is now obsolete due to criteria + focus command
236 if ($command =~ /^goto/) {
237 my ($mark) = ($command =~ /^goto\s+(.*)/);
238 print qq|$statement $key [con_mark="$mark"] focus\n|;
242 # the jump command is also obsolete due to criteria + focus
243 if ($command =~ /^jump/) {
244 my ($params) = ($command =~ /^jump\s+(.*)/);
245 if ($params =~ /^"/) {
246 # jump ["]window class[/window title]["]
247 ($params) = ($params =~ /^"([^"]+)"/);
249 # check if a window title was specified
250 if ($params =~ m,/,) {
251 my ($class, $title) = ($params =~ m,([^/]+)/(.+),);
252 print qq|$statement $key [class="$class" title="$title"] focus\n|;
254 print qq|$statement $key [class="$params"] focus\n|;
258 # jump <workspace> [ column row ]
259 print "# XXX: jump workspace is obsolete in 4.x: $line\n";
264 if (!$replaced && $command =~ /^focus/) {
265 my ($what) = ($command =~ /^focus\s+(.*)/);
268 $what = 'mode_toggle';
269 } elsif ($what eq 'floating' || $what eq 'tiling') {
270 # those are unchanged
272 print "# XXX: focus <number> is obsolete in 4.x: $line\n";
275 print qq|$statement $key focus $what\n|;
279 if ($command =~ /^mode/) {
280 my ($parameters) = ($command =~ /^mode\s+(.*)/);
281 print qq|$statement $key mode "$parameters"\n|;
285 # the parameters of the resize command have changed
286 if ($command =~ /^resize/) {
287 # OLD: resize <left|right|top|bottom> [+|-]<pixels>\n")
288 # NEW: resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt]
289 my ($direction, $sign, $px) = ($command =~ /^resize\s+(left|right|top|bottom)\s+([+-])([0-9]+)/);
291 $cmd .= ($sign eq '+' ? 'grow' : 'shrink') . ' ';
292 if ($direction eq 'top') {
294 } elsif ($direction eq 'bottom') {
297 $cmd .= "$direction ";
299 print qq|$statement $key $cmd\n|;
304 if ($command =~ /^[0-9]+/) {
305 my ($number) = ($command =~ /^([0-9]+)/);
306 if (exists $workspace_names{$number}) {
307 print qq|$statement $key workspace $workspace_names{$number}\n|;
310 print qq|$statement $key workspace $number\n|;
316 if ($command =~ /^m[0-9]+/) {
317 my ($number) = ($command =~ /^m([0-9]+)/);
318 if (exists $workspace_names{$number}) {
319 print qq|$statement $key move container to workspace $workspace_names{$number}\n|;
322 print qq|$statement $key move container to workspace $number\n|;
327 # With Container-commands are now obsolete due to different abstraction
328 if ($command =~ /^wc/) {
329 $command =~ s/^wc//g;
331 for (my $c = 0; $c < @replace; $c += 2) {
332 if ($command =~ $replace[$c]) {
333 $command = $replace[$c+1];
339 print "# XXX: migration script could not handle command: $line\n";
341 # NOTE: This is not 100% accurate, as it only works for one level
342 # of nested containers. As this is a common use case, we use 'focus
343 # parent; $command' nevertheless. For advanced use cases, the user
344 # has to modify his config.
345 print "$statement $key focus parent; $command\n";
351 print "$statement $key $command\n";
353 print "# XXX: migration script could not handle command: $line\n";
358 # add an i3bar invocation automatically if no 'workspace_bar no' was found
359 if ($workspace_bar) {
361 print "# XXX: Automatically added a bar configuration\n";
363 print " status_command i3status\n";