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 print "workspace_layout$parameters\n";
124 # workspace_bar is gone, you should use i3bar now
125 if ($statement eq 'workspace_bar') {
126 $workspace_bar = ($parameters =~ /\s+(yes|true|on|enable|active)/);
127 print "# XXX: REMOVED workspace_bar line. There is no internal workspace bar in v4.\n";
131 # new_window changed the parameters from bb to none etc.
132 if ($statement eq 'new_window') {
133 if ($parameters =~ /bb/) {
134 print "new_window none\n";
135 } elsif ($parameters =~ /bp/) {
136 print "new_window 1pixel\n";
137 } elsif ($parameters =~ /bn/) {
138 print "new_window normal\n";
140 print "# XXX: Invalid parameter for new_window, not touching line:\n";
146 # bar colors are obsolete, need to be configured in i3bar
147 if ($statement =~ /^bar\./) {
148 print "# XXX: REMOVED $statement, configure i3bar instead.\n";
149 print "# Old line: $line\n";
153 # one form of this is still ok (workspace assignments), the other (named workspaces) isn’t
154 if ($statement eq 'workspace') {
155 my ($number, $params) = ($parameters =~ /\s+([0-9]+)\s+(.+)/);
156 if ($params =~ /^output/) {
160 print "# XXX: workspace name will end up in the corresponding bindings.\n";
165 if ($statement eq 'bind' || $statement eq 'bindsym') {
166 convert_command($line);
170 print "# XXX: migration script could not handle line: $line\n";
174 # Converts a command (after bind/bindsym)
176 sub convert_command {
179 my @unchanged_cmds = qw(
188 my ($statement, $key, $command) = ($line =~ /([a-zA-Z_-]+)\s+([^\s]+)\s+(.*)/);
190 # turn 'bind' to 'bindcode'
191 $statement = 'bindcode' if $statement eq 'bind';
193 # check if it’s one of the unchanged commands
194 my ($cmd) = ($command =~ /([a-zA-Z_-]+)/);
195 if ($cmd ~~ @unchanged_cmds) {
196 print "$statement $key $command\n";
200 # simple replacements
202 qr/^s/ => 'layout stacking',
203 qr/^d/ => 'layout toggle split',
204 qr/^T/ => 'layout tabbed',
205 qr/^f($|[^go])/ => 'fullscreen',
206 qr/^fg/ => 'fullscreen global',
207 qr/^t/ => 'floating toggle',
208 qr/^h/ => 'focus left',
209 qr/^j($|[^u])/ => 'focus down',
210 qr/^k/ => 'focus up',
211 qr/^l/ => 'focus right',
212 qr/^mh/ => 'move left',
213 qr/^mj/ => 'move down',
214 qr/^mk/ => 'move up',
215 qr/^ml/ => 'move right',
216 qr/^bn/ => 'border normal',
217 qr/^bp/ => 'border 1pixel',
218 qr/^bb/ => 'border none',
219 qr/^bt/ => 'border toggle',
220 qr/^pw/ => 'workspace prev',
221 qr/^nw/ => 'workspace next',
225 for (my $c = 0; $c < @replace; $c += 2) {
226 if ($command =~ $replace[$c]) {
227 $command = $replace[$c+1];
233 # goto command is now obsolete due to criteria + focus command
234 if ($command =~ /^goto/) {
235 my ($mark) = ($command =~ /^goto\s+(.*)/);
236 print qq|$statement $key [con_mark="$mark"] focus\n|;
240 # the jump command is also obsolete due to criteria + focus
241 if ($command =~ /^jump/) {
242 my ($params) = ($command =~ /^jump\s+(.*)/);
243 if ($params =~ /^"/) {
244 # jump ["]window class[/window title]["]
245 ($params) = ($params =~ /^"([^"]+)"/);
247 # check if a window title was specified
248 if ($params =~ m,/,) {
249 my ($class, $title) = ($params =~ m,([^/]+)/(.+),);
250 print qq|$statement $key [class="$class" title="$title"] focus\n|;
252 print qq|$statement $key [class="$params"] focus\n|;
256 # jump <workspace> [ column row ]
257 print "# XXX: jump workspace is obsolete in 4.x: $line\n";
262 if (!$replaced && $command =~ /^focus/) {
263 my ($what) = ($command =~ /^focus\s+(.*)/);
266 $what = 'mode_toggle';
267 } elsif ($what eq 'floating' || $what eq 'tiling') {
268 # those are unchanged
270 print "# XXX: focus <number> is obsolete in 4.x: $line\n";
273 print qq|$statement $key focus $what\n|;
277 if ($command =~ /^mode/) {
278 my ($parameters) = ($command =~ /^mode\s+(.*)/);
279 print qq|$statement $key mode "$parameters"\n|;
283 # the parameters of the resize command have changed
284 if ($command =~ /^resize/) {
285 # OLD: resize <left|right|top|bottom> [+|-]<pixels>\n")
286 # NEW: resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt]
287 my ($direction, $sign, $px) = ($command =~ /^resize\s+(left|right|top|bottom)\s+([+-])([0-9]+)/);
289 $cmd .= ($sign eq '+' ? 'grow' : 'shrink') . ' ';
290 if ($direction eq 'top') {
292 } elsif ($direction eq 'bottom') {
295 $cmd .= "$direction ";
297 print qq|$statement $key $cmd\n|;
302 if ($command =~ /^[0-9]+/) {
303 my ($number) = ($command =~ /^([0-9]+)/);
304 if (exists $workspace_names{$number}) {
305 print qq|$statement $key workspace $workspace_names{$number}\n|;
308 print qq|$statement $key workspace $number\n|;
314 if ($command =~ /^m[0-9]+/) {
315 my ($number) = ($command =~ /^m([0-9]+)/);
316 if (exists $workspace_names{$number}) {
317 print qq|$statement $key move container to workspace $workspace_names{$number}\n|;
320 print qq|$statement $key move container to workspace $number\n|;
325 # With Container-commands are now obsolete due to different abstraction
326 if ($command =~ /^wc/) {
327 $command =~ s/^wc//g;
329 for (my $c = 0; $c < @replace; $c += 2) {
330 if ($command =~ $replace[$c]) {
331 $command = $replace[$c+1];
337 print "# XXX: migration script could not handle command: $line\n";
339 # NOTE: This is not 100% accurate, as it only works for one level
340 # of nested containers. As this is a common use case, we use 'focus
341 # parent; $command' nevertheless. For advanced use cases, the user
342 # has to modify their config.
343 print "$statement $key focus parent; $command\n";
349 print "$statement $key $command\n";
351 print "# XXX: migration script could not handle command: $line\n";
356 # add an i3bar invocation automatically if no 'workspace_bar no' was found
357 if ($workspace_bar) {
359 print "# XXX: Automatically added a bar configuration\n";
361 print " status_command i3status\n";