package Xconfigurator; # $Id: Xconfigurator.pm,v 1.210 2000/10/06 15:59:51 fpons Exp $





use common qw(:common :file :functional :system);
use log;
use detect_devices;
use run_program;
use Xconfigurator_consts;
use any;
use modules;
use my_gtk qw(:wrappers);

my $tmpconfig = "/tmp/Xconfig";

my ($prefix, %monitors);

1;

sub getVGAMode($) { $_[0]->{card}{vga_mode} || $vgamodes{"640x480x16"}; }

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

    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}{needChipset} = 1 if $val eq 'GeForce DDR';
	    $card->{flags}{needVideoRam} = 1 if member($val, qw(mgag10 mgag200 RIVA128 SiS6326));
	},
	SERVER => sub { $card->{server} = $val; },
	DRIVER => sub { $card->{driver} = $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 next; #log::l("bad line $lineno ($_)"), next;

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

	$f ? &$f() : log::l("unknown line $lineno ($_)");
    }
    \%cards;
}
sub readCardsNames {
    my $file = "/usr/X11R6/lib/X11/CardsNames";
    local *F; open F, $file or die "can't find $file\n";
    map { (split '=>')[0] } <F>;
}
sub cardName2RealName {
    my $file = "/usr/X11R6/lib/X11/CardsNames";
    my ($name) = @_;
    local *F; open F, $file or die "can't find $file\n";
    foreach (<F>) { chop;
	my ($name_, $real) = split '=>';
	return $real if $name eq $name_;
    }
    $name;
}
sub updateCardAccordingName {
    my ($card, $name) = @_;
    my $cards = readCardsDB("/usr/X11R6/lib/X11/Cards+");

    add2hash($card->{flags}, $cards->{$name}{flags});
    add2hash($card, $cards->{$name});
    $card;
}

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(vendor type eisa hsyncrange vsyncrange);
	my @l = split /\s*;\s*/;
	@l == @fields or log::l("bad line $lineno ($_)"), next;

	my %l; @l{@fields} = @l;
	if ($monitors{$l{type}}) {
	    my $i; for ($i = 0; $monitors{"$l{type} ($i)"}; $i++) {}
	    $l{type} = "$l{type} ($i)";
	}
	$monitors{"$l{vendor}|$l{type}"} = \%l;
    }
    while (my ($k, $v) = each %standard_monitors) {
	$monitors{'Generic|' . translate($k)} =
	    { hsyncrange => $v->[1], vsyncrange => $v->[2] };
    }
}

sub rewriteInittab {
    my ($runlevel) = @_;
    my $f = "$prefix/etc/inittab";
    -r $f or log::l("missing inittab!!!"), return;
    substInFile { s/^(id:)[35](:initdefault:)\s*$/$1$runlevel$2\n/ } $f;
}

sub keepOnlyLegalModes {
    my ($card, $monitor) = @_;
    my $mem = 1024 * ($card->{memory} || ($card->{server} eq 'FBDev' ? 2048 : 99999));
    my $hsync = max(split(/[,-]/, $monitor->{hsyncrange}));

    while (my ($depth, $res) = each %{$card->{depth}}) {
	@$res = grep {
	    $mem >= product(@$_, $depth / 8) &&
	    $hsync >= ($min_hsync4wres{$_->[0]} || 0) &&
	    ($card->{server} ne 'FBDev' || $vgamodes{"$_->[0]x$_->[1]x$depth"})
	} @$res;
	delete $card->{depth}{$depth} if @$res == 0;
    }
}

sub cardConfigurationAuto() {
    my $card;
    if (my ($c) = grep { $_->{driver} =~ /(Card|Server):/ } detect_devices::probeall()) {
	local $_ = $c->{driver};
	$card->{type} = $1 if /Card:(.*)/;
	$card->{server} = $1 if /Server:(.*)/;
	$card->{flags}{needVideoRam} &&= /86c368/;
	$card->{identifier} = $c->{description};
	push @{$card->{lines}}, @{$lines{$card->{identifier}} || []};
    }
    
    if (arch() =~ /^sparc/ && !$card->{server} && !$card->{type}) {
        log::l("Using probe with /proc/fb as nothing has been found!");
	local $_ = cat_("/proc/fb");
	if (/Mach64/) { $card->{server} = "Mach64" }
	elsif (/Permedia2/) { $card->{server} = "3DLabs" }
	else { $card->{server} = "Sun24" }
    }
    $card;
}

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

    updateCardAccordingName($card, $card->{type}) if $card->{type}; 
    undef $card->{type} unless $card->{server}; 
    add2hash($card, cardConfigurationAuto()) unless $card->{server} || $noauto;
    $card->{server} = 'FBDev' unless !$allowFB || $card->{server} || $card->{type} || $noauto;
    $card->{type} = cardName2RealName($in->ask_from_treelist(_("Graphic card"), _("Select a graphic card"), '|', ['Other|Unlisted', readCardsNames()])) unless $card->{type} || $card->{server};
    undef $card->{type}, $card->{server} = $in->ask_from_list(_("X server"), _("Choose a X server"), $allowFB ? \@allservers : \@allbutfbservers ) if $card->{type} eq 'Other|Unlisted';

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

    $card->{memory} = 4096,  delete $card->{depth} if $card->{driver} eq 'i810';
    $card->{memory} = 16384, delete $card->{depth} if $card->{chipset} =~ /PERMEDIA/ && $card->{memory} <= 1024;

    
    $card->{Utah_glx} = ($card->{identifier} =~ /Matrox.* G[24]00/ || 
			 $card->{identifier} =~ /Riva.*128/ ||
			 $card->{identifier} =~ /Rage X[CL]/ ||
			 $card->{identifier} =~ /Rage Mobility (?:P\/M|L) / ||
			 $card->{identifier} =~ /3D Rage (?:LT|Pro)/);
                         
    
    $card->{Utah_glx_EXPERIMENTAL} = ($card->{type} =~ /RIVA TNT/ || 
				      #$card->{type} =~ /RIVA128/ ||
				      $card->{type} =~ /GeForce 256/ ||
				      $card->{type} =~ /S3 Savage3D/ || 
				      
				      $card->{type} =~ /SiS /);
    
    $card->{DRI_glx} = ($card->{identifier} =~ /Voodoo [35]/ || $card->{identifier} =~ /Voodoo Banshee/ || 
			#$card->{identifier} =~ /Matrox.* G[24]00/ || 
			$card->{identifier} =~ /8281[05].* CGC/ || 
			$card->{identifier} =~ /Rage 128/); 

    
    $card->{use_xf4} = $card->{driver} && !$card->{flags}{unsupported};
    $card->{prefer_xf3} = ($card->{type} =~ /RIVA TNT/ ||
			   $card->{type} =~ /RIVA128/ ||
			   $card->{type} =~ /GeForce/ ||
			   $card->{type} =~ /NeoMagic /);

    
    my ($xf4_ver, $xf3_ver) = ("4.0.1", "3.3.6");
    my $xf3_tc = { text => _("XFree %s", $xf3_ver),
		   code => sub { $card->{Utah_glx} = $card->{DRI_glx} = ''; $card->{use_xf4} = '';
				 log::l("Using XFree $xf3_ver") } };
    my $msg = _("Which configuration of XFree do you want to have?");
    my @choices = $card->{use_xf4} ? (($card->{prefer_xf3} ? ($xf3_tc) : ()),
				      (!$card->{prefer_xf3} || $::expert ?
				       ({ text => _("XFree %s", $xf4_ver),
					  code => sub { $card->{Utah_glx} = $card->{DRI_glx} = '';
							log::l("Using XFree $xf4_ver") } }) : (),),
				      ($::expert && !$card->{prefer_xf3} ? ($xf3_tc) : ())) : ($xf3_tc);

    
    
    if ($card->{Utah_glx}) {
	$msg = ($card->{use_xf4} && !$card->{DRI_glx} ?
_("Your card can have 3D hardware acceleration support but only with XFree %s.
Your card is supported by XFree %s which may have a better support in 2D.", $xf3_ver, $xf4_ver) :
_("Your card can have 3D hardware acceleration support with XFree %s.", $xf3_ver)) . "\n\n\n" . $msg;
	$::beginner and @choices = (); 
	unshift @choices, { text => _("XFree %s with 3D hardware acceleration", $xf3_ver),
			    code => sub { $card->{use_xf4} = '';
					  log::l("Using XFree $xf3_ver with 3D hardware acceleration") } };
    }

    
    
    if ($::expert && $card->{Utah_glx_EXPERIMENTAL}) {
	$msg = ($card->{use_xf4} && !$card->{DRI_glx} ?
_("Your card can have 3D hardware acceleration support but only with XFree %s,
NOTE THIS IS EXPERIMENTAL SUPPORT AND MAY FREEZE YOUR COMPUTER.
Your card is supported by XFree %s which may have a better support in 2D.", $xf3_ver, $xf4_ver) :
_("Your card can have 3D hardware acceleration support with XFree %s,
NOTE THIS IS EXPERIMENTAL SUPPORT AND MAY FREEZE YOUR COMPUTER.", $xf3_ver)) . "\n\n\n" . $msg;
	push @choices, { text => _("XFree %s with EXPERIMENTAL 3D hardware acceleration", $xf3_ver),
			 code => sub { $card->{use_xf4} = ''; $card->{Utah_glx} = 'EXPERIMENTAL';
				       log::l("Using XFree $xf3_ver with EXPERIMENTAL 3D hardware acceleration") } };
    }

    
    if ($card->{use_xf4} && $card->{DRI_glx}) {
	$msg = _("Your card can have 3D hardware acceleration support with XFree %s.", $xf4_ver) . "\n\n\n" . $msg;
	$::expert or @choices = (); 
	unshift @choices, { text => _("XFree %s with 3D hardware acceleration", $xf4_ver),
			    code => sub { log::l("Using XFree $xf4_ver with 3D hardware acceleration") } };
    }

    
    
    my $tc = $in->ask_from_listf(_("XFree configuration"), formatAlaTeX($msg), sub { translate($_[0]{text}) }, \@choices);
    $tc->{code} and $tc->{code}();

    $card->{prog} = "/usr/X11R6/bin/" . ($card->{use_xf4} ? 'XFree86' : $card->{server} =~ /Sun (.*)/x ?
					 "Xsun$1" : "XF86_$card->{server}");

    
    
    my @l = ();
    if ($card->{DRI_glx}) {
	push @l, 'Glide_V5' if $card->{identifier} =~ /Voodoo 5/;
	push @l, 'Glide_V3-DRI' if $card->{identifier} =~ /Voodoo (3|Banshee)/;
	push @l, 'Device3Dfx', 'XFree86-glide-module' if $card->{identifier} =~ /Voodoo/;
    }
    if ($card->{Utah_glx}) {
	push @l, 'Mesa' if !$card->{use_xf4};
    }

    -x "$prefix$card->{prog}" or $install && do {
	$in->suspend;
	&$install('server', @l) if $card->{use_xf4};
	&$install($card->{server}, @l) if !$card->{use_xf4};
	$in->resume;
    };
    -x "$prefix$card->{prog}" or die "server $card->{server} is not available (should be in $prefix$card->{prog})";

    $card->{options}{sw_cursor} = 1 if $card->{type} =~ /S3 Trio3D/;
    unless ($card->{type}) {
	$card->{flags}{noclockprobe} = member($card->{server}, qw(I128 S3 S3V Mach64));
    }
    $card->{options_xf3}{power_saver} = 1;
    $card->{options_xf4}{DPMS} = 1;

    $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])};


    
    if ($card->{identifier} =~ /Rage X[CL]/ ||
	$card->{identifier} =~ /Rage Mobility (?:P\/M|L) / ||
	$card->{identifier} =~ /3D Rage (?:LT|Pro)/) {
	$card->{options_xf3}{no_font_cache} = $card->{Utah_glx};
	$card->{options_xf3}{no_pixmap_cache} = $card->{Utah_glx};
    }

    
    
    if ($card->{DRI_glx}) {
	$card->{identifier} =~ /Matrox.* G[24]00/ and $card->{flags}{needVideoRam} = 'fakeVideoRam';
	$card->{identifier} =~ /8281[05].* CGC/ and ($card->{flags}{needVideoRam}, $card->{memory}) = ('fakeVideoRam', 16384);
    }

    if (!$::isStandalone && $card->{driver} eq 'i810') {
	require modules;
	eval { modules::load("agpgart"); };
    }
    $card;
}

sub optionsConfiguration($) {
    my ($o) = @_;
    my @l;
    my %l;

    foreach (@options) {
	if ($o->{card}{server} eq $_->[1] && $o->{card}{identifier} =~ /$_->[2]/) {
	    my $options = 'options_' . ($o->{card}{server} eq 'XFree86' ? 'xf4' : 'xf3');
	    $o->{card}{$options}{$_->[0]} ||= 0;
	    unless ($l{$_->[0]}) {
		push @l, $_->[0], { val => \$o->{card}{$options}{$_->[0]}, type => 'bool' };
		$l{$_->[0]} = 1;
	    }
	}
    }
    @l = @l[0..19] if @l > 19; 

    $in->ask_from_entries_refH('', _("Choose options for server"), \@l);
}

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

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

    readMonitorsDB("/usr/X11R6/lib/X11/MonitorsDB");

    add2hash($monitor, { type => $in->ask_from_treelist(_("Monitor"), _("Choose a monitor"), '|', ['Custom', keys %monitors], 'Generic|' . translate($default_monitor)) }) unless $monitor->{type};
    if ($monitor->{type} eq 'Custom') {
	$in->ask_from_entries_ref('',
_("The two critical parameters are the vertical refresh rate, which is the rate
at which the whole screen is refreshed, and most importantly the horizontal
sync rate, which is the rate at which scanlines are displayed.

It is VERY IMPORTANT that you do not specify a monitor type with a sync range
that is beyond the capabilities of your monitor: you may damage your monitor.
 If in doubt, choose a conservative setting."),
				  [ _("Horizontal refresh rate"), _("Vertical refresh rate") ],
				  [ { val => \$monitor->{hsyncrange}, list => \@hsyncranges },
				    { val => \$monitor->{vsyncrange}, list => \@vsyncranges }, ]);
    } else {
	add2hash($monitor, $monitors{$monitor->{type}});
    }
    add2hash($monitor, { type => "Unknown", vendor => "Unknown", model => "Unknown", manual => 1 });
}

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

    write_XF86Config($o, $tmpconfig);

    unlink "/tmp/.X9-lock";
    

    my $f = $tmpconfig . ($o->{card}{use_xf4} && "-4");
    local *F;
    open F, "$prefix$o->{card}{prog} :9 -probeonly -pn -xf86config $f 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, $skiptest, $skip_badcard) = @_;

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

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

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

    my $f = "/etc/X11/XF86Config.test";
    write_XF86Config($o, $::testing ? $tmpconfig : "$prefix/$f");

    $skiptest || $o->{card}{server} =~ 'FBDev|Sun' and return 1; 

    
    my $bad_card = $o->{card}{identifier} =~ /i740|ViRGE/;
    $bad_card ||= $o->{card}{identifier} =~ /Rage Mobility (?:P\/M|L) / ||  $o->{card}{identifier} =~ /3D Rage LT/;
    $bad_card ||= $o->{card}{use_xf4}; 
    log::l("the graphic card does not like X in framebuffer") if $bad_card;

    my $verybad_card = $o->{card}{driver} eq 'i810';
    $verybad_card and return 1;

    my $mesg = _("Do you want to test the configuration?");
    my $def = 1;
    if ($bad_card && !$::isStandalone) {
	$skip_badcard and return 1;
	$mesg = $mesg . "\n" . _("Warning: testing this graphic card may freeze your computer");
	$def = 0;
    }
    $auto && $def or $in->ask_yesorno(_("Test of the configuration"), $mesg, $def) or return 1;

    unlink "$prefix/tmp/.X9-lock";

    
    
    
    if (-d "/tmp/.X11-unix") {
	symlinkf "$prefix/tmp/.X11-unix/X9", "/tmp/.X11-unix/X9" if $prefix;
    } else {
	symlinkf "$prefix/tmp/.X11-unix", "/tmp/.X11-unix" if $prefix;
    }
    

    my $f_err = "$prefix/tmp/Xoutput";
    my $pid;
    unless ($pid = fork) {
	open STDERR, ">$f_err";
	chroot $prefix if $prefix;
	exec $o->{card}{prog}, 
	  ($o->{card}{prog} !~ /Xsun/ ? ("-xf86config", ($::testing ? $tmpconfig : $f) . ($o->{card}{use_xf4} && "-4")) : ()),
	  ":9" or c::_exit(0);
    }

    do { sleep 1 } until c::Xtest(":9") || waitpid($pid, c::WNOHANG());

    my $b = before_leaving { unlink $f_err };

    unless (c::Xtest(":9")) {
	local $_;
	local *F; open F, $f_err;
      i: while (<F>) {
	    if (/\b(error|not supported)\b/i) {
		my @msg = !/error/ && $_ ;
		while (<F>) {
		    /not fatal/ and last i;
		    /^$/ and last;
		    push @msg, $_;
		}
		$in->ask_warn('', [ _("An error has occurred:"), " ", @msg, _("\ntry to change some parameters") ]);
		return 0;
	    }
	}
    }

    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_background(200 * 257, 210 * 257, 210 * 257);
        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;
            1;
	});

        my $background = "/usr/share/pixmaps/backgrounds/linux-mandrake/XFdrake-image-test.jpg";
        my $qiv = "/usr/bin/qiv";
        -r "} . $prefix . q{/$background" && -x "} . $prefix . q{/$qiv" and
            system(($::testing ? "} . $prefix . q{" : "chroot } . $prefix . q{/ ") . "$qiv -y $background");

	exit (interactive_gtk->new->ask_yesorno('', [ _("Is this the correct setting?"), $text ], 0) ? 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 has occurred, try to change some parameters"));
    $rc;
}

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

    return 24 if $card->{identifier} =~ /SiS/; 
    return 16 if $card->{Utah_glx} || $card->{DRI_glx}; 
    
    for ($card->{server}) {
	/FBDev/   and return 16; 
	/Sun24/   and return 24;
	/SunMono/ and return 2;
	/Sun/     and return 8;
    }

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

	
	$best = max($best || 0, $d) if $r->[0][0] >= $wres_wanted;
    }
    $best || $depth or die "no valid modes";
}

sub autoDefaultResolution {
    return "1024x768" if $isLaptop;

    my ($size) = @_;
    $monitorSize2resolution[round($size || 14)] || 
      $monitorSize2resolution[-1]; 
}

sub chooseResolutionsGtk($$;$) {
    my ($card, $chosen_depth, $chosen_w) = @_;
    my $W = my_gtk->new(_("Resolution"));
    my %txt2depth = reverse %depths;
    my ($r, $depth_combo, %w2depth, %w2h, %w2widget);

    my $best_w;
    while (my ($depth, $res) = each %{$card->{depth}}) {
	foreach (@$res) {
	    $w2h{$_->[0]} = $_->[1];
	    push @{$w2depth{$_->[0]}}, $depth;

	    $best_w = max($_->[0], $best_w) if $_->[0] <= $chosen_w;
	}
    }
    $chosen_w = $best_w;

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

    
    my $ignore;
    my $set = sub { $ignore = 1; $_[0]->set_active(1); $ignore = 0; };

    while (my ($w, $h) = each %w2h) {
	my $V = $w . "x" . $h;
	$w2widget{$w} = $r = new Gtk::RadioButton($r ? ($V, $r) : $V);
	&$set($r) if $chosen_w == $w;
	$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 the resolution and the color depth"),
					      "(" . ($card->{type} ? 
						     _("Graphic card: %s", $card->{type}) :
						     _("XFree86 server: %s", $card->{server})) . ")"
					     ),
		    1, gtkpack(new Gtk::HBox(0,20),
			       $depth_combo = new Gtk::Combo,
			       gtkpack_(new Gtk::VBox(0,0),
					map {; 0, $w2widget{$_} } ikeys(%w2widget),
					),
			       ),
		    0, gtkadd($W->create_okcancel,
			      gtksignal_connect(new Gtk::Button(_("Show all")), clicked => sub { $W->{retval} = 1; $chosen_w = 0; Gtk->main_quit })),
		    ));
    $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->{ok}->grab_focus;

    $W->main or return;
    ($chosen_depth, $chosen_w);
}

sub chooseResolutions($$;$) {
    goto &chooseResolutionsGtk if ref($in) =~ /gtk/;

    my ($card, $chosen_depth, $chosen_w) = @_;

    my $best_w;
    local $_ = $in->ask_from_list(_("Resolutions"), "", 
				  [ map_each { map { "$_->[0]x$_->[1] ${main::a}bpp" } @$::b } %{$card->{depth}} ]) or return;
    reverse /(\d+)x\S+ (\d+)/;
}


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

    
    if (member($card->{server}, "Mono", "VGA16")) {
	$card->{depth}{8} = [[ 640, 480 ]];
	return;
    } elsif ($card->{server} =~ /Sun/) {
	$card->{depth}{2} = [[ 1152, 864 ]] if $card->{server} =~ /^(SunMono)$/;
	$card->{depth}{8} = [[ 1152, 864 ]] if $card->{server} =~ /^(SunMono|Sun)$/;
	$card->{depth}{24} = [[ 1152, 864 ]] if $card->{server} =~ /^(SunMono|Sun|Sun24)$/;
	$card->{default_wres} = 1152;
	$o->{default_depth} = max(keys %{$card->{depth}});
	return 1; 
    }
    if (is_empty_hash_ref($card->{depth})) {
	$card->{depth}{$_} = [ map { [ split "x" ] } @resolutions ]
	  foreach @depths;
    }
    
    foreach (values %{$card->{depth}}) {
	my $i = 0;
	@$_ = grep { first($i != $_->[0], $i = $_->[0]) }
	  sort { $b->[0] <=> $a->[0] } @$_;
    }

    
    keepOnlyLegalModes($card, $o->{monitor});

    my $res = $o->{resolution_wanted} || autoDefaultResolution($o->{monitor}{size});
    my $wres = first(split 'x', $res);

    
    $wres = max map { first(grep { $_->[0] <= $wres } @$_)->[0] } values %{$card->{depth}};
    my $depth = eval { $o->{default_depth} || autoDefaultDepth($card, $wres) };

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

    unless ($wres) {
	delete $card->{depth};
	return resolutionsConfiguration($o);
    }

    
    $card->{depth}{$depth} or die "you selected an unusable depth";

    
    
    $_ = [ grep { $_->[0] <= $wres } @$_ ] foreach values %{$card->{depth}};
    $card->{default_wres} = $wres;
    $card->{vga_mode} = $vgamodes{"${wres}xx$depth"} || $vgamodes{"${res}x$depth"}; 
    $o->{default_depth} = $depth;
    1;
}



sub write_XF86Config {
    my ($o, $file) = @_;
    my $O;

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

    print F $XF86firstchunk_text, $XF86firstchunk_text2;
    print G $XF86firstchunk_text;
    print G qq(    Option "Pixmap"  "24"\n) if $o->{card}{type} eq "SiS 6326";
    print G $XF86firstchunk_text2;

    
    $O = $o->{keyboard};
    print F $keyboardsection_start;
    print G $keyboardsection_start_v4;
    print F qq(    XkbDisable\n) unless $O->{xkb_keymap};
    print G qq(    Option "XkbDisable"\n) unless $O->{xkb_keymap};
    print F $keyboardsection_part3;
    print G $keyboardsection_part3_v4;
    print F qq(    XkbLayout       "$O->{xkb_keymap}"\n);
    print G qq(    Option "XkbLayout" "$O->{xkb_keymap}"\n);
    print F join '', map { "    $_\n" } @{$xkb_options{$O->{xkb_keymap}} || []};
    print G join '', map { /(\S+)(.*)/; qq(    Option "$1" $2\n) } @{$xkb_options{$O->{xkb_keymap}} || []};
    print F $keyboardsection_end;
    print G $keyboardsection_end;

    
    $O = $o->{mouse};
    print F $pointersection_text;
    print G $pointersection_text_v4;
    print F qq(    Protocol    "$O->{XMOUSETYPE}"\n);
    print G qq(    Option "Protocol"    "$O->{XMOUSETYPE}"\n);
    print F qq(    Device      "/dev/$O->{device}"\n);
    print G qq(    Option "Device"      "/dev/$O->{device}"\n);
    
    print F "    ZAxisMapping 4 5\n" if $O->{nbuttons} > 3;
    print F "    ZAxisMapping 6 7\n" if $O->{nbuttons} > 5;
    print G qq(    Option "ZAxisMapping" "4 5"\n) if $O->{nbuttons} > 3;
    print G qq(    Option "ZAxisMapping" "6 7"\n) if $O->{nbuttons} > 5;

    print F "#" unless $O->{nbuttons} < 3;
    print G "#" unless $O->{nbuttons} < 3;
    print F qq(    Emulate3Buttons\n);
    print G qq(    Option "Emulate3Buttons"\n);
    print F "#" unless $O->{nbuttons} < 3;
    print G "#" unless $O->{nbuttons} < 3;
    print F qq(    Emulate3Timeout    50\n\n);
    print G qq(    Option "Emulate3Timeout"    "50"\n\n);
    print F "# ChordMiddle is an option for some 3-button Logitech mice\n\n";
    print G "# ChordMiddle is an option for some 3-button Logitech mice\n\n";
    print F "#" unless $O->{chordmiddle};
    print G "#" unless $O->{chordmiddle};
    print F qq(    ChordMiddle\n\n);
    print G qq(    Option "ChordMiddle"\n\n);
    print F "    ClearDTR\n" if $O->{cleardtrrts};
    print F "    ClearRTS\n\n"  if $O->{cleardtrrts};
    print F "EndSection\n\n\n";
    print G "EndSection\n\n\n";

    
    if ($o->{wacom} || $o->{card}{Utah_glx}) {
	print F qq(Section "Module"
);
	print F qq(    Load "xf86Wacom.so"\n) if $o->{wacom};
	print F qq(    Load "glx-3.so"\n) if $o->{card}{Utah_glx}; 
	print F qq(EndSection

);
    }

    
    print F qq(
Section "XInput"
    SubSection "WacomStylus"
        Port "/dev/$o->{wacom}"
        AlwaysCore
        Mode Absolute
    EndSubSection
    SubSection "WacomCursor"
        Port "/dev/$o->{wacom}"
        AlwaysCore
        Mode Relative
    EndSubSection
    SubSection "WacomEraser"
        Port "/dev/$o->{wacom}"
        AlwaysCore
        Mode Absolute
    EndSubSection
EndSection

) if $o->{wacom};

    print G qq(
Section "InputDevice"
    Identifier	"stylus"
    Driver	"wacom"
    Option	"Type" "stylus"
    Option	"Device" "/dev/$o->{wacom}"
    Option	"Mode" "Absolute"
EndSection
Section "InputDevice"
    Identifier	"eraser"
    Driver	"wacom"
    Option	"Type" "eraser"
    Option	"Device" "/dev/$o->{wacom}"
    Option	"Mode" "Absolute"
EndSection
Section "InputDevice"
    Identifier	"cursor"
    Driver	"wacom"
    Option	"Type" "cursor"
    Option	"Device" "/dev/$o->{wacom}"
    Option	"Mode" "Relative"
EndSection
) if $o->{wacom};

    
    print G qq(
Section "Module"

# This loads the DBE extension module.

    Load	"dbe"
);
    print G qq(
    Load	"glx"
    Load	"dri"
) if $o->{card}{DRI_glx};
    print G qq(

# This loads the miscellaneous extensions module, and disables
# initialisation of the XFree86-DGA extension within that module.

    SubSection	"extmod"
	#Option	"omit xfree86-dga"
    EndSubSection

# This loads the Type1 and FreeType font modules

    Load	"type1"
    Load	"freetype"
EndSection
);
    print G qq(

Section "DRI"
    Mode	0666
EndSection
) if $o->{card}{DRI_glx};

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

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

    print F "#" if $O->{chipset} && !$O->{flags}{needChipset};
    print F qq(    Chipset     "$O->{chipset}"\n) if $O->{chipset};
    print G qq(    Driver      "$O->{driver}"\n);

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

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

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

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

    # Uncomment following option if you see a big white block        
    # instead of the cursor!                                          
    #    Option      "sw_cursor"

);
    my $p = sub {
	my $l = $O->{$_[0]};
	map { (!$l->{$_} && '#') . qq(    Option      "$_"\n) } keys %{$l || {}};
    };
    print F $p->('options');
    print F $p->('options_xf3');
    print G $p->('options');
    print G $p->('options_xf4');
    print F "EndSection\n\n\n";
    print G "EndSection\n\n\n";

    
    print F $screensection_text1, "\n";
    print G $screensection_text1, "\n";

    my $subscreen = sub {
	my ($f, $server, $defdepth, $depths) = @_;
	print $f "    DefaultColorDepth $defdepth\n" if $defdepth;

        foreach (ikeys(%$depths)) {
	    my $m = $server ne "fbdev" ? join(" ", map { qq("$_->[0]x$_->[1]") } @{$depths->{$_}}) : qq("default"); 
	    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";
    };

    my $screen = sub {
	my ($server, $defdepth, $device, $depths) = @_;
	print F qq(
Section "Screen"
    Driver "$server"
    Device      "$device"
    Monitor     "$o->{monitor}{type}"
); 
	$subscreen->(*F, $server, $defdepth, $depths);
    };

    
    print F qq(
# The Colour SVGA server
);

    if (member($O->{server}, @svgaservers)) {
	&$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});

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


    print G qq(
Section "Screen"
    Identifier "screen1"
    Device      "$O->{type}"
    Monitor     "$o->{monitor}{type}"
);
    
    $subscreen->(*G, "svga", min($o->{default_depth}, 24), $O->{depth});

    print G '

Section "ServerLayout"
    Identifier "layout1"
    Screen     "screen1"
    InputDevice "Mouse1" "CorePointer"
';
    print G '
    InputDevice "stylus" "AlwaysCore"
    InputDevice "eraser" "AlwaysCore"
    InputDevice "cursor" "AlwaysCore"
' if $o->{wacom};
    print G '
    InputDevice "Keyboard1" "CoreKeyboard"
EndSection
'; 

    close F;
    close G;
}

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

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

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

    if (-e $l && (stat($f))[1] != (stat($l))[1]) { 
	-e $l and unlink($l) || die "can't remove bad $l";
	symlinkf "../../../../etc/X11/XF86Config$ext", $l;
    }
}

sub 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};
    if ($o->{default_depth} and my $depth = $o->{card}{depth}{$o->{default_depth}}) {
    $info .= _("Color depth: %s\n", translate($depths{$o->{default_depth}}));
    $info .= _("Resolution: %s\n", join "x", @{$depth->[0]}) if $depth && !is_empty_array_ref($depth->[0]);
    }
    $info .= _("XFree86 server: %s\n", $o->{card}{server}) if $o->{card}{server};
    $info .= _("XFree86 driver: %s\n", $o->{card}{driver}) if $o->{card}{driver};
    $info;
}

sub show_info {
    my ($o) = @_;
    $in->ask_warn('', info($o));
}


sub main {
    my ($o, $allowFB);
    ($prefix, $o, $in, $allowFB, $isLaptop, $install) = @_;
    $o ||= {};

    XF86check_link('');
    XF86check_link('-4');

    {
	my $w = $in->wait_message('', _("Preparing X-Window configuration"), 1);

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

	$o->{monitor} = monitorConfiguration($o->{monitor}, $o->{card}{server} eq 'FBDev');
    }
    my $ok = resolutionsConfiguration($o, $::auto);

    $ok &&= testFinalConfig($o, $::auto, $o->{skiptest}, $::auto);

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

	my %c = my @c = (
	   __("Change Monitor") => sub { $o->{monitor} = monitorConfiguration() },
           __("Change Graphic card") => sub { $o->{card} = cardConfiguration('', 'noauto', $allowFB) },
           ($::expert ? (__("Change Server options") => sub { optionsConfiguration($o) }) : ()),
	   __("Change Resolution") => sub { resolutionsConfiguration($o) },
	   __("Show information") => sub { show_info($o) },
	   __("Test again") => sub { $ok = testFinalConfig($o, 1) },
	   __("Quit") => sub { $quit = 1 },
        );
	$in->set_help('configureXmain') unless $::isStandalone;
	my $f = $in->ask_from_list_(['XFdrake'],
				 _("What do you want to do?"),
				 [ grep { !ref } @c ]);
	eval { &{$c{$f}} };
	!$@ || $@ =~ /ask_from_list cancel/ or die;
	$in->kill;
    }
    if (!$ok) {
	$ok = $in->ask_yesorno('', _("Keep the changes?
Current configuration is:

%s", info($o)));
    }
    if ($ok) {
	unless ($::testing) {
	    my $f = "$prefix/etc/X11/XF86Config";
	    if (-e "$f.test") {
		rename $f, "$f.old" or die "unable to make a backup of XF86Config";
		rename "$f-4", "$f-4.old";
		rename "$f.test", $f;
		rename "$f.test-4", "$f-4";
		symlinkf "../..$o->{card}{prog}", "$prefix/etc/X11/X";
	    }
	}

	if ($::isStandalone && $0 =~ /Xdrakres/) {
	    my $found;
	    foreach (@window_managers) {
		if (`pidof "$_"` > 0) {
		    if ($in->ask_okcancel('', _("Please relog into %s to activate the changes", ucfirst $_), 1)) {
			fork and $in->exit;
			system("kwmcom logout") if /kwm/;
			system("dcop kdesktop default logout") if /kwin/;
			system("save-session --kill") if /gnome-session/;
			system("killall -QUIT icewm") if /icewm/;

			open STDIN, "</dev/zero";
			open STDOUT, ">/dev/null";
			open STDERR, ">&STDERR";
			c::setsid();
		        exec qw(perl -e), q{
                          my $wm = shift;
  		          for (my $nb = 30; $nb && `pidof "$wm"` > 0; $nb--) { sleep 1 }
  		          system("killall X ; killall -15 xdm gdm kdm prefdm") unless `pidof "$wm"` > 0;
  		        }, $_;
		    }
		    $found = 1; last;
		}
	    }
	    $in->ask_warn('', _("Please log out and then use Ctrl-Alt-BackSpace")) unless $found;
	} else {
	    $in->set_help('configureXxdm') unless $::isStandalone;
	    my $run = exists $o->{xdm} ? $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?"), 1);
	    rewriteInittab($run ? 5 : 3) unless $::testing;
	}
	autologin($prefix, $o, $in, $install);
    }
}

sub autologin {
    my ($prefix, $o, $in, $install) = @_;
    my $cmd = $prefix ? "chroot $prefix" : "";
    my @wm = (split (' ', `$cmd /usr/sbin/chksession -l`));
    my @etc_pass_fields = qw(name pw uid gid realname home shell);
    my @users = mapgrep {
	my %l; @l{@etc_pass_fields} = split ':';
	$l{uid} > 500, $l{name};
    } cat_("$prefix/etc/passwd");
    my @runlevel = mapgrep {
	/^id:([35]):initdefault:\s*$/ > 0, $1;
    } cat_("$prefix/etc/inittab");
    if (!($::isStandalone && $0 =~ /Xdrakres/) && !($::auto && $o->{skiptest}) && first(@runlevel, 0) == 5 &&
	@wm && @users && !$o->{authentication}{NIS} && $ENV{SECURE_LEVEL} <= 3) {
	my %l = getVarsFromSh("$prefix/etc/sysconfig/autologin");
	$o->{autologin} ||= $l{USER};
	$in->ask_from_entries_refH(_("Autologin"),
				   _("I can set up your computer to automatically log on one user.
If you don't want to use this feature, click on the cancel button."),
				   [ _("Choose the default user:") => { val => \$o->{autologin}, list => [ '', @users ] },
 				     _("Choose the window manager to run:") => { val => \$o->{desktop}, list => \@wm }, ]) or delete $o->{autologin};
    }
    $o->{autologin} and $::isStandalone ? do { $in->suspend; system("urpmi --auto --best-output autologin"); $in->resume; } : $in->pkg_install("autologin");
    any::setAutologin($prefix, $o->{autologin}, $o->{desktop});
    run_program::rooted($prefix, "chkconfig", "--del", "gpm") if $o->{mouse}{device} =~ /ttyS/ && !$::isStandalone;
}
