#!/usr/bin/perl use strict; use lib qw(/usr/lib/libDrakX); use standalone; use common; use ugtk2 qw(:helpers :wrappers :create); use interactive; #- convenience variables for true and false my $true = 1; my $in = 'interactive'->vnew('su'); my $window = ugtk2->new('DrakSplash'); $window->{rwindow}->signal_connect(delete_event => \&CloseAppWindow); #- verification of package image magik unless ($in->do_pkgs->is_installed('ImageMagick')) { $in->ask_okcancel(N("Error"), N("package 'ImageMagick' is required to be able to complete configuration.\nClick \"Ok\" to install 'ImageMagick' or \"Cancel\" to quit")) && $in->do_pkgs->install('ImageMagick') or CloseAppWindow(); } #- application vars my $tmp_path = '/tmp/draksplash/'; ! -d $tmp_path and mkdir($tmp_path); my $thm_path = '/usr/share/bootsplash/themes/'; my $boot_conf_path = '/etc/bootsplash/themes/'; my $cfg_path = "/cfg/"; my $img_file; my $prev_window; my %font_size = ('h' =>16, 'w' =>8); my %theme = ('name' => 'new_theme', 'res' => { 'res' => '800x600', 'h' => '600', 'w' => '800', }, 'boot_conf' => { 'tx' => 0, 'ty' => 0, 'tw' => 0, 'th' => 0, 'px' => 0, 'py' => 0, 'pw' => 0, 'ph' => 0, 'pc' => '0x21459d', }, 'boot_img' => '' ); my %scale_size = ('tx' => ($theme{res}{w} / $font_size{w}), 'ty' => ($theme{res}{h} / $font_size{h}), 'tw' => ($theme{res}{w} / $font_size{w}), 'th' => ($theme{res}{h} / $font_size{h}), 'px' => $theme{res}{w}, 'py' => $theme{res}{h}, 'pw' => $theme{res}{w}, 'ph' => $theme{res}{h}, ); my %first = ('frame' => Gtk2::Frame->new(N("first step creation")), 'widget' => { 'label' => { 'res' => N("final resolution"), 'file' => N("choose image file"), 'name' => N("Theme name") }, 'button' => { #'boot_conf' => N("Make bootsplash step 2"), #'lilo_conf' => N("Go to lilosplash configuration"), 'file' => N("Browse"), }, 'combo' => { 'res' => ['800x600', '1024x768', '1280x1024'], 'name' => [ $theme{name}, giv_exist_thm() ] }, extras => { res => { noneditable => 1, }, }, }, 'pos' => [ 'name', 'res', 'file', 'boot_conf', #'save', #'kill' ], ); my %boot_conf_frame = ('frame' => Gtk2::Frame->new(N("Configure bootsplash picture")), 'widget' => { 'label' => { 'tx' => N("x coordinate of text box\nin number of characters"), 'ty' => N("y coordinate of text box\nin number of characters"), 'tw' => N("text width"), 'th' => N("text box height"), 'px' => N("the progress bar x coordinate\nof its upper left corner"), 'py' => N("the progress bar y coordinate\nof its upper left corner"), 'pw' => N("the width of the progress bar"), 'ph' => N("the height of the progress bar"), 'pc' => N("the color of the progress bar") }, #- must set scale values to true to get them created by mk_frame 'scale' => { 'tx' => 1, 'ty' => 1, 'tw' => 1, 'th' => 1, 'px' => 1, 'py' => 1, 'pw' => 1, 'ph' => 1, }, 'button' => { #'annul' => N("Go back"), 'prev' => N("Preview"), 'kill' => N("Quit"), 'save' => N("Save theme"), 'pc' => N("Choose color"), }, 'check' => { 'logo' => N("Display logo on Console"), 'quiet' => N("Make kernel message quiet by default"), }, }, 'pos' => [ 'tx 1', 'ty 1', 'tw 1', 'th 1', 'px 1', 'py 1', 'pw 1', 'ph 1', 'pc', 'logo', 'quiet', 'annul', 'prev', 'save', 'kill', ], ); #- var action is used to hide/show the correct frame $first{frame}->add(mk_frame(\%first)); my $first_vbox = Gtk2::VBox->new(0, 5); #****************************- Signal event actions #- change resolution $first{widgets}{combo}{res}->entry->signal_connect(changed => sub { $theme{res}{res} = $first{widgets}{combo}{res}->entry->get_text; ($theme{res}{w}, $theme{res}{h}) = $theme{res}{res} =~ /([^x]+)x([^x]+)/; &set_scale_size; $boot_conf_frame{frame}->destroy; $boot_conf_frame{frame} = Gtk2::Frame->new(N("Configure bootsplash picture")); &make_boot_frame; $first_vbox->add($boot_conf_frame{frame}); member($theme{name}, giv_exist_thm()) && thm_in_this_res() && get_this_thm_res_conf() || $in->ask_warn(N("Notice"), N("This theme does not yet have a bootsplash in %s !", $theme{res}{res})); }); #- go to bootsplash configuration step 2 #$first{widgets}{button}{boot_conf}->signal_connect(clicked => sub{show_act(\%boot_conf_frame) } ); #- image file selection for new theme $first{widgets}{button}{file}->signal_connect(clicked => sub { my $file_dialog = gtkset_modal(Gtk2::FileSelection->new(N("choose image")), 1); $file_dialog->set_transient_for($window->{rwindow}); $file_dialog->set_filename($img_file || '~/'); $file_dialog->cancel_button->signal_connect(clicked => sub { $file_dialog->destroy }); $file_dialog->ok_button->signal_connect(clicked => sub { $img_file = $file_dialog->get_filename; $file_dialog->destroy }); $file_dialog->show; }); #- changing theme name $first{widgets}{combo}{name}->entry->signal_connect(changed => sub { get_this_thm_res_conf(); $theme{name} = $first{widgets}{combo}{name}->entry->get_text }); #************************************************** $first_vbox->add($first{frame}); $first_vbox->add($boot_conf_frame{frame}); &make_boot_frame; # set window attributes and show it unless ($::isEmbedded) { $window->{rwindow}->set_border_width(5); $window->{window}->add($first_vbox); $window->{rwindow}->set_position('center'); $window->{rwindow}->show_all; #&show_act(\%first); } # Gtk event loop $window->main; # Should never get here ugtk2->exit(0); ### Callback function to close the window sub CloseAppWindow() { ugtk2->exit(0); } #- ====## used funtions ##===== #- Desc => write config file for boot theme and copy image in the right location sub write_boot_thm { my $_w = $in->wait_message('', N("saving Bootsplash theme...")); &set_thm_values; my $logo = $boot_conf_frame{widgets}{check}{logo}->get_active ? 'yes' : 'no'; my $quiet = $boot_conf_frame{widgets}{check}{quiet}->get_active ? 'yes' : 'no'; my $globalconf_file = $boot_conf_path.$theme{name}.'/global.config'; my $cfg_file = $boot_conf_path . $theme{name} . "$cfg_path/bootsplash-" . $theme{res}{res} . '.cfg'; #- verify all dir exists or create them foreach my $dir ($boot_conf_path . $theme{name} . $cfg_path, $thm_path.$theme{name} . '/images/') { mkdir_p($dir) if !-d $dir; } #- copy image to dest by convert system('convert -scale '.$theme{res}{res} . ' ' . $img_file . ' ' . $thm_path.$theme{name} . '/images/bootsplash-' . $theme{res}{res} . '.jpg'); system('/usr/share/bootsplash/scripts/rewritejpeg '.$thm_path.$theme{name}.'/images/bootsplash-'.$theme{res}{res}.'.jpg'); #- write conf files my $cfg_cont = '# This is the configuration file for the '.$theme{res}{res}.' bootsplash picture # this file is necessary to specify the coordinates of the text box on the # splash screen. # tx is the x coordinate of the text window in characters. default is 24 # multiply width font width for coordinate in pixels. tx='.$theme{boot_conf}{tx}.' # ty is the y coordinate of the text window in characters. default is 14 ty='.$theme{boot_conf}{ty}.' # tw is the width of the text window in characters. default is 130 # note: this should at least be 80 as on the standard linux text console tw='.$theme{boot_conf}{tw}.' # th is the height of the text window in characters. default is 44 # NOTE: this should at least be 25 as on the standard linux text console th='.$theme{boot_conf}{th}.' # px is the progress bar x coordinate of its upper left corner px='.$theme{boot_conf}{px}.' # py is the progress bar y coordinate of its upper left corner py='.$theme{boot_conf}{py}.' # pw is the with of the progress bar pw='.$theme{boot_conf}{pw}.' # ph is the height of the progress bar ph='.$theme{boot_conf}{ph}.' # pc is the color of the progress bar pc='.$theme{boot_conf}{pc}.''; my $globalconf_cont = '# Display logo on console. LOGO_CONSOLE='.$logo.' # Make kernel message quiet by default. QUIET='.$quiet; output($globalconf_file, $globalconf_cont); output($cfg_file, $cfg_cont); } #- Desc => read the current bootsplash theme configuration if exist sub get_this_thm_res_conf() { member($first{widgets}{combo}{name}->entry->get_text, giv_exist_thm()) and $theme{name} = $first{widgets}{combo}{name}->entry->get_text and thm_in_this_res(1) and read_boot_conf(); -f $thm_path.$theme{name}."/images/bootsplash-".$theme{res}{res}.".jpg" and $img_file = $thm_path . $theme{name} . "/images/bootsplash-" . $theme{res}{res} . ".jpg"; return 1; } sub read_boot_conf { chdir($boot_conf_path); my $line; if (-f $theme{name} . "/$cfg_path/bootsplash-" . $theme{res}{res} . '.cfg') { local *CFG; open CFG, $theme{name} . "/$cfg_path/bootsplash-" . $theme{res}{res} . '.cfg'; while ($line = <CFG>) { $line =~ m/^([a-z][a-z])=([^\n]+)/ and $theme{boot_conf}{$1} = $2; } close CFG; &set_scale_values; } else { return 0; } } my %adj; sub set_scale_values() { foreach (keys %{$theme{boot_conf}}) { $adj{$_} and $adj{$_}->set_value($theme{boot_conf}{$_}); } } #- Desc => check if this theme is available in the current resolution else #- change the current resolution or display a ask_warn box #- Args => � #- return=> (bool) sub thm_in_this_res { my ($check_res) = @_; (-f $thm_path.$theme{name}."/images/bootsplash-".$theme{res}{res}.".jpg") ? return 1 : $check_res == 1 ? return which_res_exist() : return 0; } sub which_res_exist() { chdir($thm_path.$theme{name}."/images/"); my $is_ok = 0; foreach (@{$first{widget}{combo}{res}}) { next if !-f "bootsplash-$_.jpg"; $is_ok = 1; $first{widgets}{combo}{res}->entry->set_text($_); last; } $is_ok == 1 or $in->ask_warn(N("Notice"), N("This theme does not yet have a bootsplash in %s !", $theme{res}{res})) and return 0; return 1; } #- Desc => retrieve all installed theme #- Args => � #- Return=> @arr of available theme sub giv_exist_thm() { chdir($thm_path); my @thms_dirs; foreach (glob("*")) { -d $_ && m/^[^.]/ and push @thms_dirs, $_; } return @thms_dirs; } #- Desc => show only the right frame #- Args => action(str) #- Return=> (bool) sub show_act { # my ($action) = @_; # foreach (@action_frame){ # if($_ == $action){ # $_->{frame}->show_all ; # }else{ # $_->{frame}->hide; # } # } } #- Desc => just add tooltips #- Args => name of widget(str) and frame to work on it (\%hash) sub tool_tip { my ($name, $ref) = @_; foreach (keys %{$ref->{widget}}) { $_ eq 'tooltip' and next; if ($ref->{widget}{$_}{$name}) { ! $adj{$name.'_tip'} and $adj{$name.'_tip'} = Gtk2::Tooltips->new; $adj{$name.'_tip'}->set_tip($ref->{widgets}{$_}{$name}, $ref->{widget}{tooltip}{$name}, ''); } } } #- Desc => just prepare widgets for a fram hash #- Args => $box(a Vbox widget to contain all widgets), \%frame (hash with complete definition of the frame) #- Return=> all hash{widgets} are created and packed in $box sub mk_frame { my ($ref) = @_; my @buttons; my $u = create_packtable({ col_spacings => 10, row_spacings => 5 }, map { my @widgets; #- look for label if ($ref->{widget}{label}{$1}) { my $w = $ref->{widgets}{label}{$1} = Gtk2::Label->new($ref->{widget}{label}{$1}); push @widgets, $w; } #- look for scale if ($ref->{widget}{scale}{$1}) { my $w = $ref->{widgets}{scale}{$1} = Gtk2::HScale->new($adj{$1} = Gtk2::Adjustment->new(0, 0, $scale_size{$1}, 1, 10, 0)); $ref->{widgets}{scale}{$1}->set_digits(0); push @widgets, $w; } $adj{$1} and $adj{$1}->set_value($theme{boot_conf}{$1}); #- look for combo my @popdown; if ($ref->{widget}{combo}{$1}) { @popdown = @{$ref->{widget}{combo}{$1}}; my $w = $ref->{widgets}{combo}{$1} = $ref->{widget}{extras}{$1}{noneditable} ? Gtk2::OptionMenu->new : Gtk2::Combo->new; $ref->{widgets}{combo}{$1}->set_popdown_strings(@popdown); push @widgets, $w; } #- look for checkbox if ($ref->{widget}{check}{$1}) { my $w = $ref->{widgets}{check}{$1} = Gtk2::CheckButton->new($ref->{widget}{check}{$1}); $ref->{widgets}{check}{$1}->set_active(1); push @widgets, $w; } #- look for button if ($ref->{widget}{button}{$1}) { my $w = $ref->{widgets}{button}{$1} = Gtk2::Button->new($ref->{widget}{button}{$1}); @widgets ? push @widgets, $w : push @buttons, $w; } #- look for tooltips $ref->{widget}{tooltip}{$1} and tool_tip($1, \%$ref); if_(@widgets, \@widgets); } @{$ref->{pos}} ); gtkpack__(Gtk2::VBox->new, gtkset_border_width($u, 3), @buttons); } #- Desc => take a decimal value between 0 to 255 and return the corresponding hexadecimal value sub dec2hex { my ($dec) = @_; my @dec_hex = (0..9, 'A', 'B', 'C', 'D', 'E', 'F'); my $int; my $float; $dec = $dec/16; $int = int($dec); $float = $dec_hex[int(($dec-$int)*16)]; $int = $dec_hex[$int]; return "$int$float"; } #- Desc => prepare and set all signal_connect for boot_frame widget sub make_boot_frame() { $boot_conf_frame{frame}->add(mk_frame(\%boot_conf_frame)); #- open a color choose box $boot_conf_frame{widgets}{button}{pc}->signal_connect(clicked => sub { my $color = gtkshow(Gtk2::ColorSelectionDialog->new(N("ProgressBar color selection"))); my @rgb = map { hex($_)/255 } ($1, $2, $3) if $theme{boot_conf}{pc} =~ m/0x(.{2})(.{2})(.{2})/; $color->colorsel->set_current_color(gtkcolor(@rgb)); $color->cancel_button->signal_connect(clicked => sub { $color->destroy }); $color->ok_button->signal_connect(clicked => sub { my $colour = $color->colorsel->get_current_color; @rgb = map { dec2hex($_*255) } ($colour->red, $colour->green, $colour->blue); $theme{boot_conf}{pc} = "0x$rgb[0]$rgb[1]$rgb[2]"; $color->destroy; }); }); #- quit button $boot_conf_frame{widgets}{button}{kill}->signal_connect(clicked => \&CloseAppWindow); $boot_conf_frame{widgets}{button}{save}->signal_connect(clicked => \&write_boot_thm); #- return to first screen #$boot_conf_frame{widgets}{button}{annul}->signal_connect(clicked => sub { show_act( \%first ) } ); #- made a preview $boot_conf_frame{widgets}{button}{prev}->signal_connect(clicked => sub { unless (-f $img_file) { $in->ask_warn(N("Notice"), N("You must choose an image file first!")); return 0; } #- calculation of the 2 angle of text box and progress bar set_thm_values(); my $_w = $in->wait_message('', N("Generating preview ...")); my $txt_tl_x = $theme{boot_conf}{tx}*$font_size{w}; my $txt_tl_y = $theme{boot_conf}{ty}*$font_size{h}; my $txt_width = $theme{boot_conf}{tw}*$font_size{w}; my $txt_height = $theme{boot_conf}{th}*$font_size{h}; my $prog_tl_x = $theme{boot_conf}{px}; my $prog_tl_y = $theme{boot_conf}{py}; my $prog_width = $theme{boot_conf}{pw}; my $prog_height = $theme{boot_conf}{ph}; show_prev($txt_tl_x, $txt_tl_y, $txt_width, $txt_height, $prog_tl_x, $prog_tl_y, $prog_width, $prog_height); }); $boot_conf_frame{frame}->show_all; # - check scales values are possibly correct #&set_scale_values; foreach my $k (keys %{$theme{boot_conf}}) { $k =~ m/[tp][hwyx]/ and $adj{$k}->signal_connect(value_changed => \&check_boot_scales); } } #- Desc => set theme values from user entry (scales widgets) sub set_thm_values() { foreach (keys %{$theme{boot_conf}}) { m/[tp][hwyx]/ and $theme{boot_conf}{$_} = int($adj{$_}->get_value); } } my ($prev_pic, $prev_canvas); #- Desc => destroy properly all widget of preview window sub kill_preview() { $prev_window->destroy; undef($prev_window); $prev_canvas->destroy; undef($prev_canvas); undef($prev_pic); } #- Desc => create a new window with a preview of splash screen #- Args => $img_file (str) full path to preview file sub show_prev { my ($txt_tl_x, $txt_tl_y, $txt_width, $txt_height, $prog_tl_x, $prog_tl_y, $prog_width, $prog_height) = @_; $prev_window or $prev_window = Gtk2::Window->new('toplevel'); #-PO First %s is theme name, second %s (in parenthesis) is resolution $prev_window->set_title(N("%s BootSplash (%s) preview", $theme{name}, $theme{res}{res})); $prev_pic = gtkcreate_pixbuf($img_file); $prev_pic->scale_simple($theme{res}{w}, $theme{res}{h}, 'hyper'); $prev_canvas && $prev_canvas->isa('Gtk2::Widget') or $prev_canvas = Gtk2::DrawingArea->new and $prev_window->add($prev_canvas); $prev_canvas->set_size_request($theme{res}{w}, $theme{res}{h}); $prev_canvas->signal_connect(expose_event => sub { my ($w, $event) = @_; my ($x, $y, $width, $height) = $event->area->values; $prev_pic->render_to_drawable($w->window, $w->style->fg_gc('normal'), $x, $y, $x, $y, $width, $height, 'normal', 0, 0); $prev_canvas->window->draw_rectangle($prev_canvas->style->black_gc, $true, $txt_tl_x, $txt_tl_y, $txt_width, $txt_height); $prev_canvas->window->draw_rectangle($prev_canvas->style->black_gc, $true, $prog_tl_x, $prog_tl_y, $prog_width, $prog_height); }); $prev_window->signal_connect(delete_event => \&kill_preview); $prev_window->show_all; } #- Desc => define the max size of boot's scales sub set_scale_size() { %scale_size = ('tx' => ($theme{res}{w} / $font_size{w}), 'ty' => ($theme{res}{h} / $font_size{h}), 'tw' => ($theme{res}{w} / $font_size{w}), 'th' => ($theme{res}{h} / $font_size{h}), 'px' => $theme{res}{w}, 'py' => $theme{res}{h}, 'pw' => $theme{res}{w}, 'ph' => $theme{res}{h}, ); } #- Desc => verify that boot's scales widgets are correctly set #- Args => $obj (str) is the scale to check value sub check_boot_scales { my ($obj) = @_; my $tw = $adj{tw}->get_value; my $tx = $adj{tx}->get_value; my $th = $adj{th}->get_value; my $ty = $adj{ty}->get_value; my $pw = $adj{pw}->get_value; my $ph = $adj{ph}->get_value; my $px = $adj{px}->get_value; my $py = $adj{py}->get_value; my $max_x = $scale_size{tw}; my $max_y = $scale_size{th}; my $max_xres = $theme{res}{w}; my $max_yres = $theme{res}{h}; $obj eq 'tw' and $max_x < $tw + $tx and $adj{tx}->set_value($max_x - $tw); $obj eq 'tx' and $max_x < $tw + $tx and $adj{tw}->set_value($max_x - $tx); $obj eq 'th' and $max_y < $th + $ty and $adj{ty}->set_value($max_y - $th); $obj eq 'ty' and $max_y < $th + $ty and $adj{th}->set_value($max_y - $ty); $obj eq 'pw' and $max_xres < $pw + $px and $adj{px}->set_value($max_xres - $pw); $obj eq 'px' and $max_xres < $pw + $px and $adj{pw}->set_value($max_xres - $px); $obj eq 'ph' and $max_yres < $ph + $py and $adj{py}->set_value($max_yres - $ph); $obj eq 'py' and $max_yres < $ph + $py and $adj{ph}->set_value($max_yres - $py); }