#!/usr/bin/perl #- convert in.png -colors 128 -treedepth 6 -dither out.gif use strict; use lib '/usr/lib/libDrakX'; use Getopt::Long; use ugtk2 qw(:all); use Gtk2::Gdk::Keysyms; use MDK::Common; use Image::Magick; my $kernel_size = 1500; #- in KiB my $initrd_size = 130; #- in KiB my $lilo_block_size = 50; #- in KiB my $isolinux_block_size = 64; #- in KiB my @modes = ( { Vesa => 0x101, X => 640, Y => 480 }, { Vesa => 0x103, X => 800, Y => 600 }, { Vesa => 0x105, X => 1024, Y => 768 }, { Vesa => 0x107, X => 1280, Y => 1024 }, ); my (%image_size, $kernel_and_initrd_size); my ($progress_rect, $progress_color) = ([], {}); my ($timer_pos, $timer_bg, $timer_fg) = ({}, {}, {}); my ($entry_rect, $entry_selected_color, $entry_color) = ([], {}, {}); my $isolinux_mode; my $magick = Image::Magick->new; my ($current_rect, $current_point); my ($image_area, $image_pixbuf); sub move_point { my ($up_down, $direction) = @_; my $wanted = $current_point->{$direction} + $up_down; if (0 <= $wanted && $wanted < $image_size{$direction}) { $current_point->{$direction} += $up_down; } $image_area->queue_draw; } sub create_image_area() { $image_area = Gtk2::DrawingArea->new; $image_area->can_focus(1); $image_area->add_events('button-press-mask'); $image_area->signal_connect(button_press_event => \&image_button_pressed); $image_area->signal_connect(expose_event => \&image_expose); gtkmodify_font($image_area, 'Monospace 12'); my $keys = { $Gtk2::Gdk::Keysyms{Down} => sub { move_point( ($_[0] ? 5 : 1), 'Y') }, $Gtk2::Gdk::Keysyms{Up} => sub { move_point(-($_[0] ? 5 : 1), 'Y') }, $Gtk2::Gdk::Keysyms{Left} => sub { move_point(-($_[0] ? 5 : 1), 'X') }, $Gtk2::Gdk::Keysyms{Right} => sub { move_point( ($_[0] ? 5 : 1), 'X') }, $Gtk2::Gdk::Keysyms{q} => sub { Gtk2->main_quit }, $Gtk2::Gdk::Keysyms{Escape} => sub { Gtk2->main_quit }, }; $image_area->signal_connect(key_press_event => sub { my (undef, $event) = @_; if (my $f = $keys->{$event->keyval}) { $f->(member('control-mask', @{$event->state})); } 1; }); $image_area->grab_focus; $image_area->show; $image_area; } sub image_expose { my ($widget) = @_; my $window = $widget->window; $window->draw_pixbuf($widget->style->white_gc, $image_pixbuf, 0, 0, 0, 0, -1, -1, 'none', 0, 0); if (!$isolinux_mode) { { my $layout = $widget->create_pango_layout('--:--'); my ($width, $height) = $layout->get_pixel_size; $window->draw_rectangle(color_index2gc($window, $timer_bg), 1, $timer_pos->{X}, $timer_pos->{Y}, $width, $height); $window->draw_layout(color_index2gc($window, $timer_fg), $timer_pos->{X}, $timer_pos->{Y}, $layout); } my ($x, $y, $w, $h) = rectangle2xywh($entry_rect); my @std_labels = ('linux', 'failsafe', '2.6.3-7mdk', 'X' x ($w / 8)); for (my $nb = 0; $nb < int($h / 16); $y += 16, $nb++) { my $label = shift(@std_labels) || 'label_' . ($nb+1); my $gc = color_index2gc($window, !$nb ? $entry_selected_color : $entry_color); my $layout = $widget->create_pango_layout($label); $window->draw_layout($gc, $x, $y, $layout); } } { my ($x, $y, $w, $h) = rectangle2xywh($progress_rect); my $w2 = $w - $w % nb_steps(); if ($current_rect) { $window->draw_rectangle(color_index2gc($window, $progress_color), 1, $x, $y, $w2, $h); $window->draw_rectangle($widget->style->black_gc, 0, $x, $y, $w, $h); } else { $window->draw_rectangle(color_index2gc($window, $progress_color), 1, $x, $y, $w, $h); } } 0; } sub image_button_pressed { my (undef, $event) = @_; if ($event->button eq '3') { create_popup()->popup(undef, undef, undef, undef, '3', $event->time); return 1; } my $point = { X => $event->x, Y => $event->y }; my $chosen_point; if ($current_rect) { if (!@$current_rect) { my @corners = ({ X => 0, Y => 0 }, { X => 0, Y => $image_size{Y} - 1 }, { X => $image_size{X} - 1, Y => 0 }, { X => $image_size{X} - 1, Y => $image_size{Y} - 1 }); @$current_rect = ($corners[farthest($point, @corners)], nearest($point, @corners)); } $current_point = nearest($point, @$current_rect); } elsif ($current_point) { %$current_point or %$current_point = %$point; } else { return; } %$current_point = %$point; $current_point->{Color} = $magick->Get("index[$point->{X},$point->{Y}]"); $image_area->queue_draw; 1; } sub image_set_file { my ($file) = @_; $image_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($file); %image_size = (X => $image_pixbuf->get_width, Y => $image_pixbuf->get_height); $image_area->set_size_request($image_size{X}, $image_size{Y}); $image_area->queue_draw; } sub color_index2gc { my ($window, $color) = @_; my ($r, $g, $b) = split(',', $magick->Get("colormap[$color->{Color}]")); my $gc = Gtk2::Gdk::GC->new($window); my $gdk_color = Gtk2::Gdk::Color->new($r, $g, $b) or die "bad color for $r,$g,$b"; $window->get_colormap->rgb_find_color($gdk_color); $gc->set_rgb_fg_color($gdk_color); $gc; } sub rectangle2xywh { my ($rect) = @_; my $x = min($rect->[0]{X} , $rect->[1]{X}); my $y = min($rect->[0]{Y} , $rect->[1]{Y}); my $w = abs($rect->[0]{X} - $rect->[1]{X}); my $h = abs($rect->[0]{Y} - $rect->[1]{Y}); ($x, $y, $w, $h); } sub distance { my ($p1, $p2) = @_; sqr($p1->{X} - $p2->{X}) + sqr($p1->{Y} - $p2->{Y}); } sub farthest { my ($point, @others) = @_; my $i = 0; my $dist = 0; my $farthest; foreach (@others) { my $d = distance($point, $_); if ($d >= $dist) { $dist = $d; $farthest = $_; } } $farthest; } sub nearest { my ($point, @others) = @_; my $i = 0; my $dist; my $nearest; foreach (@others) { my $d = distance($point, $_); if (! defined $dist || $d < $dist) { $dist = $d; $nearest = $_; } } $nearest; } sub create_popup() { my %l = my @l = ( 'Progress bar position' => sub { $current_rect = $progress_rect }, 'Progress bar color' => sub { $current_point = $progress_color }, '' => sub {}, $isolinux_mode ? ( 'switch to lilo mode' => sub { $isolinux_mode = 0 }, ) : ( 'Timer position' => sub { $current_point = $timer_pos }, 'Timer text color' => sub { $current_point = $timer_fg }, 'Timer background' => sub { $current_point = $timer_bg }, '' => sub {}, 'Entry position' => sub { $current_rect = $entry_rect }, 'Entry text selected color' => sub { $current_point = $entry_selected_color }, 'Entry text color' => sub { $current_point = $entry_color }, '' => sub {}, 'switch to isolinux mode' => sub { $isolinux_mode = 1 }, ), ); my $popup = Gtk2::Menu->new; foreach (group_by2(@l)) { my ($descr, $f) = @$_; gtkappend($popup, gtksignal_connect(Gtk2::MenuItem->new_with_label($descr), activate => sub { $current_rect = $current_point = undef; $f->(); })); } $popup } sub nb_steps() { $kernel_and_initrd_size / ($isolinux_mode ? $isolinux_block_size : $lilo_block_size); } sub read_parameters { my ($file) = @_; my %h = getVarsFromSh($file); $entry_rect->[0]{X} = $h{'entry_x'}; $entry_rect->[0]{Y} = $h{'entry_y'}; $entry_rect->[1]{X} = $h{'entry_x'} + $h{'entry_w'}; $entry_rect->[1]{Y} = $h{'entry_y'} + $h{'entry_h'}; $entry_color->{Color} = $h{'entry_bg'} - 64; $entry_selected_color->{Color} = $h{'entry_fg'} - 64; $timer_pos->{X} = $h{'timer_x'}; $timer_pos->{Y} = $h{'timer_y'}; $timer_bg->{Color} = $h{'timer_bg'} - 64; $timer_fg->{Color} = $h{'timer_fg'} - 64; $progress_color->{Color} = $h{'progress_c'} - 64; $progress_rect->[0]{X} = $h{'progress_x'}; $progress_rect->[0]{Y} = $h{'progress_y'}; $progress_rect->[1]{X} = $h{'progress_x'} + $h{'progress_real_w'}; $progress_rect->[1]{Y} = $h{'progress_y'} + $h{'progress_h'}; $isolinux_mode = $h{'isolinux_mode'}; } sub save_parameters { my ($file) = @_; my %h; $h{'mode'} = (find { $image_size{X} eq $_->{X} } @modes)->{Vesa}; $h{'clear_h'} = $image_size{X}; $h{'clear_w'} = $image_size{Y}; if (!$isolinux_mode) { ($h{'entry_x'}, $h{'entry_y'}, $h{'entry_w'}, $h{'entry_h'}) = rectangle2xywh($entry_rect); $h{'entry_w_chr'} = int($h{'entry_w'} / 8); $h{'entry_h_chr'} = int($h{'entry_h'} / 16); $h{'entry_bg'} = $entry_color->{Color} + 64; $h{'entry_fg'} = $entry_selected_color->{Color} + 64; $h{'timer_x'} = $timer_pos->{X}; $h{'timer_y'} = $timer_pos->{Y}; $h{'timer_bg'} = $timer_bg->{Color} + 64; $h{'timer_fg'} = $timer_fg->{Color} + 64; } $h{'progress_c'} = $progress_color->{Color} + 64; ($h{'progress_x'}, $h{'progress_y'}, $h{'progress_real_w'}, $h{'progress_h'}) = rectangle2xywh($progress_rect); $h{'progress_w'} = int($h{'progress_real_w'} / nb_steps()); $h{'isolinux_mode'} = $isolinux_mode; output($file, map { "$_=$h{$_}\n" } sort keys %h); } # MAIN ######################################################################### my $usage = <<EOF; usage: $0 [--kernel <kernel> --initrd <initrd>] [--size <size in KiB>] <image> (kernel and initrd are used to compute the size of data to load) EOF GetOptions('kernel=s' => \ (my $kernel), 'initrd=s' => \ (my $initrd), 'size=s' => \$kernel_and_initrd_size, 'isolinux' => \ (my $force_isolinux_mode), ) or die $usage; my ($file) = @ARGV; @ARGV == 1 && -e $file or die $usage; if ($kernel_and_initrd_size) { $kernel and die "give kernel and initrd or size, not both\n"; } else { if ($kernel) { $initrd or die "give both kernel and initrd\n"; $kernel_size = (-s $kernel) / 1024 or die "bad file $kernel: $!\n"; $initrd_size = (-s $initrd) / 1024 or die "bad file $initrd: $!\n"; } $kernel_and_initrd_size = $kernel_size + $initrd_size; } my $err; $err = $magick->Read($file) and die $err; my $bmp_file_name = $file; $bmp_file_name =~ s/\.\w\w\w?$//; $bmp_file_name .= '.bmp'; if ($file ne $bmp_file_name || $magick->Get('colors') > 128) { warn "writing $bmp_file_name\n"; $err = $magick->Quantize(colors => 128, dither => 'True', treedepth => 6) and die $err; $err = $magick->Write(filename => "bmp3:$bmp_file_name", compression => 'None') and die $err; } read_parameters("$bmp_file_name.parameters"); $isolinux_mode ||= $force_isolinux_mode; my $window_widget = Gtk2::Window->new('toplevel'); $window_widget->signal_connect(destroy => sub { Gtk2->main_quit }); gtkadd($window_widget, gtkpack(Gtk2::VBox->new(0,0), create_image_area())); image_set_file($bmp_file_name); $window_widget->show; $image_area->window->set_cursor(Gtk2::Gdk::Cursor->new('crosshair')); Gtk2->main; save_parameters("$bmp_file_name.parameters");