# chres.pl - change the resolution of the screen # # This script was written by Aldo Calpini (dada@perl.it) in 2001. # It is released into the public domain. You may use it freely, however, # if you make any modifications and redistribute, please list your name # and describe the changes. This script is distributed without any warranty, # express or implied. use Win32::API 0.20; use Getopt::Mixed; my $VERSION = '0.51'; # the required APIs my $EnumDisplaySettings = new Win32::API( "user32", "EnumDisplaySettings", "PNP", "N" ); my $ChangeDisplaySettings = new Win32::API( "user32", "ChangeDisplaySettings", "PN", "N" ); my $CreateDC = new Win32::API( "gdi32", "CreateDC", "PPPP", "N" ); my $GetDeviceCaps = new Win32::API( "gdi32", "GetDeviceCaps", "NN", "N" ); my $DeleteDC = new Win32::API( "gdi32", "DeleteDC", "N", "V" ); # process command line options Getopt::Mixed::getOptions qw( help h>help ?>help quiet q>quiet test t>test list l>list info i>info permanent p>permanent global g>global reset r>reset x=i y=i colordepth=s c>colordepth frequency=i f>frequency ); # beautify list for BPPs my %colors = ( 4 => "16 Colors", 8 => "256 Colors", 16 => "High Color", 24 => "True Color", 32 => "True Color", ); $opt_colordepth = color2bpp($opt_colordepth) if $opt_colordepth; # process non-options on command line $doing = "x"; while(@ARGV) { $argv = shift @ARGV; if($doing eq "x") { if($argv =~ /(\d+)x(\d+)/i) { $opt_x = $1; $opt_y = $2; $doing = "colordepth"; } else { $opt_x = $argv; $doing = "y"; } } elsif($doing eq "y") { $opt_y = $argv; $doing = "colordepth"; } elsif($doing eq "colordepth") { $opt_colordepth = color2bpp($argv); $doing = "frequency"; } elsif($doing eq "frequency") { $opt_frequency = $argv; $doing = "skip"; } } if($opt_test) { $flag = 0x02; } elsif($opt_permanent) { $flag = 0x01; } elsif($opt_global) { $flag = 0x08; } else { $flag = 0; } if($opt_help) { display_help(); exit(); } if($opt_reset) { $res = $ChangeDisplaySettings->Call( 0, 0 ); print "Default mode restored.\n" unless $opt_quiet; exit($res); } if($opt_info) { ($X, $Y, $BPP, $HZ) = getres(); printf "%dx%d %s (%d Bit) %dHz\n", $X, $Y, $colors{$BPP}, $BPP, $HZ unless $opt_quiet; exit(); } if($opt_list) { exit( list_modes($opt_x, $opt_y, $opt_colordepth, $opt_frequency) ); } if( not defined $opt_x and not defined $opt_y and not defined $opt_colordepth and not defined $opt_frequency ) { if(not $opt_quiet) { print "Nothing to do.\n"; print "Type $0 --help for more information.\n"; } exit(); } $res = chres($opt_x, $opt_y, $opt_colordepth, $opt_frequency, $flag); unless( $opt_quiet ) { if($res == 0) { if($opt_test) { print "Test successful.\n"; } else { print "Mode changed.\n"; } } if($res == 1) { print "The computer must be restarted.\n"; } if($res == -1) { print "The display driver failed the specified graphics mode.\n"; } if($res == -2) { print "The graphics mode is not supported.\n"; } if($res == -3) { print "Unable to write settings to the registry.\n"; } if($res == -4) { print "Invalid parameters.\n"; } if($res == -5) { print "Invalid parameters.\n"; } } exit($res); sub chres { my($wanted_X, $wanted_Y, $wanted_BPP, $wanted_HZ, $flags) = @_; $flags = 0 unless defined $flags; my($actual_X, $actual_Y, $actual_BPP, $actual_HZ) = @_; if(not defined $wanted_X or not defined $wanted_X or not defined $wanted_BPP or not defined $wanted_HZ) { ($actual_X, $actual_Y, $actual_BPP, $actual_HZ) = getres(); } my $wanted; $wanted = ((defined $wanted_X) ? $wanted_X : $actual_X); $wanted .= "," . ((defined $wanted_Y) ? $wanted_Y : $actual_Y); $wanted .= "," . ((defined $wanted_BPP) ? $wanted_BPP : $actual_BPP); $wanted .= "," . ((defined $wanted_HZ) ? $wanted_HZ : $actual_HZ); my $devmode = init_devmode(); my $newmode = undef; my $i = 0; my $res = $EnumDisplaySettings->Call( 0, $i, $devmode ); while( $res != 0) { ($BPP, $X, $Y, undef, $HZ) = unpack("x104 LLLLL", $devmode); $mode = "$X,$Y,$BPP,$HZ"; if($mode eq $wanted) { $newmode = $devmode; last; } $res = $EnumDisplaySettings->Call( 0, ++$i, $devmode ); } if(defined $newmode) { $res = $ChangeDisplaySettings->Call( $newmode, $flags ); } else { $res = -2; } return $res; } sub getres { my $hdc = $CreateDC->Call("DISPLAY", 0, 0, 0); if(!$hdc) { return undef; } my $HORZRES = 8; my $VERTRES = 10; my $BITSPIXEL = 12; my $VREFRESH = 116; my $X = $GetDeviceCaps->Call($hdc, $HORZRES); my $Y = $GetDeviceCaps->Call($hdc, $VERTRES); my $BPP = $GetDeviceCaps->Call($hdc, $BITSPIXEL); my $HZ = $GetDeviceCaps->Call($hdc, $VREFRESH); $DeleteDC->Call($hdc); return ($X, $Y, $BPP, $HZ); } sub list_modes { my($wanted_X, $wanted_Y, $wanted_BPP, $wanted_HZ) = @_; my $modes = 0; my $devmode = init_devmode(); my $i = 0; my $res = $EnumDisplaySettings->Call( 0, $i, $devmode ); while( $res != 0) { ($BPP, $X, $Y, undef, $HZ) = unpack("x104 LLLLL", $devmode); if( (not defined $wanted_X or $wanted_X == $X) and (not defined $wanted_Y or $wanted_Y == $Y) and (not defined $wanted_BPP or $wanted_BPP == $BPP) and (not defined $wanted_HZ or $wanted_HZ == $HZ) ) { printf "%dx%d %s (%d Bit) %dHz\n", $X, $Y, $colors{$BPP}, $BPP, $HZ unless $opt_quiet; $modes++; } $res = $EnumDisplaySettings->Call( 0, ++$i, $devmode ); } print "No matching graphics modes.\n" if not $opt_quiet and $modes == 0; return $modes; } sub init_devmode { return pack( "B" x 32 . "SSSSLsssssssssssss" . "B" x 32 . "SLLLLL", (0 x 32), # dmDeviceName 0, # dmSpecVersion 0, # dmDriverVersion 124, # dmSize 0, # dmDriverExtra 0, # dmFields 0, # dmOrientation 0, # dmPaperSize 0, # dmPaperLength 0, # dmPaperWidth 0, # dmScale 0, # dmCopies 0, # dmDefaultSource 0, # dmPrintQuality 0, # dmColor 0, # dmDuplex 0, # dmYResolution 0, # dmTTOption 0, # dmCollate (0 x 32), # dmFormName 0, # dmLogPixels 0, # dmBitsPerPel 0, # dmPelsWidth 0, # dmPelsHeight 0, # dmDisplayFlags 0, # dmDisplayFrequency ); } sub color2bpp { my($arg) = shift; $arg = lc $arg; my %table = ( 1 => 1, 2 => 2, 16 => 4, 256 => 8, 65000 => 16, '64k' => 16, '65k' => 16, high => 16, '16m' => 24, true => 32, ); if($arg =~ /^(\d+)b$/) { return $1; } elsif(exists $table{$arg}) { return $table{$arg}; } } sub display_help { print qq( $0 version $VERSION, (c) 2001 Aldo Calpini usage: $0 [OPTIONS] [NNNxNNN] [COLORS] [FREQ] OPTIONS: --help shows this help --x NNN width, in pixels --y NNN height, in pixels --colordepth COLORS color depth (see below) --frequency NNN vertical refresh rate, in Hz (only WinNT/2000) --quiet does not display information on STDOUT --reset reset the screen to the default mode --info isplay the current mode and exit --list list the available modes. can be combined with --x, --y, --colordepth and --frequency, for example: '--x 1024 --list' lists all the modes with width of 1024 pixels --test test the given mode without making changes --permanent make change permanent (for the current user) --global make change permanent and global (for all users) (only WinNT/2000) all options can be given in the short form too (eg. -t for --test, -h for --help, -x for --x and so on). COLORS: recognized values are: 1, 2, 1b > 2 colors (1 bpp) 16, 4b => 16 colors (4 bpp) 256, 8b => 256 colors (8 bpp) 65000, 64k, high, 16b => 65536 colors (16 bpp) 16m, true, 32b => 16 millions colors (32 bpp) the --x, --y, --colordepth and --frequency options can also be given on command line without introduction, but in this case order must be respected. for example, to change to a 800x600 screen resolution: $0 800x600 or $0 800 600 to change to 800x600, 32bpp, 85Hz: $0 800 600 32 85 ); } =head1 NAME chres - CHange RESolution =head1 DESCRIPTION Change the resolution of the screen on a Windows machine. Launch the script with the --help option for a detailed description of the usage. =head1 README Change the resolution of the screen on a Windows machine. =head1 PREREQUISITES This script requires C and C. =pod OSNAMES MSWin32 =pod SCRIPT CATEGORIES Win32 Win32/Utilities =head1 AUTHOR Aldo Calpini (dada@perl.it). =cut