diff --git a/apps/gtkmm/include/timer_widget.h b/apps/gtkmm/include/timer_widget.h index 5e43fa6..b2f149e 100644 --- a/apps/gtkmm/include/timer_widget.h +++ b/apps/gtkmm/include/timer_widget.h @@ -8,12 +8,15 @@ class TimerWidget : public Gtk::Widget { TimerWidget(); ~TimerWidget() override; + void setTimeStep(int ts); + protected: void measure_vfunc(Gtk::Orientation orientation, int for_size, int& minimum, int& natural, int& minimum_baseline, int& natural_baseline) const override; void snapshot_vfunc(const Glib::RefPtr& snapshot) override; - Gtk::Border padding; Gdk::RGBA foregroundColor { 0.0, 0.0, 0.0 }; + int timeStep; + long lastUpdateMs; }; } // authppgtk \ No newline at end of file diff --git a/apps/gtkmm/src/app_window.cpp b/apps/gtkmm/src/app_window.cpp index b4e7d4b..fb9738c 100644 --- a/apps/gtkmm/src/app_window.cpp +++ b/apps/gtkmm/src/app_window.cpp @@ -44,7 +44,9 @@ AppWindow::AppWindow(BaseObjectType* baseObjectType, const Glib::RefPtrset_data("name", builder->get_widget("name")); list_item->set_data("issuer", builder->get_widget("issuer")); list_item->set_data("code", builder->get_widget("code")); - builder->get_widget("toprow")->append(*Gtk::make_managed()); + auto timer = Gtk::make_managed(); + builder->get_widget("toprow")->append(*timer); + list_item->set_data("timer", timer); list_item->set_child(*accountWidget); }); factory->signal_bind().connect([](const Glib::RefPtr& list_item) { @@ -52,6 +54,7 @@ AppWindow::AppWindow(BaseObjectType* baseObjectType, const Glib::RefPtr(list_item->get_data("issuer")); auto* const code = static_cast(list_item->get_data("code")); + auto holder = std::dynamic_pointer_cast(list_item->get_item()); std::string issuerValue; @@ -62,6 +65,14 @@ AppWindow::AppWindow(BaseObjectType* baseObjectType, const Glib::RefPtrset_text(nameValue); issuer->set_text(issuerValue); code->set_text(holder->account.code.value); + + auto* const timer = static_cast(list_item->get_data("timer")); + if (holder->account.code.type == authpp::oath::Type::TOTP) { + timer->setTimeStep(holder->account.timeStep); + timer->set_visible(true); + } else { + timer->set_visible(false); + } }); refreshButton->signal_clicked().connect(sigc::mem_fun(*this, &AppWindow::requestAccounts)); diff --git a/apps/gtkmm/src/timer_widget.cpp b/apps/gtkmm/src/timer_widget.cpp index 5194fca..00d6ed9 100644 --- a/apps/gtkmm/src/timer_widget.cpp +++ b/apps/gtkmm/src/timer_widget.cpp @@ -1,21 +1,40 @@ #include "timer_widget.h" +#include +#include + +#include #include + #include namespace authppgtk { + +namespace { + authpp::Logger log("TimerWidget"); +} + TimerWidget::TimerWidget() { set_expand(false); - padding.set_left(0); - padding.set_right(0); - padding.set_top(0); - padding.set_bottom(0); + timeStep = 0; + lastUpdateMs = 0L; + + add_tick_callback([this](const auto& clock) { + auto currentFrameTime = clock->get_frame_time() / 1000; + if (currentFrameTime > lastUpdateMs + 200) { + queue_draw(); + lastUpdateMs = currentFrameTime; + } + return true; + }); } TimerWidget::~TimerWidget() = default; +void TimerWidget::setTimeStep(int ts) { this->timeStep = ts; } + void TimerWidget::measure_vfunc(Gtk::Orientation orientation, int /* for_size */, int& minimum, int& natural, int& minimum_baseline, int& natural_baseline) const { @@ -27,7 +46,7 @@ void TimerWidget::measure_vfunc(Gtk::Orientation orientation, int /* for_size */ void TimerWidget::snapshot_vfunc(const Glib::RefPtr& snapshot) { - auto toRad = [](double deg) -> double { + auto degToRad = [](double deg) -> double { auto d = deg - 90; if (d < 0) { d = 360 + d; @@ -35,19 +54,29 @@ void TimerWidget::snapshot_vfunc(const Glib::RefPtr& snapshot) return d * M_PI / 180.0; }; + auto currentMillis = authpp::TimeUtil::getCurrentMilliSeconds(); + auto currentSeconds = currentMillis / 1000; + + auto ts = authpp::TimeUtil::getTotpTimeStep(currentSeconds, timeStep); + const auto allocation = get_allocation(); - const Gdk::Rectangle rect(0, 0, allocation.get_width(), allocation.get_height()); + const auto width = allocation.get_width(); + const auto width_2 = width / 2.0; + const auto height = allocation.get_height(); + const auto height_2 = height / 2.0; + const Gdk::Rectangle rect(0, 0, width, height); auto cr = snapshot->append_cairo(rect); - cr->set_antialias(Cairo::ANTIALIAS_SUBPIXEL); - Gdk::Cairo::set_source_rgba(cr, foregroundColor); - cr->translate(padding.get_left(), padding.get_top()); - cr->move_to(allocation.get_width() / 2.0, allocation.get_height() / 2.0); - cr->arc_negative(allocation.get_width() / 2.0, allocation.get_height() / 2.0, - allocation.get_height() / 2.0 - 2, toRad(0), toRad(90)); - cr->move_to(allocation.get_width() / 2.0, allocation.get_height() / 2.0); - cr->fill(); - cr->arc(allocation.get_width() / 2.0, allocation.get_height() / 2.0, - allocation.get_height() / 2.0 - 2, 0, 2 * M_PI); + + if (currentSeconds < ((ts + 1) * timeStep)) { + auto accountDelayMs = (double)(currentMillis - ts * timeStep * 1000); + auto deg = (360.0 / (timeStep * 1000)) * (accountDelayMs - 1500); + Gdk::Cairo::set_source_rgba(cr, foregroundColor); + cr->move_to(width_2, height_2); + cr->arc_negative(width_2, height_2, height_2 - 2, degToRad(0), degToRad(deg)); + cr->move_to(width_2, height_2); + cr->fill(); + } + cr->arc(width_2, height_2, height_2 - 2, 0, 2 * M_PI); cr->stroke(); }