package Xconfigurator; # $Id$ use diagnostics; use strict; use vars qw($in $do_pkgs @window_managers @depths @monitorSize2resolution @hsyncranges %min_hsync4wres @vsyncranges %depths @resolutions %serversdriver @svgaservers @accelservers @allbutfbservers @allservers %vgamodes %videomemory @ramdac_name @ramdac_id @clockchip_name @clockchip_id %keymap_translate %standard_monitors $XF86firstchunk_text $keyboardsection_start $keyboardsection_start_v4 $keyboardsection_part2 $keyboardsection_part3 $keyboardsection_part3_v4 $keyboardsection_end $pointersection_text $monitorsection_text1 $monitorsection_text2 $monitorsection_text3 $monitorsection_text4 $modelines_text_Trident_TG_96xx $modelines_text_ext $modelines_text $devicesection_text $devicesection_text_v4 $screensection_text1 %lines @options %xkb_options $good_default_monitor $low_default_monitor $layoutsection_v4 $modelines_text_apple); use common; use log; use detect_devices; use run_program; use Xconfigurator_consts; use any; use modules; my $tmpconfig = "/tmp/Xconfig"; my ($prefix, %monitors, %standard_monitors_); sub xtest { my ($display) = @_; $::isStandalone ? system("DISPLAY=$display /usr/X11R6/bin/xtest") == 0 : c::Xtest($display); } sub getVGAMode($) { $_[0]->{card}{vga_mode} || $vgamodes{"640x480x16"}; } sub readCardsDB { my ($file) = @_; my ($card, %cards); my $F = common::openFileMaybeCompressed($file); 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 {}, }; local $_; while (<$F>) { $lineno++; s/\s+$//; /^#/ and next; /^$/ and next; /^END/ and do { $cards{$card->{type}} = $card if $card; 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 = "$ENV{SHARE_PATH}/ldetect-lst/CardsNames"; map { (split '=>')[0] } grep { !/^#/ } catMaybeCompressed($file); } sub cardName2RealName { my ($name) = @_; my $file = "$ENV{SHARE_PATH}/ldetect-lst/CardsNames"; foreach (catMaybeCompressed($file)) { chop; next if /^#/; my ($name_, $real) = split '=>'; return $real if $name eq $name_; } $name; } sub updateCardAccordingName { my ($card, $name) = @_; my $cards = readCardsDB("$ENV{SHARE_PATH}/ldetect-lst/Cards+"); add2hash($card->{flags}, $cards->{$name}{flags}); add2hash($card, $cards->{$name}); $card; } sub readMonitorsDB { my ($file) = @_; %monitors and return; my $F = common::openFileMaybeCompressed($file); local $_; my $lineno = 0; while (<$F>) { $lineno++; s/\s+$//; /^#/ and next; /^$/ and next; my @fields = qw(vendor type eisa hsyncrange vsyncrange dpms); my @l = split /\s*;\s*/; 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)} = $standard_monitors_{$k} = { hsyncrange => $v->[1], vsyncrange => $v->[2] }; } } sub keepOnlyLegalModes { my ($card, $monitor) = @_; my $mem = 1024 * ($card->{memory} || ($card->{server} eq 'FBDev' ? 2048 : 32768)); #- limit to 2048x1536x64 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 @cards; if (my @c = grep { $_->{driver} =~ /(Card|Server):/ } detect_devices::probeall()) { foreach my $i (0..$#c) { local $_ = $c[$i]->{driver}; my $card = { identifier => ($c[$i]{description} . (@c > 1 && " $i")) }; $card->{type} = $1 if /Card:(.*)/; $card->{server} = $1 if /Server:(.*)/; $card->{flags}{needVideoRam} = /86c368|S3 Inc|Tseng.*ET6\d00/; $card->{busid} = "PCI:$c[$i]{pci_bus}:$c[$i]{pci_device}:$c[$i]{pci_function}"; push @{$card->{lines}}, @{$lines{$card->{identifier}} || []}; push @cards, $card; } } #- take a default on sparc if nothing has been found. if (arch() =~ /^sparc/ && !@cards) { log::l("Using probe with /proc/fb as nothing has been found!"); local $_ = cat_("/proc/fb"); if (/Mach64/) { push @cards, { server => "Mach64" } } elsif (/Permedia2/) { push @cards, { server => "3DLabs" } } else { push @cards, { server => "Sun24" } } } #- special case for dual head card using only one busid. @cards = map { my $dup = $_->{identifier} =~ /MGA G[45]50/ ? 2 : 1; if ($dup > 1) { my @result; my $orig = $_; foreach (1..$dup) { my $card = {}; add2hash($card, $orig); push @result, $card; } @result; } else { ($_); } } @cards; #- make sure no type are already used, duplicate both screen #- and rename type (because used as id). if (@cards > 1) { my $card = 1; foreach (@cards) { updateCardAccordingName($_, $_->{type}) if $_->{type}; $_->{type} = "$_->{type} $card"; $card++; } } #- in case of only one cards, remove all busid reference, this will avoid #- need of change of it if the card is moved. #- on many PPC machines, card is on-board, busid is important, leave? @cards == 1 and delete $cards[0]{busid} if arch() !~ /ppc/; @cards; } sub cardConfiguration(;$$$) { my ($card, $noauto, $cardOptions) = @_; $card ||= {}; updateCardAccordingName($card, $card->{type}) if $card->{type}; #- try to get info from given type undef $card->{type} unless $card->{server} || $card->{driver}; #- bad type as we can't find the server my @cards = cardConfigurationAuto(); if (@cards > 1 && ($noauto || !$card->{server})) {#} && !$::isEmbedded) { my (%single_heads, @choices, $tc); my $configure_multi_head = sub { add2hash($card, $cards[0]); #- assume good default. delete $card->{cards} if $noauto; $card->{cards} or $card->{cards} = \@cards; $card->{force_xf4} = 1; #- force XF4 in such case. $card->{Xinerama} = $_[0]; }; foreach (@cards) { unless ($_->{driver} && !$_->{flags}{unsupported}) { log::l("found card \"$_->{identifier}\" not supported by XF4, disabling mutli-head support"); $configure_multi_head = undef; } #- if more than one card use the same BusID, we have to use screen. if ($single_heads{$_->{busid}}) { $single_heads{$_->{busid}}{screen} ||= 0; $_->{screen} = $single_heads{$_->{busid}}{screen} + 1; } $single_heads{$_->{busid}} = $_; } if ($configure_multi_head) { push @choices, { text => _("Configure all heads independently"), code => sub { $configure_multi_head->('') } }; push @choices, { text => _("Use Xinerama extension"), code => sub { $configure_multi_head->(1) } }; } foreach my $e (values %single_heads) { push @choices, { text => _("Configure only card \"%s\" (%s)", $e->{identifier}, $e->{busid}), code => sub { add2hash($card, $e); foreach (qw(cards screen Xinerama)) { delete $card->{$_} } } }; } $tc = $in->ask_from_listf(_("Multi-head configuration"), _("Your system support multiple head configuration. What do you want to do?"), sub { translate($_[0]{text}) }, \@choices) or return; #- no more die, CHECK with auto that return ''! $tc->{code} and $tc->{code}(); } else { #- only one head found, configure it as before. add2hash($card, $cards[0]) unless $noauto; delete $card->{cards}; delete $card->{Xinerama}; } $card->{server} = 'FBDev' unless !$cardOptions->{allowFB} || $card->{server} || $card->{driver} || $card->{type} || $noauto; $card->{type} = cardName2RealName($in->ask_from_treelist(_("Graphic card"), _("Select a graphic card"), '|', ['Other|Unlisted', readCardsNames()])) or return unless $card->{type} || $card->{server} || $card->{driver}; undef $card->{type}, $card->{server} = $in->ask_from_list(_("X server"), _("Choose a X server"), $cardOptions->{allowFB} ? \@allservers : \@allbutfbservers ) or return if $card->{type} eq 'Other|Unlisted'; updateCardAccordingName($card, $card->{type}) if $card->{type}; add2hash($card, { vendor => "Unknown", board => "Unknown" }); foreach ($card, @{$card->{cards} || []}) { $_->{memory} = 4096, delete $_->{depth} if $_->{driver} eq 'i810'; $_->{memory} = 16384, delete $_->{depth} if $_->{chipset} =~ /PERMEDIA/ && $_->{memory} <= 1024; } #- 3D acceleration configuration for XFree 3.3 using Utah-GLX. $card->{Utah_glx} = ($card->{identifier} =~ /Matrox.* G[24]00/ || #- 8bpp does not work. $card->{identifier} =~ /Rage X[CL]/ || $card->{identifier} =~ /3D Rage (?:LT|Pro)/);