1 |
package Xconfig::xfree; # $Id: xfree.pm 262502 2009-10-21 13:46:52Z cfergeau $ |
2 |
|
3 |
my ($Revision) = '$Revision: 262502 $' =~ /(\d+)/; |
4 |
|
5 |
use diagnostics; |
6 |
use strict; |
7 |
|
8 |
use common; |
9 |
use Xconfig::parse; |
10 |
|
11 |
#- mostly internal only |
12 |
sub new { |
13 |
my ($class, $raw) = @_; |
14 |
@$raw && bless { raw => $raw }, $class; |
15 |
} |
16 |
|
17 |
sub _conf_file() { |
18 |
"$::prefix/etc/X11/xorg.conf"; |
19 |
} |
20 |
|
21 |
################################################################################ |
22 |
# I/O ########################################################################## |
23 |
################################################################################ |
24 |
sub read { |
25 |
my ($class) = @_; |
26 |
my $raw_X = $class->new(Xconfig::parse::read_XF86Config(_conf_file())) or return; |
27 |
$raw_X->{before} = $raw_X->prepare_write; |
28 |
|
29 |
if (my ($keyboard) = $raw_X->get_InputDevices('keyboard')) { |
30 |
$keyboard->{Driver}{val} = 'kbd'; |
31 |
} |
32 |
if (my ($keyboard) = $raw_X->get_InputDevices('Keyboard')) { |
33 |
$keyboard->{Driver}{val} = 'kbd'; |
34 |
} |
35 |
|
36 |
#- ugly hack to fix empty ModeLine lines, XFdrake seems to generate some, but where??? |
37 |
#- at least this allows fixing the pb by re-running XFdrake |
38 |
foreach ($raw_X->get_Sections('Monitor')) { |
39 |
my $l = $_->{ModeLine} or next; |
40 |
@$l = grep { $_->{val} } @$l; |
41 |
} |
42 |
|
43 |
foreach ($raw_X->get_Sections('Device')) { |
44 |
$_->{Driver} && $_->{Driver}{val} eq 'i810' and $_->{Driver}{val} = 'intel'; |
45 |
} |
46 |
|
47 |
$raw_X; |
48 |
} |
49 |
sub write { |
50 |
my ($raw_X, $o_file) = @_; |
51 |
my $file = $o_file || _conf_file(); |
52 |
if (!$o_file) { |
53 |
renamef($file, "$file.old"); |
54 |
} |
55 |
no_ModeLine_on_fglrx($raw_X); #- HACK |
56 |
set_Revision($raw_X); |
57 |
Xconfig::parse::write_XF86Config($raw_X->{raw}, $file); |
58 |
} |
59 |
sub prepare_write { |
60 |
my ($raw_X) = @_; |
61 |
set_Revision($raw_X); |
62 |
join('', Xconfig::parse::prepare_write_XF86Config($raw_X->{raw})); |
63 |
} |
64 |
sub is_modified { |
65 |
my ($raw_X) = @_; |
66 |
$raw_X->{before} ne $raw_X->prepare_write; |
67 |
} |
68 |
sub is_only_resolution_modified { |
69 |
my ($raw_X) = @_; |
70 |
($raw_X->{after_set_resolutions} || $raw_X->{before}) eq $raw_X->prepare_write; |
71 |
} |
72 |
sub empty_config { |
73 |
my ($class) = @_; |
74 |
$class->new(Xconfig::parse::read_XF86Config_from_string(our $default_header)); |
75 |
} |
76 |
|
77 |
my $mark = '# File generated by XFdrake'; |
78 |
sub get_Revision { |
79 |
my ($raw_X) = @_; |
80 |
my $first = $raw_X->{raw}[0]; |
81 |
$first->{pre_comment} =~ /^\Q$mark\E \(rev (\d+)\)/ && $1; |
82 |
} |
83 |
sub set_Revision { |
84 |
my ($raw_X) = @_; |
85 |
my $first = $raw_X->{raw}[0]; |
86 |
my $first_comment = $first->{pre_comment}; |
87 |
|
88 |
my $was_there = $first_comment =~ s/^\Q$mark\E.*\n//; |
89 |
$first->{pre_comment} = "$mark (rev $Revision)\n" . ($was_there ? '' : "\n") . $first_comment; |
90 |
} |
91 |
|
92 |
################################################################################ |
93 |
# keyboard ##################################################################### |
94 |
################################################################################ |
95 |
my @keyboard_fields = qw(XkbLayout XkbModel XkbDisable XkbOptions XkbCompat); |
96 |
sub get_keyboard { |
97 |
my ($raw_X) = @_; |
98 |
my $raw_kbd = _raw_get_keyboard($raw_X) or die "no keyboard section"; |
99 |
raw_export_section($raw_kbd, \@keyboard_fields); |
100 |
} |
101 |
sub _raw_get_keyboard { |
102 |
my ($raw_X) = @_; |
103 |
first($raw_X->get_Sections('InputDevice', sub { |
104 |
my ($entry) = @_; |
105 |
my $Driver = val($entry->{Driver}); |
106 |
$Driver eq 'kbd' || $Driver eq 'evdev' && val($entry->{XkbLayout}); |
107 |
})); |
108 |
} |
109 |
|
110 |
################################################################################ |
111 |
# mouse ######################################################################## |
112 |
################################################################################ |
113 |
#- example mouse: { Protocol => 'IMPS/2', Device => '/dev/psaux', Emulate3Buttons => undef, Emulate3Timeout => 50, ZAxisMapping => [ '4 5', '6 7' ] } |
114 |
#- example evdev: { bustype => '0x0003', vendor => '0x045e', product => '0x008c' } |
115 |
my @mouse_fields = qw(Protocol Device ZAxisMapping Emulate3Buttons Emulate3Timeout bustype vendor product); #-); |
116 |
sub get_mice { |
117 |
my ($raw_X) = @_; |
118 |
my @raw_mice = $raw_X->get_Sections('InputDevice', \&_is_mouse); |
119 |
map { raw_export_section($_, \@mouse_fields) } @raw_mice; |
120 |
} |
121 |
sub set_mice { |
122 |
my ($raw_X, @mice) = @_; |
123 |
@mice = grep { $_->{Driver} && $_->{Driver} ne 'evdev' } @mice; |
124 |
my @raw_mice = _new_mouse_sections($raw_X, map { delete $_->{Driver} } @mice); |
125 |
mapn { |
126 |
my ($raw_mouse, $mouse) = @_; |
127 |
raw_import_section($raw_mouse, $mouse); |
128 |
_set_Option('mouse', $raw_mouse, keys %$mouse); |
129 |
} \@raw_mice, \@mice; |
130 |
} |
131 |
sub _is_mouse { |
132 |
my ($entry) = @_; |
133 |
my $Driver = val($entry->{Driver}); |
134 |
member($Driver, qw(mouse vboxmouse vmmouse)) || $Driver eq 'evdev' && !val($entry->{XkbLayout}); |
135 |
} |
136 |
sub _new_mouse_sections { |
137 |
my ($raw_X, @Drivers) = @_; |
138 |
$raw_X->remove_InputDevices_when(\&_is_mouse); |
139 |
|
140 |
@Drivers or return; |
141 |
|
142 |
my @l = map_index { |
143 |
my $h = { Identifier => { val => 'Mouse' . ($::i + 1) }, Driver => { val => $_ } }; |
144 |
$raw_X->add_Section('InputDevice', $h); |
145 |
} @Drivers; |
146 |
|
147 |
my $layout = get_ServerLayout($raw_X)->{InputDevice} ||= []; |
148 |
push @$layout, { val => qq("Mouse1" "CorePointer") }; |
149 |
push @$layout, { val => qq("Mouse$_" "SendCoreEvents") } foreach 2 .. @Drivers; |
150 |
|
151 |
@l; |
152 |
} |
153 |
|
154 |
|
155 |
################################################################################ |
156 |
# resolution ################################################################### |
157 |
################################################################################ |
158 |
sub get_resolution { |
159 |
my ($raw_X, $o_Screen) = @_; |
160 |
first(get_resolutions($raw_X, $o_Screen)); |
161 |
} |
162 |
sub get_resolutions { |
163 |
my ($raw_X, $o_Screen) = @_; |
164 |
my $Screen = $o_Screen || $raw_X->get_default_screen or return {}; |
165 |
|
166 |
my $depth = val($Screen->{DefaultColorDepth} || $Screen->{DefaultDepth}); |
167 |
my $Display = find { !$depth || val($_->{l}{Depth}) eq $depth } @{$Screen->{Display} || []} or return { automatic => 1, Depth => $depth }; |
168 |
my $s = val($Display->{l}{Virtual} || $Display->{l}{Modes}); |
169 |
my @l; |
170 |
while ($s =~ /(\d+)(x|\s+)(\d+)/g) { |
171 |
push @l, { X => $1, Y => $3, Depth => val($Display->{l}{Depth}) }; |
172 |
} |
173 |
@l ? @l : { automatic => 1, Depth => $depth }; |
174 |
} |
175 |
sub set_resolutions { |
176 |
my ($raw_X, $resolutions, $o_Screen) = @_; |
177 |
|
178 |
my $Depth = $resolutions->[0]{Depth} eq '32' ? 24 : $resolutions->[0]{Depth}; |
179 |
my $only_resolution = $raw_X->is_only_resolution_modified; |
180 |
|
181 |
foreach my $Screen ($o_Screen ? $o_Screen : $raw_X->get_Sections('Screen')) { |
182 |
$Screen ||= $raw_X->get_default_screen or internal_error('no screen'); |
183 |
|
184 |
delete $Screen->{DefaultDepth}; |
185 |
$only_resolution &&= $Screen->{DefaultColorDepth} && $Screen->{DefaultColorDepth}{val} eq $Depth; |
186 |
if ($resolutions->[0]{X}) { |
187 |
#- if the existing config is using Virtual, keep Virtual, otherwise default to Modes |
188 |
my $Mode_name = (any { $_->{l}{Virtual} } @{$Screen->{Display} || []}) ? 'Virtual' : 'Modes'; |
189 |
|
190 |
my @l = $Mode_name eq 'Modes' ? @$resolutions : first(@$resolutions); |
191 |
my @Modes = map { sprintf($Mode_name eq 'Modes' ? '"%dx%d"' : '%d %d', @$_{'X', 'Y'}) } @l; |
192 |
|
193 |
$Screen->{Display} = [ map { |
194 |
{ l => { Depth => { val => $_ }, $Mode_name => { val => join(' ', @Modes) } } }; |
195 |
} 8, 15, 16, 24 ]; |
196 |
} else { |
197 |
delete $Screen->{Display}; |
198 |
} |
199 |
if ($Depth) { |
200 |
$Screen->{DefaultColorDepth} = { val => $Depth }; |
201 |
} else { |
202 |
delete $Screen->{DefaultColorDepth}; |
203 |
} |
204 |
} |
205 |
add_gtf_ModeLines($raw_X, $resolutions) if $resolutions->[0]{X}; |
206 |
|
207 |
$raw_X->{after_set_resolutions} = $only_resolution ? $raw_X->prepare_write : ''; |
208 |
} |
209 |
|
210 |
|
211 |
################################################################################ |
212 |
# device ####################################################################### |
213 |
################################################################################ |
214 |
my @device_fields = qw(VendorName BoardName Driver VideoRam Screen BusID); #-); |
215 |
sub get_device { |
216 |
my ($raw_X) = @_; |
217 |
first(get_devices($raw_X)); |
218 |
} |
219 |
sub get_devices { |
220 |
my ($raw_X) = @_; |
221 |
my @raw_devices = $raw_X->get_Sections('Device'); |
222 |
map { |
223 |
my $raw_device = $_; |
224 |
my $device = raw_export_section($raw_device, [ 'Identifier', @device_fields ]); |
225 |
$device->{Options} = raw_export_section($raw_device, [ grep { (deref_array($raw_device->{$_}))[0]->{Option} } keys %$raw_device ]); |
226 |
$device; |
227 |
} @raw_devices; |
228 |
} |
229 |
sub set_devices { |
230 |
my ($raw_X, @devices) = @_; |
231 |
my @raw_devices = _new_device_sections($raw_X, int @devices); |
232 |
mapn { |
233 |
my ($raw_device, $device) = @_; |
234 |
my %Options = %{$device->{Options} || {}}; |
235 |
raw_import_section($raw_device, $device, \@device_fields); |
236 |
raw_import_section($raw_device, \%Options); |
237 |
$_->{Option} = 1 foreach map { deref_array($raw_device->{$_}) } keys %Options; |
238 |
$raw_device->{''} = [ { post_comment => $device->{raw_LINES} } ] if $device->{raw_LINES}; |
239 |
} \@raw_devices, \@devices; |
240 |
} |
241 |
sub _new_device_sections { |
242 |
my ($raw_X, $nb_new) = @_; |
243 |
$raw_X->remove_Section('Device'); |
244 |
map { $raw_X->add_Section('Device', { Identifier => { val => "device$_" }, DPMS => { Option => 1 } }) } (1 .. $nb_new); |
245 |
} |
246 |
sub get_Driver { |
247 |
my ($raw_X) = @_; |
248 |
my $card = eval { $raw_X->get_device }; |
249 |
$card && $card->{Driver}; |
250 |
} |
251 |
|
252 |
################################################################################ |
253 |
# wacoms ####################################################################### |
254 |
################################################################################ |
255 |
sub set_wacoms { |
256 |
my ($raw_X, @wacoms) = @_; |
257 |
$raw_X->remove_InputDevices('wacom'); |
258 |
|
259 |
@wacoms or return; |
260 |
|
261 |
my @Modes = ('Stylus', 'Eraser', 'Cursor', 'Pad'); |
262 |
|
263 |
my $layout = get_ServerLayout($raw_X)->{InputDevice} ||= []; |
264 |
each_index { |
265 |
my $wacom = $_; |
266 |
foreach (@Modes) { |
267 |
next if $wacom->{USB}; |
268 |
my $identifier = $_ . ($::i + 1); |
269 |
my $h = { Identifier => { val => $identifier }, |
270 |
Driver => { val => 'wacom' }, |
271 |
Type => { val => lc $_, Option => 1 }, |
272 |
Device => { val => $wacom->{Device}, Option => 1 } |
273 |
}; |
274 |
$raw_X->add_Section('InputDevice', $h); |
275 |
push @$layout, { val => qq("$identifier" "SendCoreEvents") }; |
276 |
} |
277 |
} @wacoms; |
278 |
} |
279 |
|
280 |
################################################################################ |
281 |
# monitor ###################################################################### |
282 |
################################################################################ |
283 |
my @monitor_fields = qw(VendorName ModelName HorizSync VertRefresh PreferredMode); |
284 |
sub get_monitors { |
285 |
my ($raw_X) = @_; |
286 |
my @raw_monitors = $raw_X->get_Sections('Monitor'); |
287 |
map { |
288 |
my $h = raw_export_section($_, [ 'Identifier', @monitor_fields ]); |
289 |
$h->{ModeLine} = $_->{ModeLine} if $_->{ModeLine}; |
290 |
$h; |
291 |
} @raw_monitors; |
292 |
} |
293 |
sub set_monitors { |
294 |
my ($raw_X, @monitors) = @_; |
295 |
my @raw_monitors = _new_monitor_sections($raw_X, int @monitors); |
296 |
mapn { |
297 |
my ($raw_monitor, $monitor) = @_; |
298 |
raw_import_section($raw_monitor, $monitor, \@monitor_fields); |
299 |
$raw_monitor->{PreferredMode}{Option} = 1 if $raw_monitor->{PreferredMode}; |
300 |
$raw_monitor->{ModeLine} = $monitor->{ModeLine} if $monitor->{ModeLine}; |
301 |
} \@raw_monitors, \@monitors; |
302 |
} |
303 |
sub get_or_new_monitors { |
304 |
my ($raw_X, $nb_new) = @_; |
305 |
my @monitors = $raw_X->get_monitors; |
306 |
|
307 |
#- ensure we have exactly $nb_new monitors; |
308 |
if ($nb_new > @monitors) { |
309 |
@monitors, ({}) x ($nb_new - @monitors); |
310 |
} else { |
311 |
splice(@monitors, 0, $nb_new); |
312 |
} |
313 |
} |
314 |
sub _new_monitor_sections { |
315 |
my ($raw_X, $nb_new) = @_; |
316 |
$raw_X->remove_Section('Monitor'); |
317 |
map { $raw_X->add_Section('Monitor', { Identifier => { val => "monitor$_" }, ModeLine => default_ModeLine() }) } (1 .. $nb_new); |
318 |
} |
319 |
sub default_ModeLine() { |
320 |
ModeLine_from_string(qq(Section "Monitor"\n) . (our $default_ModeLine) . qq(EndSection\n)); |
321 |
} |
322 |
|
323 |
sub XxY { |
324 |
my ($resolution) = @_; |
325 |
$resolution && $resolution->{X} && $resolution->{Y} && |
326 |
$resolution->{X} . 'x' . $resolution->{Y}; |
327 |
} |
328 |
|
329 |
sub xorg_builtin_resolution { |
330 |
my ($X, $Y) = @_; |
331 |
my $res = $X . 'x' . $Y; |
332 |
|
333 |
$res eq '1280x1024' || |
334 |
$res ne '1400x1050' && $res ne '1152x864' && $Xconfig::xfree::resolution2ratio{$res} eq '4/3'; |
335 |
} |
336 |
|
337 |
sub resolution2ratio { |
338 |
my ($resolution, $b_non_strict) = @_; |
339 |
my $res = XxY($resolution); |
340 |
$res eq '1280x1024' && $b_non_strict ? '4/3' : $Xconfig::xfree::resolution2ratio{$res}; |
341 |
} |
342 |
|
343 |
sub add_gtf_ModeLines { |
344 |
my ($raw_X, $resolutions) = @_; |
345 |
|
346 |
my $banner = 'modeline generated by gtf(1) [handled by XFdrake]'; |
347 |
my @to_add; |
348 |
if (!xorg_builtin_resolution($resolutions->[0]{X}, $resolutions->[0]{Y})) { |
349 |
@to_add = map { |
350 |
my $resolution = $_; |
351 |
map { |
352 |
my $s = run_program::rooted_get_stdout($::prefix, 'gtf', $resolution->{X}, $resolution->{Y}, $_); |
353 |
if (my ($name, $val) = $s =~ /ModeLine\s*"(.*)"(.*)/i) { |
354 |
chomp $val; |
355 |
$name =~ s/\.00//; #- nicer that way |
356 |
{ val => qq("${name}"$val), pre_comment => "# $banner\n" }; |
357 |
} else { () } |
358 |
} reverse(sort_numbers(@Xconfig::xfree::vfreqs)); |
359 |
} @$resolutions; |
360 |
} |
361 |
|
362 |
$raw_X->set_monitors(map { |
363 |
@{$_->{ModeLine}} = ( |
364 |
(grep { index($_->{pre_comment}, $banner) == -1 } @{$_->{ModeLine}}), |
365 |
@to_add, |
366 |
); |
367 |
$_; |
368 |
} $raw_X->get_monitors); |
369 |
|
370 |
1; |
371 |
} |
372 |
|
373 |
#- HACK for fglrx (#30934) |
374 |
sub no_ModeLine_on_fglrx { |
375 |
my ($raw_X) = @_; |
376 |
|
377 |
if (get_Driver($raw_X) eq 'fglrx') { |
378 |
delete $_->{ModeLine} foreach $raw_X->get_Sections('Monitor'); |
379 |
} |
380 |
} |
381 |
|
382 |
################################################################################ |
383 |
# screens ###################################################################### |
384 |
################################################################################ |
385 |
sub get_default_screen { |
386 |
my ($raw_X) = @_; |
387 |
my @l = $raw_X->get_Sections('Screen'); |
388 |
(find { $_->{Identifier} && val($_->{Identifier}) eq 'screen1' || |
389 |
$_->{Driver} && val($_->{Driver}) =~ /svga|accel/ } @l) || $l[0]; |
390 |
} |
391 |
sub set_screens { |
392 |
my ($raw_X, @screens) = @_; |
393 |
my @raw_screens = _new_screen_sections($raw_X, int @screens); |
394 |
mapn { |
395 |
my ($raw_screen, $screen) = @_; |
396 |
raw_import_section($raw_screen, $screen); |
397 |
} \@raw_screens, \@screens; |
398 |
} |
399 |
sub _new_screen_sections { |
400 |
my ($raw_X, $nb_new) = @_; |
401 |
$raw_X->remove_Section('Screen'); |
402 |
my @l = map { $raw_X->add_Section('Screen', { Identifier => { val => "screen$_" } }) } (1 .. $nb_new); |
403 |
|
404 |
get_ServerLayout($raw_X)->{Screen} = [ |
405 |
{ val => qq("screen1") }, |
406 |
map { { val => sprintf('"screen%d" RightOf "screen%d"', $_, $_ - 1) } } (2 .. $nb_new) |
407 |
]; |
408 |
@l; |
409 |
} |
410 |
sub is_fbdev { |
411 |
my ($raw_X, $o_Screen) = @_; |
412 |
|
413 |
my $Screen = $o_Screen || $raw_X->get_default_screen or return; |
414 |
|
415 |
my $Device = $raw_X->get_Section_by_Identifier('Device', val($Screen->{Device})) or internal_error("no device named $Screen->{Device}"); |
416 |
val($Device->{Driver}) eq 'fbdev'; |
417 |
} |
418 |
|
419 |
|
420 |
|
421 |
|
422 |
################################################################################ |
423 |
# modules ###################################################################### |
424 |
################################################################################ |
425 |
sub get_modules { |
426 |
my ($raw_X) = @_; |
427 |
my $raw_Module = $raw_X->get_Section('Module') or return; |
428 |
my $Module = raw_export_section($raw_Module, ['Load']); |
429 |
@{$Module->{Load} || []}; |
430 |
} |
431 |
sub get_disabled_modules { |
432 |
my ($raw_X) = @_; |
433 |
my $raw_Module = $raw_X->get_Section('Module') or return; |
434 |
my $Module = raw_export_section($raw_Module, ['Disable']); |
435 |
@{$Module->{Disable} || []}; |
436 |
} |
437 |
sub add_load_module { |
438 |
my ($raw_X, $module) = @_; |
439 |
my $raw_Module = $raw_X->get_Section('Module') || $raw_X->add_Section('Module', {}); |
440 |
|
441 |
my %load_modules_comment = ( |
442 |
dbe => 'Double-Buffering Extension', |
443 |
v4l => 'Video for Linux', |
444 |
dri => 'direct rendering', |
445 |
glx => '3D layer', |
446 |
'glx-3.so' => '3D layer', |
447 |
); |
448 |
my $comment = $load_modules_comment{$module}; |
449 |
push @{$raw_Module->{Load}}, { val => $module, |
450 |
comment_on_line => $comment && " # $comment", |
451 |
} if !member($module, $raw_X->get_modules); |
452 |
} |
453 |
sub add_disable_module { |
454 |
my ($raw_X, $module) = @_; |
455 |
my $raw_Module = $raw_X->get_Section('Module') || $raw_X->add_Section('Module', {}); |
456 |
|
457 |
push @{$raw_Module->{Disable}}, { val => $module } if !member($module, $raw_X->get_disabled_modules); |
458 |
} |
459 |
sub remove_load_module { |
460 |
my ($raw_X, $module) = @_; |
461 |
my $raw_Module = $raw_X->get_Section('Module') or return; |
462 |
if (my @l = grep { $_->{val} ne $module } @{$raw_Module->{Load}}) { |
463 |
$raw_Module->{Load} = \@l; |
464 |
} else { |
465 |
delete $raw_Module->{Load}; |
466 |
} |
467 |
} |
468 |
sub remove_disable_module { |
469 |
my ($raw_X, $module) = @_; |
470 |
my $raw_Module = $raw_X->get_Section('Module') or return; |
471 |
if (my @l = grep { $_->{val} ne $module } @{$raw_Module->{Disable}}) { |
472 |
$raw_Module->{Disable} = \@l; |
473 |
} else { |
474 |
delete $raw_Module->{Disable}; |
475 |
} |
476 |
} |
477 |
sub set_load_module { |
478 |
my ($raw_X, $module, $bool) = @_; |
479 |
if ($bool) { |
480 |
remove_disable_module($raw_X, $module); |
481 |
add_load_module($raw_X, $module); |
482 |
} else { |
483 |
remove_load_module($raw_X, $module); |
484 |
add_disable_module($raw_X, $module); |
485 |
} |
486 |
} |
487 |
|
488 |
|
489 |
################################################################################ |
490 |
# modules ###################################################################### |
491 |
################################################################################ |
492 |
sub remove_extension { |
493 |
my ($raw_X, $extension) = @_; |
494 |
my $raw = $raw_X->get_Section('Extensions') or return; |
495 |
delete $raw->{$extension}; |
496 |
%$raw or $raw_X->remove_Section('Extensions'); |
497 |
} |
498 |
sub get_extension { |
499 |
my ($raw_X, $extension) = @_; |
500 |
my $raw = $raw_X->get_Section('Extensions'); |
501 |
$raw && $raw->{$extension} && $raw->{$extension}{val}; |
502 |
} |
503 |
sub set_extension { |
504 |
my ($raw_X, $extension, $val) = @_; |
505 |
my $raw = $raw_X->get_Section('Extensions') || $raw_X->add_Section('Extensions', {}); |
506 |
$raw->{$extension} = { 'Option' => 1, val => $val }; |
507 |
} |
508 |
|
509 |
################################################################################ |
510 |
# ModulePath ################################################################### |
511 |
################################################################################ |
512 |
sub get_ModulePaths { |
513 |
my ($raw_X) = @_; |
514 |
my $raw_Files = $raw_X->get_Section('Files') or return; |
515 |
my $Files = raw_export_section($raw_Files, ['ModulePath']); |
516 |
@{$Files->{ModulePath} || []}; |
517 |
} |
518 |
sub add_ModulePath { |
519 |
my ($raw_X, $ModulePath) = @_; |
520 |
my $raw_Files = $raw_X->get_Section('Files') || $raw_X->add_Section('Files', {}); |
521 |
|
522 |
push @{$raw_Files->{ModulePath}}, { val => $ModulePath } if !member($ModulePath, $raw_X->get_ModulePaths); |
523 |
} |
524 |
sub remove_ModulePath { |
525 |
my ($raw_X, $ModulePath) = @_; |
526 |
my $raw_Files = $raw_X->get_Section('Files') or return; |
527 |
my @l = grep { $_->{val} ne $ModulePath && $_->{val} ne "$ModulePath/" } @{$raw_Files->{ModulePath}}; |
528 |
$raw_Files->{ModulePath} = \@l; |
529 |
} |
530 |
|
531 |
#-############################################################################## |
532 |
#- helpers |
533 |
#-############################################################################## |
534 |
sub _set_Option { |
535 |
my ($category, $node, @names) = @_; |
536 |
|
537 |
if (member($category, 'keyboard', 'mouse')) { |
538 |
#- everything we export is an Option |
539 |
$_->{Option} = 1 foreach map { deref_array($node->{$_}) } @names; |
540 |
} |
541 |
} |
542 |
|
543 |
sub get_InputDevices { |
544 |
my ($raw_X, $Driver) = @_; |
545 |
$raw_X->get_Sections('InputDevice', sub { val($_[0]{Driver}) eq $Driver }); |
546 |
} |
547 |
sub remove_InputDevices { |
548 |
my ($raw_X, $Driver) = @_; |
549 |
$raw_X->remove_InputDevices_when(sub { val($_[0]{Driver}) eq $Driver }); |
550 |
} |
551 |
sub remove_InputDevices_when { |
552 |
my ($raw_X, $when) = @_; |
553 |
my @removed = $raw_X->remove_Section('InputDevice', $when); |
554 |
|
555 |
my $Identifier_regexp = join('|', map { val($_->{l}{Identifier}) } @removed); |
556 |
my $layout = get_ServerLayout($raw_X)->{InputDevice} ||= []; |
557 |
@$layout = grep { $_->{val} !~ /^"$Identifier_regexp"/ } @$layout; |
558 |
} |
559 |
|
560 |
sub get_ServerLayout { |
561 |
my ($raw_X) = @_; |
562 |
$raw_X->get_Section('ServerLayout') || |
563 |
$raw_X->add_Section('ServerLayout', { Identifier => { val => 'layout1' } }); |
564 |
} |
565 |
|
566 |
#-############################################################################## |
567 |
#- helpers |
568 |
#-############################################################################## |
569 |
sub raw_export_section { |
570 |
my ($section, $fields) = @_; |
571 |
|
572 |
my $export_name = sub { |
573 |
my ($name) = @_; |
574 |
my $h = $section->{$name} or return; |
575 |
|
576 |
my @l = map { if_(!$_->{commented}, $_->{val}) } deref_array($h) or return; |
577 |
$name => (ref($h) eq 'ARRAY' ? \@l : $l[0]); |
578 |
}; |
579 |
|
580 |
my %h = map { $export_name->($_) } @$fields; |
581 |
\%h; |
582 |
} |
583 |
|
584 |
sub raw_import_section { |
585 |
my ($section, $h, $o_fields) = @_; |
586 |
foreach ($o_fields ? grep { exists $h->{$_} } @$o_fields : sort keys %$h) { |
587 |
my @l = map { ref($_) eq 'HASH' ? $_ : { val => $_ } } deref_array($h->{$_}); |
588 |
$section->{$_} = (ref($h->{$_}) eq 'ARRAY' ? \@l : $l[0]); |
589 |
} |
590 |
} |
591 |
|
592 |
sub add_Section { |
593 |
my ($raw_X, $Section, $h) = @_; |
594 |
my @suggested_ordering = qw(Extensions Files ServerFlags Module DRI Keyboard Pointer XInput InputDevice Monitor Device Screen ServerLayout); |
595 |
my %order = map_index { { lc($_) => $::i } } @suggested_ordering; |
596 |
my $e = { name => $Section, l => $h }; |
597 |
my $added; |
598 |
my $raw = $raw_X->{raw}; |
599 |
@$raw = map { |
600 |
if ($order{lc $_->{name}} > $order{lc $Section} && !$added) { |
601 |
$added = 1; |
602 |
($e, $_); |
603 |
} else { $_ } |
604 |
} @$raw; |
605 |
push @$raw, $e if !$added; |
606 |
$h; |
607 |
} |
608 |
sub remove_Section { |
609 |
my ($raw_X, $Section, $o_when) = @_; |
610 |
my $raw = $raw_X->{raw}; |
611 |
my ($keep, $remove) = partition { $_->{name} ne $Section || $o_when && !$o_when->($_->{l}) } @$raw; |
612 |
@$raw = @$keep; |
613 |
@$remove; |
614 |
} |
615 |
sub get_Sections { |
616 |
my ($raw_X, $Section, $o_when) = @_; |
617 |
map { if_(lc($_->{name}) eq lc($Section) && (!$o_when || $o_when->($_->{l})), $_->{l}) } @{$raw_X->{raw}}; |
618 |
} |
619 |
sub get_Section { |
620 |
my ($raw_X, $Section, $o_when) = @_; |
621 |
my @l = get_Sections($raw_X, $Section, $o_when); |
622 |
@l > 1 and log::l("Xconfig: found more than one Section $Section"); |
623 |
$l[0]; |
624 |
} |
625 |
sub get_Section_by_Identifier { |
626 |
my ($raw_X, $Section, $Identifier) = @_; |
627 |
my @l = get_Sections($raw_X, $Section, sub { val($_[0]{Identifier}) eq $Identifier }); |
628 |
@l > 1 and die "more than one Section $Section has Identifier $Identifier"; |
629 |
$l[0]; |
630 |
} |
631 |
|
632 |
sub val { |
633 |
my ($ref) = @_; |
634 |
$ref && $ref->{val}; |
635 |
} |
636 |
|
637 |
|
638 |
sub ModeLine_from_string { |
639 |
my ($s) = @_; |
640 |
my $raw_X_for_ModeLine = Xconfig::parse::read_XF86Config_from_string($s); |
641 |
get_Section(Xconfig::xfree->new($raw_X_for_ModeLine), 'Monitor')->{ModeLine}; |
642 |
} |
643 |
|
644 |
|
645 |
|
646 |
# http://home.comcast.net/~igpl/Aspect.html |
647 |
# movies http://www.technosound.co.uk/nav.php?pageid=hcg_widescreen |
648 |
# esp for 1360x768 http://www.winischhofer.at/linuxsispart1.shtml |
649 |
|
650 |
# www.dell.com/downloads/global/vectors/2003_cvt.pdf |
651 |
# file vesamodes in Xorg is DMT Standard Display Modes |
652 |
|
653 |
# http://www.vesa.org/Public |
654 |
# http://www.vesa.org/Public/EEDIDguideV1.pdf |
655 |
|
656 |
#- http://www.vesa.org/Public/CVT |
657 |
our @CVT_ratios = qw(4/3 16/9 16/10 5/4 15/9 3/2); |
658 |
our @CVT_vfreqs = qw(50 60 75 85); # and also 60Hz "reduced blanking" in CVT |
659 |
|
660 |
our @vfreqs = (@CVT_vfreqs, qw(100 120)); |
661 |
|
662 |
our %ratio2resolutions = ( |
663 |
|
664 |
# first all the CVT_ratios |
665 |
|
666 |
# 1.25 |
667 |
'5/4' => [ '640x512', |
668 |
'720x576', |
669 |
'1280x1024', # SXGA |
670 |
'1800x1440', |
671 |
#'2560x2048', # QSXGA |
672 |
#'5120x4096', # HSXGA |
673 |
], |
674 |
|
675 |
# 1.33 |
676 |
'4/3' => [ '320x240', # QVGA |
677 |
'480x360', |
678 |
'640x480', # VGA |
679 |
#'768x576', # PAL |
680 |
'800x600', # SVGA |
681 |
'832x624', |
682 |
'1024x768', # XGA |
683 |
'1152x864', |
684 |
'1280x960', |
685 |
'1400x1050', # SXGA+ |
686 |
'1600x1200', # UXGA |
687 |
#'1792x1344', |
688 |
#'1800x1350', |
689 |
#'1856x1392', |
690 |
'1920x1440', |
691 |
'2048x1536', # QXGA |
692 |
'2800x2100', # QSXGA+ |
693 |
'3200x2400', # QUXGA |
694 |
'4096x3072', # HXGA |
695 |
'6400x4800', # HUXGA |
696 |
# DBLSCAN: 400x300 416x312 512x384 576x432 700x525 896x672 928x696 960x720 |
697 |
], |
698 |
|
699 |
# 1.5 |
700 |
'3/2' => [ '360x240', |
701 |
'720x480', # NTSC |
702 |
'1152x768', |
703 |
#'1280x854', |
704 |
#'1440x960', |
705 |
], # 576x384 (DBLSCAN of 1152x768) |
706 |
|
707 |
# 1.6 |
708 |
'16/10' => [ #'320x200', # CGA |
709 |
#'640x400', |
710 |
#'960x600', |
711 |
'1280x800', |
712 |
'1440x900', |
713 |
'1600x1000', |
714 |
'1680x1050', # WSXGA+ |
715 |
'1920x1200', # WUXGA |
716 |
'2560x1600', # WQXGA |
717 |
'3840x2400', # WQUXGA |
718 |
'5120x3200', # WHXGA |
719 |
'7680x4800', # WHUXGA |
720 |
], |
721 |
|
722 |
# 1.67 |
723 |
'15/9' => [ '800x480', |
724 |
'1280x768', # WXGA ?? should be 1366x768 ?? |
725 |
], |
726 |
|
727 |
# 1.78 |
728 |
'16/9' => [ #'854x480', # WVGA |
729 |
#'960x540', |
730 |
#'1024x576', |
731 |
'1280x720', # HD 720 |
732 |
'1360x765', # one kind of WXGA (rounded down) |
733 |
'1366x768', # HDTV |
734 |
'1600x900', # WSXGA? |
735 |
'1920x1080', # HD 1080 |
736 |
'7680x4320', # UHDTV |
737 |
], |
738 |
|
739 |
# now more weird things |
740 |
|
741 |
# 1.32 |
742 |
# '192/145' => [ '1152x870' ], |
743 |
|
744 |
# 1.328 |
745 |
# '85/64' => [ '1360x1024' ], |
746 |
|
747 |
# 1.42 |
748 |
# '17/12' => [ '544x384' ] , |
749 |
|
750 |
# 1.56 |
751 |
#'25/16' => [ '1600x1024', # WSXGA |
752 |
# '3200x2048', # WQSXGA |
753 |
# '6400x4096', # WHSXGA |
754 |
# ], # (DBLSCAN 800x512) |
755 |
|
756 |
# 1.767 |
757 |
# '53/30' => [ '848x480' ], |
758 |
|
759 |
# 1.775 |
760 |
# '71/40' => [ '852x480' ], |
761 |
|
762 |
# 1.783 |
763 |
# '107/60' => [ '856x480' ], |
764 |
|
765 |
N_("_:weird aspect ratio\nother") => [ |
766 |
|
767 |
# 1.707 = 128/75 |
768 |
'1024x600', |
769 |
|
770 |
# 1.771 = 85/48 |
771 |
'1360x768', |
772 |
|
773 |
# 2.13 = 32/15 |
774 |
'1024x480', '1280x600', # VAIO |
775 |
|
776 |
# 2.67 = 8/3 |
777 |
'2048x768', '2560x960', '3200x1200', |
778 |
|
779 |
# 4.0 = 4/1 |
780 |
'3072x768', '3456x864', '3840x960', '4800x1200', |
781 |
|
782 |
# ?? 352x288 640x350 (DBLSCAN 320x175) 720x400 (DBLSCAN 360x200) |
783 |
], |
784 |
); |
785 |
|
786 |
our %resolution2ratio = map_each { map { $_ => $::a } @$::b } %ratio2resolutions; |
787 |
our @resolutions = map_each { @$::b } %ratio2resolutions; |
788 |
|
789 |
foreach my $ratio (keys %ratio2resolutions) { |
790 |
if ($ratio =~ m!^(\d+)/(\d+)$!) { |
791 |
my $eval = $2 / $1; |
792 |
foreach (@{$ratio2resolutions{$ratio}}) { |
793 |
my ($x, $y) = /(\d+)x(\d+)/; |
794 |
my $y2 = round($x * $eval); |
795 |
$y == $y2 or do { |
796 |
my $good_ratio = (find { m!^(\d+)/(\d+)$! && $y == round($x * $2 / $1) } keys %ratio2resolutions) || '??'; |
797 |
die "bad ratio $ratio for resolution $_, it should be $good_ratio\n"; |
798 |
}; |
799 |
} |
800 |
} |
801 |
} |
802 |
|
803 |
our $default_header = <<'END'; |
804 |
# ********************************************************************** |
805 |
# Refer to the xorg.conf man page for details about the format of |
806 |
# this file. |
807 |
# ********************************************************************** |
808 |
|
809 |
Section "ServerFlags" |
810 |
Option "DontZap" "False" # disable <Ctrl><Alt><BS> (server abort) |
811 |
#DontZoom # disable <Ctrl><Alt><KP_+>/<KP_-> (resolution switching) |
812 |
AllowMouseOpenFail # allows the server to start up even if the mouse does not work |
813 |
END |
814 |
|
815 |
require detect_devices; |
816 |
$default_header .= <<'END_XBOX' if detect_devices::is_xbox(); |
817 |
Option "PciProbe1" "false" |
818 |
Option "PciProbe2" "false" |
819 |
Option "PciForceConfig1" "false" |
820 |
Option "PciForceConfig2" "false" |
821 |
Option "PciOsConfig" "true" |
822 |
END_XBOX |
823 |
|
824 |
$default_header .= <<'END'; |
825 |
EndSection |
826 |
END |
827 |
|
828 |
our $default_ModeLine = arch() =~ /ppc/ ? <<'END_PPC' : <<'END'; |
829 |
# Apple iMac modes |
830 |
ModeLine "1024x768" 78.525 1024 1049 1145 1312 768 769 772 800 +hsync +vsync |
831 |
ModeLine "800x600" 62.357 800 821 901 1040 600 601 604 632 +hsync +vsync |
832 |
ModeLine "640x480" 49.886 640 661 725 832 480 481 484 514 +hsync +vsync |
833 |
# Apple monitors tend to do 832x624 |
834 |
ModeLine "832x624" 57 832 876 940 1152 624 625 628 667 -hsync -vsync |
835 |
# Apple PowerBook G3 |
836 |
ModeLine "800x600" 100 800 816 824 840 600 616 624 640 -hsync -vsync |
837 |
# Apple TI Powerbook |
838 |
ModeLine "1152x768" 78.741 1152 1173 1269 1440 768 769 772 800 +vsync +vsync |
839 |
# Pismo Firewire G3 |
840 |
ModeLine "1024x768" 65 1024 1032 1176 1344 768 771 777 806 -hsync -vsync |
841 |
# iBook2 |
842 |
ModeLine "1024x768" 65 1024 1048 1184 1344 768 771 777 806 -hsync -vsync |
843 |
# 17" Apple Studio Display |
844 |
ModeLine "1024x768" 112.62 1024 1076 1248 1420 768 768 780 808 +hsync +vsync |
845 |
# HiRes Apple Studio Display |
846 |
ModeLine "1280x1024" 135 1280 1288 1392 1664 1024 1027 1030 1064 |
847 |
# Another variation |
848 |
ModeLine "1280x1024" 134.989 1280 1317 1429 1688 1024 1025 1028 1066 +hsync +vsync |
849 |
END_PPC |
850 |
# TV fullscreen mode or DVD fullscreen output. |
851 |
# 768x576 @ 79 Hz, 50 kHz hsync |
852 |
ModeLine "768x576" 50.00 768 832 846 1000 576 590 595 630 |
853 |
# 768x576 @ 100 Hz, 61.6 kHz hsync |
854 |
ModeLine "768x576" 63.07 768 800 960 1024 576 578 590 616 |
855 |
END |
856 |
|
857 |
1; |