#!/usr/bin/perl use strict; use lib qw(/usr/lib/libDrakX); use POSIX; use common; # i18n : IMPORTANT to get correct namespace (drakconf instead of libDrakX) BEGIN { unshift @::textdomains, 'drakconf' } use mygtk3 qw(gtknew); use ugtk3 qw(:all); use interactive; use standalone; use timezone; my $in = interactive->vnew('su'); my $cairo; my ($midx, $midy, $radius); my ($dRadians_hour, $dRadians_min, $dRadians_sec); my $Radian; my $timer; my $first = 1; my $its_reset = 0; $ugtk3::wm_icon = "/usr/share/mcc/themes/default/time-mdk.png"; my $ntpd = $timezone::ntp . 'd'; my $ntpfile = '/etc/' . $timezone::ntp . '.conf'; my $my_win = ugtk3->new(N("Date, Clock & Time Zone Settings")); $my_win->{window}->signal_connect(delete_event => sub { ugtk3->exit(0) }); my $calendar = gtknew('Calendar'); $calendar->signal_connect($_ => \&cal_changed) foreach 'month-changed', 'day-selected', 'day-selected-double-click', 'prev-month', 'next-month', 'prev-year', 'next-year'; my $timezone = timezone::read(); my $label_timezone = gtknew('Label', text => $timezone->{timezone} || N("not defined")); my $button_time = gtknew('Button', text => N("Change Time Zone")); my @timezones = eval { timezone::getTimeZones() }; my $err = $@; $button_time->signal_connect(clicked => sub { local $::isEmbedded = 0; # to prevent sub window embedding if (my $tz = $in->ask_from_treelist(N("Timezone - DrakClock"), N("Which is your timezone?"), '/', \@timezones, $timezone->{timezone})) { $timezone->{timezone} = $tz; $timezone->{UTC} = $in->ask_yesorno(N("GMT - DrakClock"), N("Is your hardware clock set to GMT?"), $timezone->{UTC}); timezone::write($timezone); $label_timezone->set_text($timezone->{timezone}); } }); if (!@timezones) { warn "Failed to retrieve timezone list: $err\n"; $button_time->set_sensitive(0); } my $drawing_area; my $adjh = Gtk3::Adjustment->new(0.0, 0.0, 23.0, 1.0, 5.0, 0.0); my $adjm = Gtk3::Adjustment->new(0.0, 0.0, 59.0, 1.0, 5.0, 0.0); my $adjs = Gtk3::Adjustment->new(0.0, 0.0, 59.0, 1.0, 5.0, 0.0); my ($button_reset, $check_ntp, $hb_ntp, $combo_ntpserver, $ntp); my $mode = 0; my (undef, undef, $h_old, $old_day, $old_month, $old_year) = localtime(time()); my @image_size = (200, 200); $my_win->{window}->add( gtknew('VBox', border_width => $::isEmbedded ? 0 : 5, children => [ 1, gtknew('HBox', children => [ 1, gtknew('VBox', children => [ 0, $calendar, 1, gtknew('Frame', text => N("Network Time Protocol"), shadow_type => 'etched_in', child => gtknew('VBox', border_width => 5, children => [ 0, gtknew('Label', text => N("Your computer can synchronize its clock\n with a remote time server using NTP")), 0, gtksignal_connect(gtkset_active($check_ntp = gtknew('CheckButton', text => N("Enable Network Time Protocol")), $mode), clicked => sub { $mode = !$mode; $hb_ntp->set_sensitive($mode); if ($mode == 1 && !$in->do_pkgs->is_installed('ntp') && !$in->do_pkgs->is_installed('chrony')) { install_ntp(); } }), 0, $hb_ntp = gtknew('HBox', border_width => 5, children => [ 0, gtknew('Label', text => N("Server:")), 1, $combo_ntpserver = Gtk3::ComboBoxText->new ]) ])) ]), 0, gtknew('VBox', children => [ 0, my $clock = gtknew('HBox', children => [ 0, $drawing_area = gtkset_size_request(Gtk3::DrawingArea->new, @image_size), ]), 0, my $time_box = gtknew('HBox', homogenous => 1, children => [ 0, my $spinner_h = Gtk3::SpinButton->new($adjh, 0, 0), 0, my $spinner_m = Gtk3::SpinButton->new($adjm, 0, 0), 0, my $spinner_s = Gtk3::SpinButton->new($adjs, 0, 0), ]), 1, gtknew('Frame', text => N("Timezone"), shadow_type => 'etched_in', child => gtknew('VBox', border_width => 5, children_tight => [ $label_timezone, $button_time ])), ]), ]), 0, create_okcancel( my $w = { cancel_clicked => sub { ugtk3->exit(0) }, ok_clicked => sub { my $need_date = 1; any::disable_x_screensaver(); if ($check_ntp->get_active) { my $choosed_serv = $combo_ntpserver->entry->get_text; $choosed_serv =~ s/^[^:]+: (.+)/$1/; if (!$choosed_serv) { err_dialog(N("Error"), N("Please enter a valid NTP server address.")); return; } timezone::set_ntp_server($choosed_serv); system('systemctl', 'enable', "$ntpd.service"); system('systemctl', 'stop', "$ntpd.service"); #verify that we have a valid hostname (thx sam) $choosed_serv =~ s/[^-a-zA-Z0-9.]//g; my $sync_ok; if ($ntpd eq "chronyd") { system('systemctl', 'start', "$ntpd.service"); # Wait up to 30s for sync $sync_ok = !system('chronyc', 'waitsync', '30', '0.1'); system('systemctl', 'stop', "$ntpd.service") if !$sync_ok; } else { $sync_ok = !system('/usr/sbin/ntpdate', $choosed_serv); system('systemctl', 'start', "$ntpd.service") if $sync_ok; } if ($sync_ok) { update_time(); #- get the new time before updating the hwclock $need_date = 0; } else { $in->ask_from_no_check({ title => N("Error"), messages => N("Could not synchronize with %s.", $choosed_serv), ok => N("Quit"), cancel => N("Retry"), }, []) or return; } } else { if (is_ntp_daemon_running()) { system('systemctl', 'stop', "$ntpd.service"); system('systemctl', 'disable', "$ntpd.service"); } } if ($need_date) { my ($year, $month, $day) = $calendar->get_date; $month++; my ($hour, $min, $sec) = ($adjh->get_value, $adjm->get_value, $adjs->get_value); system("date " . join('', map { print_it0($_) } ($month, $day, $hour, $min, $year)) . '.' . print_it0($sec)); } -e '/sbin/hwclock' and system('/sbin/hwclock', '--systohc'); any::enable_x_screensaver(); system(qw(dcop kicker Panel restart)) if $ENV{DESKTOP} eq 'kde'; ugtk3->exit(0); }, }, undef, undef, '', [ N("Reset"), sub { $its_reset = 1; $timer = Glib::Timeout->add(120, \&update_time); Repaint(1); $button_reset->set_sensitive(0); $its_reset = 0; } ] ), ]) ); $button_reset = $w->{buttons}{N("Reset")}; $time_box->set_direction('ltr'); my $servers = get_server(); $combo_ntpserver->set_popdown_strings(@$servers); if (-e $ntpfile && is_ntp_daemon_running()) { $ntp = timezone::ntp_server(); $ntp and ntp_widget_state(1); my $fullntp = $ntp; my $short_ntp = $ntp; #- strip digits from \d+.foo.pool.ntp.org $short_ntp =~ s/^\d+\.//; foreach (@$servers) { /^[^:]+: \Q$short_ntp\E$/ and $fullntp = $_, last; } $combo_ntpserver->entry->set_text($fullntp); } else { ntp_widget_state(0) } my $pressed; $drawing_area->set_events(${ Gtk3::Gdk::EventMask->new([ 'button_press_mask', 'button_release_mask', "pointer_motion_mask" ]) }); $drawing_area->signal_connect(draw => \&draw); $drawing_area->signal_connect(button_press_event => sub { $pressed = 1 }); $drawing_area->signal_connect(button_release_event => sub { $first = 1; $pressed = 0 }); $drawing_area->signal_connect(motion_notify_event => \&motion_event); $spinner_h->set_wrap(1); $spinner_h->signal_connect(activate => \&spinned); $spinner_h->signal_connect(button_release_event => \&spinned); $spinner_h->signal_connect(scroll_event => \&spinned); $spinner_h->signal_connect(changed => \&changed); $spinner_m->set_wrap(1); $spinner_m->signal_connect(activate => \&spinned); $spinner_m->signal_connect(scroll_event => \&spinned); $spinner_m->signal_connect(button_release_event => \&spinned); $spinner_s->set_wrap(1); $spinner_s->signal_connect(activate => \&spinned); $spinner_s->signal_connect(scroll_event => \&spinned); $spinner_s->signal_connect(button_release_event => \&spinned); gtkflush(); my $is24 = $h_old > 12; $old_year += 1900; $calendar->select_month($old_month, $old_year); $calendar->select_day($old_day); $button_reset->set_sensitive(0); $timer = Glib::Timeout->add(120, \&update_time); $drawing_area->show; $my_win->{window}->show_all; $my_win->main; ugtk3->exit(0); sub ntp_widget_state { my ($state) = @_; $check_ntp->set_active($state); $hb_ntp->set_sensitive($state); $mode = $state; } sub install_ntp() { $my_win->{window}->set_sensitive(0); $in->do_pkgs->ensure_is_installed('chrony') or ntp_widget_state(0); $my_win->{window}->set_sensitive(1); } sub is_ntp_daemon_running() { !system("systemctl is-active $ntpd.service >/dev/null"); } sub get_server() { my $servs = timezone::ntp_servers(); [ map { "$servs->{$_}: $_" } sort { $servs->{$a} cmp $servs->{$b} || $a cmp $b } keys %$servs ]; } sub update_time() { my (undef, undef, undef, $mday, $mon, $year) = localtime(time()); $year += 1900; my $old_its_reset = $its_reset; $its_reset = 1; $calendar->select_day($mday); $calendar->select_month($mon, $year); $its_reset = $old_its_reset; Repaint(1); } sub cal_changed() { if (!$its_reset and $timer) { Glib::Source->remove($timer); undef $timer; } $button_reset->set_sensitive(1); } sub changed() { my $val = $adjh->get_value; my $limit = ($is24 ? 18 : 6); if (($limit > $val && $h_old > $limit && $h_old < ($is24 ? 24 : 12)) || ($limit < $val && $h_old < $limit && $val-$h_old != 12)) { $is24 = !$is24; } $h_old = $val; } sub spinned() { Glib::Source->remove($timer) if $timer; undef $timer; $button_reset->set_sensitive(1); time_to_rad($adjs->get_value, $adjm->get_value, $adjh->get_value); Repaint(); 0; } sub motion_event { my ($widget, $event) = @_; $pressed or return; if ($first) { Glib::Source->remove($timer) if $timer; undef $timer; $Radian = determine_radian($event->x, $event->y); $button_reset->set_sensitive(1); } $$Radian = -atan2($event->x - $midx, $event->y - $midy) + $PI; Repaint(); rad_to_time(); $first = 0; } sub determine_radian { my ($x, $y) = @_; my $res; my $r; foreach (\$dRadians_hour, \$dRadians_min, \$dRadians_sec) { my $d = sqrt(($x - ($midx + 7/10 * $radius * sin($$_)))**2 + ($y - ($midy - 7/10 * $radius * cos($$_)))**2); $res or $res = $d, $r = $_; $d < $res and $res = $d, $r = $_; } $r; } sub draw { my ($widget, $event) = @_; $cairo = $event; Repaint(undef, 1); 0; } sub rad_to_time() { $adjh->set_value(POSIX::floor($dRadians_hour * 6 / $PI) + ($is24 ? 12 : 0)); $adjm->set_value(POSIX::floor($dRadians_min*30/$PI)); $adjs->set_value(POSIX::floor($dRadians_sec*30/$PI)); } sub time_to_rad { my ($sec, $min, $hour) = @_; $dRadians_hour = $hour % 12 * $PI / 6; $dRadians_min = $min * $PI / 30; $dRadians_sec = $sec * $PI / 30; $adjh->set_value($hour); $adjm->set_value($min); $adjs->set_value($sec); } sub Repaint { my ($o_update_time, $skip_redraw) = @_; my ($sec, $min, $hour) = localtime(time()); time_to_rad($sec, $min, $hour) if $o_update_time; ($midx, $midy) = ($drawing_area->get_allocated_width/2, $drawing_area->get_allocated_height/2); $radius = ($midx < $midy ? $midx : $midy) - 10; $drawing_area->set_margin_start($clock->get_allocated_width/2 - $midx); $cairo->set_antialias('default'); DrawTicks(); DrawPoints(); DrawHour(); DrawMin(); DrawSec(); # Only redraw the clock if there are changes. # This highly decreases the CPU usage. $drawing_area->queue_draw unless $skip_redraw; 1; } sub DrawSec { my $dRadians = $dRadians_sec; $cairo->set_source_rgb(0.9, 0, 0); $cairo->move_to($midx, $midy); $cairo->line_to($midx + 85/100 * $radius * sin($dRadians), $midy - 85/100 * $radius * cos($dRadians)); $cairo->stroke; } sub DrawMin { my $dRadians = $dRadians_min; $cairo->set_source_rgb(0.2, 0.2, 0.2); $cairo->move_to($midx - 3/100 * $radius * sin($dRadians), $midy + 3/100 * $radius * cos($dRadians)); $cairo->line_to($midx - 3/100 * $radius * sin($dRadians+$PI/2), $midy + 3/100 * $radius * cos($dRadians+$PI/2)); $cairo->line_to($midx + 7/10 * $radius * sin($dRadians-0.03), $midy - 7/10 * $radius * cos($dRadians-0.03)); $cairo->line_to($midx + 8/10 * $radius * sin($dRadians), $midy - 8/10 * $radius * cos($dRadians)); $cairo->line_to($midx + 7/10 * $radius * sin($dRadians+0.03), $midy - 7/10 * $radius * cos($dRadians+0.03)); $cairo->line_to($midx + 3/100 * $radius * sin($dRadians+$PI/2), $midy - 3/100 * $radius * cos($dRadians+$PI/2)); $cairo->fill; } sub DrawHour { my $dRadians = $dRadians_hour + $dRadians_min / 12; $cairo->set_source_rgb(0.2, 0.2, 0.2); $cairo->move_to($midx - 4/100 * $radius * sin($dRadians), $midy + 4/100 * $radius * cos($dRadians)); $cairo->line_to($midx - 4/100 * $radius * sin($dRadians+$PI/2), $midy + 4/100 * $radius * cos($dRadians+$PI/2)); $cairo->line_to($midx + 5/10 * $radius * sin($dRadians-0.05), $midy - 5/10 * $radius * cos($dRadians-0.05)); $cairo->line_to($midx + 6/10 * $radius * sin($dRadians), $midy - 6/10 * $radius * cos($dRadians)); $cairo->line_to($midx + 5/10 * $radius * sin($dRadians+0.05), $midy - 5/10 * $radius * cos($dRadians+0.05)); $cairo->line_to($midx + 4/100 * $radius * sin($dRadians+$PI/2), $midy - 4/100 * $radius * cos($dRadians+$PI/2)); $cairo->fill; } sub DrawTicks { $cairo->set_source_rgb(1, 1, 1); $cairo->new_sub_path; $cairo->arc($midx, $midy, $radius, 0, 2 * $PI); $cairo->fill_preserve; $cairo->set_source_rgb(0, 0, 0); $cairo->stroke; foreach my $nHour (1..12) { my $dRadians = $nHour * $PI / 6.0; $cairo->move_to($midx + 9/10 * $radius * sin($dRadians), $midy - 9/10 * $radius * cos($dRadians)); $cairo->line_to($midx + 1 * $radius * sin($dRadians), $midy - 1 * $radius * cos($dRadians)); } $cairo->stroke; } sub DrawPoints { $cairo->set_source_rgb(0, 0, 0); foreach my $nMin (1..60) { my $dRadians = $nMin * $PI / 30; $cairo->move_to($midx + 95/100 * $radius * sin($dRadians), $midy - 95/100 * $radius * cos($dRadians)); $cairo->line_to($midx + 90/100 * $radius * sin($dRadians), $midy - 90/100 * $radius * cos($dRadians)); } $cairo->stroke; } sub print_it0 { sprintf("%02d", $_[0]) }