package Xconfigurator;

use diagnostics;
use strict;
use vars qw($in $install $resolution_wanted @depths %depths @resolutions @accelservers @allservers %videomemory @ramdac_name @ramdac_id @clockchip_name @clockchip_id %keymap_translate @vsync_range %standard_monitors $intro_text $finalcomment_text $s3_comment $cirrus_comment $probeonlywarning_text $monitorintro_text $hsyncintro_text $vsyncintro_text $XF86firstchunk_text $keyboardsection_start $keyboardsection_part2 $keyboardsection_end $pointersection_text1 $pointersection_text2 $monitorsection_text1 $monitorsection_text2 $monitorsection_text3 $monitorsection_text4 $modelines_text_Trident_TG_96xx $modelines_text $devicesection_text $screensection_text1);

use pci_probing::main;
use common qw(:common :file);
use log;

use Xconfigurator_consts;
use my_gtk qw(:wrappers);

my $tmpconfig = "/tmp/Xconfig";

my ($prefix, %cards, %monitors);

1;

sub setVirtual($) {
    my $vt = '';
    local *C; 
    sysopen C, "/dev/console", 2 or die "failed to open /dev/console: $!";
    ioctl(C, c::VT_GETSTATE(), $vt) or die "ioctl VT_GETSTATE failed";
    ioctl(C, c::VT_ACTIVATE(), $_[0]) or die "ioctl VT_ACTIVATE failed";
    ioctl(C, c::VT_WAITACTIVE(), $_[0]) or die "ioctl VT_WAITACTIVE failed";
    unpack "S", $vt;
}

sub readCardsDB {
    my ($file) = @_;
    my ($card);

    %cards and return;

    local *F;
    open F, $file or die "file $file not found";

    my ($lineno, $cmd, $val) = 0;
    my $fs = { 
        LINE => sub { push @{$card->{lines}}, $val unless $val eq "VideoRam" },
	NAME => sub {
	    $cards{$card->{type}} = $card if $card;
	    $card = { type => $val };
	},
	SEE => sub {
	    my $c = $cards{$val} or die "Error in database, invalid reference $val at line $lineno";
	    
	    push @{$card->{lines}}, @{$c->{lines} || []};
	    add2hash($card->{flags}, $c->{flags});
	    add2hash($card, $c);
	},
	CHIPSET => sub { 
	    $card->{chipset} = $val; 
	    $card->{flags}{needVideoRam} = 1 if member($val, qw(mgag10 mgag200 RIVA128));
	},
	SERVER => sub { $card->{server} = $val; },
	RAMDAC => sub { $card->{ramdac} = $val; },
	DACSPEED => sub { $card->{dacspeed} = $val; },
	CLOCKCHIP => sub { $card->{clockchip} = $val; $card->{flags}{noclockprobe} = 1; },
	NOCLOCKPROBE => sub { $card->{flags}{noclockprobe} = 1 },
	UNSUPPORTED => sub { $card->{flags}{unsupported} = 1 },
	COMMENT => sub {},
    };

    foreach (<F>) { $lineno++;
	s/\s+$//;
	/^#/ and next;
	/^$/ and next;
	/^END/ and last;

	($cmd, $val) = /(\S+)\s*(.*)/ or log::l("bad line $lineno ($_)"), next;

	my $f = $fs->{$cmd};

	$f ? &$f() : log::l("unknown line $lineno ($_)");
    }
    push @{$cards{S3}{lines}}, $s3_comment;
    push @{$cards{'CL-GD'}{lines}}, $cirrus_comment;

    #- this entry is broken in X11R6 cards db 
    $cards{I128}{flags}{noclockprobe} = 1;
}

sub readMonitorsDB {
    my ($file) = @_;

    %monitors and return;

    local *F;
    open F, $file or die "can't open monitors database ($file): $!";
    my $lineno = 0; foreach (<F>) {
	$lineno++;
	s/\s+$//;
	/^#/ and next;
	/^$/ and next;

	my @fields = qw(type bandwidth hsyncrange vsyncrange);
	my @l = split /\s*;\s*/;
	@l == @fields or log::l("bad line $lineno ($_)"), next;
	
	my %l; @l{@fields} = @l;
	$monitors{$l{type}} = \%l;
    }
    while (my ($k, $v) = each %standard_monitors) {
	$monitors{$k} = 
	  $monitors{$v->[0]} = 
	    { hsyncrange => $v->[1], vsyncrange => $v->[2] };
    }
}

sub rewriteInittab {
    my ($runlevel) = @_;
    {
	local (*F, *G);
	open F, "$prefix/etc/inittab" or die "cannot open $prefix/etc/inittab: $!";
	open G, "> $prefix/etc/inittab-" or die "cannot write in $prefix/etc/inittab-: $!";
    
	foreach (<F>) {
	    print G /^(id:)[35](:initdefault:)\s*$/ ? "$1$runlevel$2\n" : $_; # **
	}
    }
    unlink("$prefix/etc/inittab");
    rename("$prefix/etc/inittab-", "$prefix/etc/inittab");
}

sub keepOnlyLegalModes {
    my ($card) = @_;
    my $mem = 1024 * ($card->{memory} || return);

    while (my ($depth, $res) = each %{$card->{depth}}) {
	@$res = grep { $mem >= product(@$_, $depth / 8)	} @$res;
    }
}

sub cardConfigurationAuto() {
    my $card;
    if (my ($c) = pci_probing::main::probe("DISPLAY")) {
	local $_;
	($card->{identifier}, $_) = @$c;
	$card->{type} = $1 if /Card:(.*)/;
	$card->{server} = $1 if /Server:(.*)/;
    }
    $card;
}

sub cardConfiguration(;$$) {
    my ($card, $noauto) = @_;
    $card ||= {};

    readCardsDB("$prefix/usr/X11R6/lib/X11/Cards");

    add2hash($card, $cards{$card->{type}}) if $card->{type}; #- try to get info from given type
    $card->{type} = undef unless $card->{server}; #- bad type as we can't find the server
	
    add2hash($card, cardConfigurationAuto()) unless $card->{server} || $noauto;
    add2hash($card, { type => $in->ask_from_list('', _("Choose a graphic card"), [keys %cards]) }) unless $card->{type} || $card->{server};

    add2hash($card, $cards{$card->{type}}) if $card->{type};
    add2hash($card, { vendor => "Unknown", board => "Unknown" });

    $card->{prog} = "/usr/X11R6/bin/XF86_$card->{server}";

    -x "$prefix$card->{prog}" or !defined $install or &$install($card->{server});
    -x "$prefix$card->{prog}" or die "server $card->{server} is not available (should be in $prefix$card->{prog})";
	
    unless ($::testing) {
	unlink("$prefix/etc/X11/X");
	symlink("../..$card->{prog}", "$prefix/etc/X11/X");
    }

    unless ($card->{type}) {
	$card->{flags}{noclockprobe} = member($card->{server}, qw(I128 S3 S3V Mach64));
    }

    $card->{flags}{needVideoRam} and
      $card->{memory} ||= 
	$videomemory{$in->ask_from_list_('', 
					 _("Select the memory size of your graphic card"), 
					 [ sort { $videomemory{$a} <=> $videomemory{$b} } 
					   keys %videomemory])};
    $card;
}

sub monitorConfiguration(;$) {
    my $monitor = shift || {};

    $monitor->{hsyncrange} && $monitor->{vsyncrange} and return $monitor;

    readMonitorsDB(-e "MonitorsDB" ? "MonitorsDB" : "/usr/share/MonitorsDB");

    add2hash($monitor, { type => $in->ask_from_list('', _("Choose a monitor"), [keys %monitors]) }) unless $monitor->{type};
    add2hash($monitor, $monitors{$monitor->{type}});
    add2hash($monitor, { type => "Unknown", vendor => "Unknown", model => "Unknown" });
    $monitor;
}

sub testConfig($) {
    my ($o) = @_;
    my ($resolutions, $clocklines);

    write_XF86Config($o, $tmpconfig);

    local *F;
    open F, "$prefix$o->{card}{prog} :9 -probeonly -pn -xf86config $tmpconfig 2>&1 |";
    foreach (<F>) {
	$o->{card}{memory} ||= $2 if /(videoram|Video RAM):\s*(\d*)/;

	# look for clocks
	push @$clocklines, $1 if /clocks: (.*)/ && !/(pixel |num)clocks:/;

	push @$resolutions, [ $1, $2 ] if /: Mode "(\d+)x(\d+)": mode clock/;
	print;
    }
    close F or die "X probeonly failed";

    ($resolutions, $clocklines);
}

sub testFinalConfig($;$) {
    my ($o, $auto) = @_;

    $o->{monitor}{hsyncrange} && $o->{monitor}{vsyncrange} or
      $in->ask_warn('', _("Monitor not configured")), return;

    $o->{card}{server} or
      $in->ask_warn('', _("Graphic card not configured yet")), return;

    $o->{card}{depth} or
      $in->ask_warn('', _("Resolutions not chosen yet")), return;

    rename("$prefix/etc/X11/XF86Config", "$prefix/etc/X11/XF86Config.old") || die "unable to make a backup of XF86Config" unless $::testing;

    write_XF86Config($o, $::testing ? $tmpconfig : "$prefix/etc/X11/XF86Config");

    $auto 
      or $in->ask_yesorno(_("Test configuration"), _("Do you want to test the configuration?"))
      or return 1;

    my $pid; unless ($pid = fork) {
	my @l = "X";
	@l = ($o->{card}{prog}, "-xf86config", $tmpconfig) if $::testing;
	chroot $prefix if $prefix;
	exec @l, ":9" or exit 1;
    }
    do { sleep 1; } until (c::Xtest(':0'));

    #- create a link from the non-prefixed /tmp/.X11-unix/X9 to the prefixed one
    #- that way, you can talk to :9 without doing a chroot
    unlink "/tmp/.X11-unix/X9" if $prefix;
    symlink "$prefix/tmp/.X11-unix/X9", "/tmp/.X11-unix/X9" if $prefix;

    local *F;
    open F, "|perl" or die;
    print F "use lib qw(", join(' ', @INC), ");\n";
    print F q{
	use interactive_gtk;
        use my_gtk qw(:wrappers);

	$ENV{DISPLAY} = ":9";
        gtkset_mousecursor(68);
        gtkset_background(200, 210, 210);
        my ($h, $w) = Gtk::Gdk::Window->new_foreign(Gtk::Gdk->ROOT_WINDOW)->get_size;
        $my_gtk::force_position = [ $w / 3, $h / 2.4 ];
	$my_gtk::force_focus = 1;
        my $text = Gtk::Label->new;
        my $time = 8;
        Gtk->timeout_add(1000, sub {
	    $text->set(_("(leaving in %d seconds)", $time));
	    $time-- or Gtk->main_quit;
	});

	exit (interactive_gtk->new->ask_yesorno('', [ _("Is this ok?"), $text ], 1) ? 0 : 222);
    };
    my $rc = close F;
    my $err = $?;

    unlink "/tmp/.X11-unix/X9" if $prefix;
    kill 2, $pid;

    $rc || $err == 222 << 8 or $in->ask_warn('', _("An error occurred, try changing some parameters"));
    $rc;
}

sub autoResolutions($;$) {
    my ($o, $nowarning) = @_;
    my $card = $o->{card};

    $nowarning || $in->ask_okcancel(_("Automatic resolutions"),
_("To find the available resolutions i will try different ones.
Your screen will blink... 
You can switch if off if you want, you'll hear a beep when it's over")) or return;

    #- swith to virtual console 1 (hopefully not X :)
    my $vt = setVirtual(1);

    #- Configure the modes order.
    my ($ok, $best);
    foreach (reverse @depths) {
	local $card->{default_depth} = $_;

	my ($resolutions, $clocklines) = eval { testConfig($o) };
	if ($@ || !$resolutions) {
	    delete $card->{depth}{$_};
	} else {
	    $card->{clocklines} ||= $clocklines unless $card->{flags}{noclockprobe};
	    $card->{depth}{$_} = [ @$resolutions ];
	}
    }

    #- restore the virtual console
    setVirtual($vt);
    print "\a"; #- beeeep!
}

sub autoDefaultDepth($$) {
    my ($card, $resolution_wanted) = @_;
    my ($wres_wanted) = split 'x', $resolution_wanted;
    my ($best, $depth);

    while (my ($d, $r) = each %{$card->{depth}}) {
	$depth = $depth ? max($depth, $d) : $d;

	# try to have $resolution_wanted
	$best = $best ? max($best, $d) : $d if $r->[0][0] >= $wres_wanted;
    }
    $best || $depth or die "no valid modes";
}

sub chooseResolutions($$) {
    my ($card, $chosen_depth) = @_;

    my $W = my_gtk->new(_("Resolution"));
    my %txt2depth = reverse %depths;
    my $chosen_w = 9999999; #- will be set by the combo callback
    my ($r, $depth_combo, %w2depth, %w2h, %w2widget);

    my $set_depth = sub { $depth_combo->entry->set_text(translate($depths{$chosen_depth})) };

    #- the set function is usefull to toggle the CheckButton with the callback being ignored
    my $ignore;
    my $set = sub { $ignore = 1; $_[0]->set_active(1); $ignore = 0; };

    while (my ($depth, $res) = each %{$card->{depth}}) {
	foreach (@$res) {
	    $w2h{$_->[0]} = $_->[1];
	    push @{$w2depth{$_->[0]}}, $depth;
	}
    }
    while (my ($w, $h) = each %w2h) {
	my $V = $w . "x" . $h;
	$w2widget{$w} = $r = new Gtk::RadioButton($r ? ($V, $r) : $V);
	$r->signal_connect("clicked" => sub {
			       $ignore and return;
			       $chosen_w = $w;
			       unless (member($chosen_depth, @{$w2depth{$w}})) {
				   $chosen_depth = max(@{$w2depth{$w}});
				   &$set_depth();
			       }
			   });
    }

    gtkadd($W->{window},
	   gtkpack_($W->create_box_with_title(_("Choose resolution and color depth")),
		    1, gtkpack(new Gtk::HBox(0,20),
			       $depth_combo = new Gtk::Combo,
			       gtkpack_(new Gtk::VBox(0,0),
					map { 0, $w2widget{$_} } ikeys(%w2widget),
					),
			       ),
		    0, $W->create_okcancel,
		    ));
    $depth_combo->disable_activate;
    $depth_combo->set_use_arrows_always(1);
    $depth_combo->entry->set_editable(0);
    $depth_combo->set_popdown_strings(map { translate($depths{$_}) } ikeys(%{$card->{depth}}));
    $depth_combo->entry->signal_connect(changed => sub {
       $chosen_depth = $txt2depth{untranslate($depth_combo->entry->get_text, keys %txt2depth)};
       my $w = $card->{depth}{$chosen_depth}[0][0];
       $chosen_w > $w and &$set($w2widget{$chosen_w = $w});
    });
    &$set_depth();

    $W->main or return;

    ($chosen_depth, $chosen_w);
}


sub resolutionsConfiguration($$) {
    my ($o, $option) = @_;
    my $card = $o->{card};
    my $auto = $option eq 'auto';
    my $nowarning = $auto || $option eq 'nowarning';
    my $noauto = $option eq 'noauto';

    #- For the mono and vga16 server, no further configuration is required.
    return if member($card->{server}, "Mono", "VGA16");

    #- some of these guys hate to be poked               
    #- if we dont know then its at the user's discretion
    #-my $manual ||= 
    #-	$card->{server} =~ /^(TGA|Mach32)/ || 
    #-	$card->{name} =~ /^Riva 128/ ||
    #-	$card->{chipset} =~ /^(RIVA128|mgag)/ ||
    #-	$::expert;
    #-
    #-my $unknown = 
    #-	member($card->{server}, qw(S3 S3V I128 Mach64)) ||
    #-	member($card->{type}, 
    #-	       "Matrox Millennium (MGA)",
    #-	       "Matrox Millennium II",
    #-	       "Matrox Millennium II AGP",
    #-	       "Matrox Mystique",
    #-	       "Matrox Mystique",
    #-	       "S3",
    #-	       "S3V",
    #-	       "I128",
    #-	      ) ||
    #-	$card->{type} =~ /S3 ViRGE/;
    #-
    #-$unknown and $manual ||= !$in->ask_okcancel('', [ _("I can try to autodetect information about graphic card, but it may freeze :("),
    #-							_("Do you want to try?") ]);
    
    unless ($card->{depth}) {
	$card->{depth}{$_} = [ map { [ split "x" ] } @resolutions ] 
	  foreach @depths;

	if ($nowarning || (!$noauto && $in->ask_okcancel(_("Automatic resolutions"), 
_("I can try to find the available resolutions (eg: 800x600).
Alas it can freeze sometimes
Do you want to try?")))) {
	    autoResolutions($o, $nowarning);
	}
    }

    #- sort resolutions in each depth
    foreach (values %{$card->{depth}}) {
	my $i;
	@$_ = grep { first($i != $_->[0], $i = $_->[0]) }
	  sort { $b->[0] <=> $a->[0] } @$_;
    }

    #- remove unusable resolutions (based on the video memory size)
    keepOnlyLegalModes($card);

    my $res = $o->{resolution_wanted} || $resolution_wanted;
    my $depth = $card->{default_depth} || autoDefaultDepth($card, $res);

    $auto or ($depth, $res) = chooseResolutions($card, $depth) or return;

    #- needed in auto mode when all has been provided by the user
    $card->{depth}{$depth} or die "you fixed an unusable depth";

    #- remove all biggest resolution (keep the small ones for ctl-alt-+)
    #- otherwise there'll be a virtual screen :(
    $card->{depth}{$depth} = [ grep { $_->[0] <= $res } @{$card->{depth}{$depth}} ];
    $card->{default_depth} = $depth;
    1;
}


#- Create the XF86Config file. 
sub write_XF86Config {
    my ($o, $file) = @_;
    my $O;

    local *F;
    open F, ">$file" or die "can't write XF86Config in $file: $!";

    print F $XF86firstchunk_text;

    #- Write keyboard section.     
    $O = $o->{keyboard};
    print F $keyboardsection_start;

    print F "    RightAlt        ", ($O->{altmeta} ? "ModeShift" : "Meta"), "\n";
    print F $keyboardsection_part2;
    print F qq(    XkbLayout       "$O->{xkb_keymap}"\n);
    print F $keyboardsection_end;

    #- Write pointer section.     
    $O = $o->{mouse};
    print F $pointersection_text1;
    print F qq(    Protocol    "$O->{XMOUSETYPE}"\n);
    print F qq(    Device      "$O->{device}"\n);
    #- this will enable the "wheel" or "knob" functionality if the mouse supports it 
    print F "    ZAxisMapping 4 5\n" if
      member($O->{XMOUSETYPE}, qw(IntelliMouse IMPS/2 ThinkingMousePS/2 NetScrollPS/2 NetMousePS/2 MouseManPlusPS/2));

    print F $pointersection_text2;
    print F "#" unless $O->{emulate3buttons};
    print F "    Emulate3Buttons\n";
    print F "#" unless $O->{emulate3buttons};
    print F "    Emulate3Timeout    50\n\n";
    print F "# ChordMiddle is an option for some 3-button Logitech mice\n\n";
    print F "#" unless $O->{chordmiddle};
    print F "    ChordMiddle\n\n";
    print F "    ClearDTR\n" if $O->{cleardtrrts};
    print F "    ClearRTS\n\n"  if $O->{cleardtrrts};
    print F "EndSection\n\n\n";

    #- Write monitor section.     
    $O = $o->{monitor};
    print F $monitorsection_text1;
    print F qq(    Identifier "$O->{type}"\n);
    print F qq(    VendorName "$O->{vendor}"\n);
    print F qq(    ModelName  "$O->{model}"\n);
    print F "\n";
    print F $monitorsection_text2;
    print F qq(    HorizSync  $O->{hsyncrange}\n);
    print F "\n";
    print F $monitorsection_text3;
    print F qq(    VertRefresh $O->{vsyncrange}\n);
    print F "\n";
    print F $monitorsection_text4;
    print F ($o->{card}{type} eq "TG 96" ? 
	     $modelines_text_Trident_TG_96xx :
	     $modelines_text);
    print F "EndSection\n\n\n";

    #- Write Device section.     
    $O = $o->{card};
    print F $devicesection_text;
    print F qq(Section "Device"\n);
    print F qq(    Identifier  "$O->{type}"\n);
    print F qq(    VendorName  "$O->{vendor}"\n);
    print F qq(    BoardName   "$O->{board}"\n);

    print F "#" if $O->{memory} && !$O->{flags}{needVideoRam};
    print F "    VideoRam    $O->{memory}\n" if $O->{memory};

    print F map { "    $_\n" } @{$O->{lines} || []};

    print F qq(    Ramdac      "$O->{ramdac}"\n) if $O->{ramdac};
    print F qq(    Dacspeed    "$O->{dacspeed}"\n) if $O->{dacspeed};

    if ($O->{clockchip}) {
	print F qq(    Clockchip   "$O->{clockchip}"\n);
    } else {
	print F "    # Clock lines\n";
	print F "    Clocks $_\n" foreach (@{$O->{clocklines}});
    }
    print F "EndSection\n\n\n";

    #- Write Screen sections.     
    print F $screensection_text1;

    my $screen = sub {
	my ($server, $defdepth, $device, $depths) = @_;
	print F qq(

Section "Screen"
    Driver "$server"
    Device      "$device"
    Monitor     "$o->{monitor}{type}"
);
	print F "    DefaultColorDepth $defdepth\n" if $defdepth;

        foreach (ikeys(%$depths)) {
	    my $m = join(" ", map { qq("$_->[0]x$_->[1]") } @{$depths->{$_}});
	    print F qq(    Subsection "Display"\n);
	    print F qq(        Depth       $_\n) if $_;
	    print F qq(        Modes       $m\n);
	    print F qq(        ViewPort    0 0\n);
	    print F qq(    EndSubsection\n);
	}
	print F "EndSection\n";
    };
    
    #- SVGA screen section.
    print F qq(
# The Colour SVGA server
);

    if ($O->{server} eq 'SVGA') {
	&$screen("svga", $O->{default_depth}, $O->{type}, $O->{depth});
    } else {
	&$screen("svga", '', "Generic VGA", { 8 => [[ 320, 200 ]] });
    }

    &$screen("vga16", '',
	     (member($O->{server}, "Mono", "VGA16") ? $O->{type} : "Generic VGA"), 
	     { '' => [[ 640, 480 ], [ 800, 600 ]]});

    &$screen("vga2", '',
	     (member($O->{server}, "Mono", "VGA16") ? $O->{type} : "Generic VGA"), 
	     { '' => [[ 640, 480 ], [ 800, 600 ]]});

    &$screen("accel", $O->{default_depth}, $O->{type}, $O->{depth});
}

sub XF86check_link {
    my ($void) = @_;

    my $f = "$prefix/etc/X11/XF86Config";
    touch($f);

    my $l = "$prefix/usr/X11R6/lib/X11/XF86Config";

    if (-e $l && (stat($f))[1] != (stat($l))[1]) { #- compare the inode, must be the sames
	-e $l and unlink($l) || die "can't remove bad $l";
	symlink "../../../../etc/X11/XF86Config", $l;
    }
}

sub show_info {
    my ($o) = @_;
    my $info;

    $info .= _("Keyboard layout: %s\n", $o->{keyboard}{xkb_keymap});
    $info .= _("Mouse type: %s\n", $o->{mouse}{XMOUSETYPE});
    $info .= _("Mouse device: %s\n", $o->{mouse}{device}) if $::expert;
    $info .= _("Monitor: %s\n", $o->{monitor}{type});
    $info .= _("Monitor HorizSync: %s\n", $o->{monitor}{hsyncrange}) if $::expert;
    $info .= _("Monitor VertRefresh: %s\n", $o->{monitor}{vsyncrange}) if $::expert;
    $info .= _("Graphic card: %s\n", $o->{card}{type});
    $info .= _("Graphic memory: %s KB\n", $o->{card}{memory}) if $o->{card}{memory};
    $info .= _("XFree86 server: %s\n", $o->{card}{server});

    $in->ask_warn('', $info);
}

#- Program entry point. 
sub main {
    my $o;
    ($prefix, $o, $in, $install) = @_;
    $o ||= {};

    XF86check_link();

    $o->{card} = cardConfiguration($o->{card}, $::noauto);

    $o->{monitor} = monitorConfiguration($o->{monitor});

    my $ok = resolutionsConfiguration($o, $::auto && 'auto' || $::noauto && 'noauto' || '');
    
    $ok &&= testFinalConfig($o, $::auto);

    my $quit;
    until ($ok || $quit) {

	my %c = my @c = (
	   __("Change Monitor") => sub { $o->{monitor} = monitorConfiguration() },
           __("Change Graphic card") => sub { $o->{card} = cardConfiguration('', 'noauto') },
	   __("Change Resolution") => sub { resolutionsConfiguration($o, 'noauto') },
	   __("Automatical resolutions search") => sub { 
	       delete $o->{card}{depth};
	       resolutionsConfiguration($o, 'nowarning');
	   },
	   __("Show information") => sub { show_info($o) },
	   __("Test again") => sub { $ok = testFinalConfig($o, 1) },
	   __("Quit") => sub { $quit = 1 },
        );
	&{$c{$in->ask_from_list_('', 
				 _("What do you want to do?"),
				 [ grep { !ref } @c ])}};
    }

    if ($ok) {
	my $run = $o->{xdm} || $::auto || $in->ask_yesorno(_("X at startup"), 
_("I can set up your computer to automatically start X upon booting.
Would you like X to start when you reboot?"));

	rewriteInittab($run ? 5 : 3) unless $::testing;

	$in->ask_warn(_("X successfully configured"),
_("Configuration file has been written. Take a look at it before running 'startx'. 
Within the server press ctrl, alt and '+' simultaneously to cycle video resolutions. 
Pressing ctrl, alt and backspace simultaneously immediately exits the server 
For further configuration, refer to /usr/X11R6/lib/X11/doc/README.Config.")) unless $::auto;
    }
}