/usr/bin/i3-migrate-config-to-v4 is in i3-wm 4.14.1-1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | #!/usr/bin/env perl
# vim:ts=4:sw=4:expandtab
#
# script to migrate an old config file (i3 < 4.0) to the new format (>= 4.0)
# this script only uses modules which come with perl 5.10
#
# reads an i3 v3 config from stdin and spits out a v4 config on stdout
# exit codes:
# 0 = the input was a v3 config
# 1 = the input was already a v4 config
# (the config is printed to stdout nevertheless)
#
# © 2011 Michael Stapelberg and contributors, see LICENSE
use strict;
use warnings;
use Getopt::Long;
use v5.10;
# is this a version 3 config file? disables auto-detection
my $v3 = 0;
my $result = GetOptions('v3' => \$v3);
# reads stdin
sub slurp {
local $/;
<>;
}
my @unchanged = qw(
font
set
mode
exec
assign
floating_modifier
focus_follows_mouse
ipc-socket
ipc_socket
client.focused
client.focused_inactive
client.unfocused
client.urgent
client.background
);
my %workspace_names;
my $workspace_bar = 1;
my $input = slurp();
my @lines = split /\n/, $input;
# remove whitespaces in the beginning of lines
@lines = map { s/^[ \t]*//g; $_ } @lines;
# Try to auto-detect if this is a v3 config file.
sub need_to_convert {
# If the user passed --v3, we need to convert in any case
return 1 if $v3;
for my $line (@lines) {
# only v4 configfiles can use bindcode or workspace_layout
return 0 if $line =~ /^bindcode/ ||
$line =~ /^workspace_layout/ ||
$line =~ /^force_focus_wrapping/;
# have a look at bindings
next unless $line =~ /^bind/;
my ($statement, $key, $command) = ($line =~ /([a-zA-Z_-]+)\s+([^\s]+)\s+(.*)/);
return 0 if $command =~ /^layout/ ||
$command =~ /^floating/ ||
$command =~ /^workspace/ ||
$command =~ /^focus (left|right|up|down)/ ||
$command =~ /^border (normal|1pixel|none)/;
}
return 1;
}
if (!need_to_convert()) {
# If this is already a v4 config file, we will spit out the lines
# and exit with return code 1
print $input;
exit 1;
}
# first pass: get workspace names
for my $line (@lines) {
next if $line =~ /^#/ || $line =~ /^$/;
my ($statement, $parameters) = ($line =~ /([a-zA-Z._-]+)(.*)/);
# skip everything but workspace lines
next unless defined($statement) and $statement eq 'workspace';
my ($number, $params) = ($parameters =~ /\s+([0-9]+)\s+(.+)/);
# save workspace name (unless the line is actually a workspace assignment)
$workspace_names{$number} = $params unless $params =~ /^output/;
}
for my $line (@lines) {
# directly use comments and empty lines
if ($line =~ /^#/ || $line =~ /^$/) {
print "$line\n";
next;
}
my ($statement, $parameters) = ($line =~ /([a-zA-Z._-]+)(.*)/);
# directly use lines which have not changed between 3.x and 4.x
if (!defined($statement) || (lc $statement ~~ @unchanged)) {
print "$line\n";
next;
}
# new_container changed only the statement name to workspace_layout
if ($statement eq 'new_container') {
print "workspace_layout$parameters\n";
next;
}
# workspace_bar is gone, you should use i3bar now
if ($statement eq 'workspace_bar') {
$workspace_bar = ($parameters =~ /\s+(yes|true|on|enable|active)/);
print "# XXX: REMOVED workspace_bar line. There is no internal workspace bar in v4.\n";
next;
}
# new_window changed the parameters from bb to none etc.
if ($statement eq 'new_window') {
if ($parameters =~ /bb/) {
print "new_window none\n";
} elsif ($parameters =~ /bp/) {
print "new_window 1pixel\n";
} elsif ($parameters =~ /bn/) {
print "new_window normal\n";
} else {
print "# XXX: Invalid parameter for new_window, not touching line:\n";
print "$line\n";
}
next;
}
# bar colors are obsolete, need to be configured in i3bar
if ($statement =~ /^bar\./) {
print "# XXX: REMOVED $statement, configure i3bar instead.\n";
print "# Old line: $line\n";
next;
}
# one form of this is still ok (workspace assignments), the other (named workspaces) isn’t
if ($statement eq 'workspace') {
my ($number, $params) = ($parameters =~ /\s+([0-9]+)\s+(.+)/);
if ($params =~ /^output/) {
print "$line\n";
next;
} else {
print "# XXX: workspace name will end up in the corresponding bindings.\n";
next;
}
}
if ($statement eq 'bind' || $statement eq 'bindsym') {
convert_command($line);
next;
}
print "# XXX: migration script could not handle line: $line\n";
}
#
# Converts a command (after bind/bindsym)
#
sub convert_command {
my ($line) = @_;
my @unchanged_cmds = qw(
exec
mark
kill
restart
reload
exit
);
my ($statement, $key, $command) = ($line =~ /([a-zA-Z_-]+)\s+([^\s]+)\s+(.*)/);
# turn 'bind' to 'bindcode'
$statement = 'bindcode' if $statement eq 'bind';
# check if it’s one of the unchanged commands
my ($cmd) = ($command =~ /([a-zA-Z_-]+)/);
if ($cmd ~~ @unchanged_cmds) {
print "$statement $key $command\n";
return;
}
# simple replacements
my @replace = (
qr/^s/ => 'layout stacking',
qr/^d/ => 'layout toggle split',
qr/^T/ => 'layout tabbed',
qr/^f($|[^go])/ => 'fullscreen',
qr/^fg/ => 'fullscreen global',
qr/^t/ => 'floating toggle',
qr/^h/ => 'focus left',
qr/^j($|[^u])/ => 'focus down',
qr/^k/ => 'focus up',
qr/^l/ => 'focus right',
qr/^mh/ => 'move left',
qr/^mj/ => 'move down',
qr/^mk/ => 'move up',
qr/^ml/ => 'move right',
qr/^bn/ => 'border normal',
qr/^bp/ => 'border 1pixel',
qr/^bb/ => 'border none',
qr/^bt/ => 'border toggle',
qr/^pw/ => 'workspace prev',
qr/^nw/ => 'workspace next',
);
my $replaced = 0;
for (my $c = 0; $c < @replace; $c += 2) {
if ($command =~ $replace[$c]) {
$command = $replace[$c+1];
$replaced = 1;
last;
}
}
# goto command is now obsolete due to criteria + focus command
if ($command =~ /^goto/) {
my ($mark) = ($command =~ /^goto\s+(.*)/);
print qq|$statement $key [con_mark="$mark"] focus\n|;
return;
}
# the jump command is also obsolete due to criteria + focus
if ($command =~ /^jump/) {
my ($params) = ($command =~ /^jump\s+(.*)/);
if ($params =~ /^"/) {
# jump ["]window class[/window title]["]
($params) = ($params =~ /^"([^"]+)"/);
# check if a window title was specified
if ($params =~ m,/,) {
my ($class, $title) = ($params =~ m,([^/]+)/(.+),);
print qq|$statement $key [class="$class" title="$title"] focus\n|;
} else {
print qq|$statement $key [class="$params"] focus\n|;
}
return;
} else {
# jump <workspace> [ column row ]
print "# XXX: jump workspace is obsolete in 4.x: $line\n";
return;
}
}
if (!$replaced && $command =~ /^focus/) {
my ($what) = ($command =~ /^focus\s+(.*)/);
$what =~ s/\s//g;
if ($what eq 'ft') {
$what = 'mode_toggle';
} elsif ($what eq 'floating' || $what eq 'tiling') {
# those are unchanged
} else {
print "# XXX: focus <number> is obsolete in 4.x: $line\n";
return;
}
print qq|$statement $key focus $what\n|;
return;
}
if ($command =~ /^mode/) {
my ($parameters) = ($command =~ /^mode\s+(.*)/);
print qq|$statement $key mode "$parameters"\n|;
return;
}
# the parameters of the resize command have changed
if ($command =~ /^resize/) {
# OLD: resize <left|right|top|bottom> [+|-]<pixels>\n")
# NEW: resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt]
my ($direction, $sign, $px) = ($command =~ /^resize\s+(left|right|top|bottom)\s+([+-])([0-9]+)/);
my $cmd = 'resize ';
$cmd .= ($sign eq '+' ? 'grow' : 'shrink') . ' ';
if ($direction eq 'top') {
$direction = 'up';
} elsif ($direction eq 'bottom') {
$direction = 'down';
}
$cmd .= "$direction ";
$cmd .= "$px px";
print qq|$statement $key $cmd\n|;
return;
}
# switch workspace
if ($command =~ /^[0-9]+/) {
my ($number) = ($command =~ /^([0-9]+)/);
if (exists $workspace_names{$number}) {
print qq|$statement $key workspace $workspace_names{$number}\n|;
return;
} else {
print qq|$statement $key workspace $number\n|;
return;
}
}
# move to workspace
if ($command =~ /^m[0-9]+/) {
my ($number) = ($command =~ /^m([0-9]+)/);
if (exists $workspace_names{$number}) {
print qq|$statement $key move container to workspace $workspace_names{$number}\n|;
return;
} else {
print qq|$statement $key move container to workspace $number\n|;
return;
}
}
# With Container-commands are now obsolete due to different abstraction
if ($command =~ /^wc/) {
$command =~ s/^wc//g;
my $wc_replaced = 0;
for (my $c = 0; $c < @replace; $c += 2) {
if ($command =~ $replace[$c]) {
$command = $replace[$c+1];
$wc_replaced = 1;
last;
}
}
if (!$wc_replaced) {
print "# XXX: migration script could not handle command: $line\n";
} else {
# NOTE: This is not 100% accurate, as it only works for one level
# of nested containers. As this is a common use case, we use 'focus
# parent; $command' nevertheless. For advanced use cases, the user
# has to modify their config.
print "$statement $key focus parent; $command\n";
}
return;
}
if ($replaced) {
print "$statement $key $command\n";
} else {
print "# XXX: migration script could not handle command: $line\n";
}
}
# add an i3bar invocation automatically if no 'workspace_bar no' was found
if ($workspace_bar) {
print "\n";
print "# XXX: Automatically added a bar configuration\n";
print "bar {\n";
print " status_command i3status\n";
print "}\n";
}
|