diff --git a/src/Makefile.am b/src/Makefile.am index 567aec65..d63f5456 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,8 +42,10 @@ gnome_calendar_SOURCES = \ views/gcal-week-view.h \ views/gcal-year-view.c \ views/gcal-year-view.h \ - gcal-application.h \ gcal-application.c \ + gcal-application.h \ + gcal-clock.c \ + gcal-clock.h \ gcal-date-chooser-day.c \ gcal-date-chooser-day.h \ gcal-date-chooser.c \ diff --git a/src/gcal-clock.c b/src/gcal-clock.c new file mode 100644 index 00000000..cb4afec0 --- /dev/null +++ b/src/gcal-clock.c @@ -0,0 +1,284 @@ +/* gcal-clock.c + * + * Copyright (C) 2017 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define G_LOG_DOMAIN "GcalClock" + +#include "gcal-clock.h" +#include "gcal-debug.h" +#include "gcal-utils.h" + +#include + +struct _GcalClock +{ + GObject parent; + + guint timeout_id; + + GDateTime *current; + + GDBusProxy *proxy; + GCancellable *cancellable; +}; + +static gboolean timeout_cb (gpointer user_data); + +G_DEFINE_TYPE (GcalClock, gcal_clock, G_TYPE_OBJECT) + +enum +{ + MINUTE_CHANGED, + HOUR_CHANGED, + DAY_CHANGED, + NUM_SIGNALS +}; + +static guint signals[NUM_SIGNALS] = { 0, }; + +/* + * Auxiliary methods + */ + +static void +update_current_date (GcalClock *self) +{ + g_autoptr (GDateTime) now; + gboolean minute_changed; + gboolean hour_changed; + gboolean day_changed; + + GCAL_ENTRY; + + now = g_date_time_new_now_local (); + + day_changed = g_date_time_get_year (now) != g_date_time_get_year (self->current) || + g_date_time_get_day_of_year (now) != g_date_time_get_day_of_year (self->current); + hour_changed = day_changed || g_date_time_get_hour (now) != g_date_time_get_hour (self->current); + minute_changed = hour_changed || g_date_time_get_minute (now) != g_date_time_get_minute (self->current); + + if (day_changed) + g_signal_emit (self, signals[DAY_CHANGED], 0); + + if (hour_changed) + g_signal_emit (self, signals[HOUR_CHANGED], 0); + + if (minute_changed) + g_signal_emit (self, signals[MINUTE_CHANGED], 0); + + g_debug ("Updating clock time"); + + gcal_clear_datetime (&self->current); + self->current = g_date_time_ref (now); + + GCAL_EXIT; +} + +static void +schedule_update_timeout (GcalClock *self) +{ + g_autoptr (GDateTime) now; + guint seconds_between; + + /* Remove the previous timeout if we came from resume */ + if (self->timeout_id > 0) + { + g_source_remove (self->timeout_id); + self->timeout_id = 0; + } + + now = g_date_time_new_now_local (); + + seconds_between = 60 - g_date_time_get_second (now); + + self->timeout_id = g_timeout_add_seconds (seconds_between, timeout_cb, self); + + g_debug ("Scheduling update for %d seconds", seconds_between); +} + +/* + * Callbacks + */ + +static gboolean +timeout_cb (gpointer user_data) +{ + GcalClock *self = user_data; + + self->timeout_id = 0; + + update_current_date (self); + schedule_update_timeout (self); + + return G_SOURCE_REMOVE; +} + +static void +logind_signal_received_cb (GDBusProxy *logind, + const gchar *sender, + const gchar *signal, + GVariant *params, + GcalClock *self) +{ + GVariant *child; + gboolean resuming; + + if (!g_str_equal (signal, "PrepareForSleep")) + return; + + child = g_variant_get_child_value (params, 0); + resuming = !g_variant_get_boolean (child); + + /* Only emit :update when resuming */ + if (resuming) + { + update_current_date (self); + schedule_update_timeout (self); + } + + g_clear_pointer (&child, g_variant_unref); +} + +static void +login_proxy_acquired_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GcalClock *self; + GError *error; + + self = GCAL_CLOCK (user_data); + error = NULL; + + self->proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + + if (error) + { + g_warning ("Error acquiring logind DBus proxy: %s", error->message); + g_clear_error (&error); + return; + } + + g_signal_connect (self->proxy, + "g-signal", + G_CALLBACK (logind_signal_received_cb), + self); + + g_debug ("Successfully acquired logind DBus proxy"); +} + +static void +gcal_clock_finalize (GObject *object) +{ + GcalClock *self = (GcalClock *)object; + + g_cancellable_cancel (self->cancellable); + + if (self->timeout_id > 0) + { + g_source_remove (self->timeout_id); + self->timeout_id = 0; + } + + gcal_clear_datetime (&self->current); + + g_clear_object (&self->cancellable); + g_clear_object (&self->proxy); + + G_OBJECT_CLASS (gcal_clock_parent_class)->finalize (object); +} + +static void +gcal_clock_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +gcal_clock_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +gcal_clock_class_init (GcalClockClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gcal_clock_finalize; + object_class->get_property = gcal_clock_get_property; + object_class->set_property = gcal_clock_set_property; + + signals[DAY_CHANGED] = g_signal_new ("day-changed", + GCAL_TYPE_CLOCK, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 0); + + signals[HOUR_CHANGED] = g_signal_new ("hour-changed", + GCAL_TYPE_CLOCK, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 0); + + signals[MINUTE_CHANGED] = g_signal_new ("minute-changed", + GCAL_TYPE_CLOCK, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 0); +} + +static void +gcal_clock_init (GcalClock *self) +{ + self->current = g_date_time_new_now_local (); + self->cancellable = g_cancellable_new (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + self->cancellable, + login_proxy_acquired_cb, + self); + + schedule_update_timeout (self); +} + +/** + * gcal_clock_new: + * + * Creates a new #GcalClock. + * + * Returns: (transfer full): a newly-allocated #GcalClock. + */ +GcalClock* +gcal_clock_new (void) +{ + return g_object_new (GCAL_TYPE_CLOCK, NULL); +} diff --git a/src/gcal-clock.h b/src/gcal-clock.h new file mode 100644 index 00000000..96352cab --- /dev/null +++ b/src/gcal-clock.h @@ -0,0 +1,35 @@ +/* gcal-clock.h + * + * Copyright (C) 2017 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GCAL_CLOCK_H +#define GCAL_CLOCK_H + +#include + +G_BEGIN_DECLS + +#define GCAL_TYPE_CLOCK (gcal_clock_get_type()) + +G_DECLARE_FINAL_TYPE (GcalClock, gcal_clock, GCAL, CLOCK, GObject) + +GcalClock* gcal_clock_new (void); + +G_END_DECLS + +#endif /* GCAL_CLOCK_H */ +