weather: Introduce a proper timer for forecasts.
This commit is contained in:
committed by
Georges Basile Stavracas Neto
parent
72dc615bfd
commit
83d6eaef9a
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* gcal-timer.c
|
||||
*
|
||||
* Copyright (C) 2017 - Florian Brosch <flo.brosch@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gcal-timer.h"
|
||||
|
||||
|
||||
/**
|
||||
* The #GcalTimer object structure.
|
||||
*
|
||||
* Do not use this one as #GSource.
|
||||
* Only use it in combination with its
|
||||
* methods.
|
||||
*/
|
||||
typedef struct _GcalTimer
|
||||
{
|
||||
GSource parent;
|
||||
gint64 last_event;
|
||||
gint64 default_duration;
|
||||
} GcalTimer;
|
||||
|
||||
|
||||
/* CallbackWrapper:
|
||||
* @timer: The timer the callback applies to.
|
||||
* @callback: Original callback.
|
||||
* @destroy_notify: Original user-data destroy function.
|
||||
* @data: Original user data.
|
||||
*
|
||||
* Internal struct used to hide GSource internals
|
||||
* from API users.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
GcalTimer *timer; /* unowned */
|
||||
GCalTimerFunc callback;
|
||||
GDestroyNotify destroy_notify;
|
||||
gpointer data;
|
||||
} CallbackWrapper;
|
||||
|
||||
|
||||
static void timer_func_destroy_notify_wrapper (CallbackWrapper *wrapper);
|
||||
|
||||
static gboolean timer_func_wrapper (CallbackWrapper *wrapper);
|
||||
|
||||
static gboolean timer_source_dispatch (GcalTimer *self,
|
||||
GSourceFunc callback,
|
||||
CallbackWrapper *user_data);
|
||||
|
||||
static void schedule_next (GcalTimer *self);
|
||||
|
||||
static void timer_source_finalize (GcalTimer *self);
|
||||
|
||||
|
||||
|
||||
|
||||
/*< private >*/
|
||||
static void
|
||||
timer_func_destroy_notify_wrapper (CallbackWrapper *wrapper)
|
||||
{
|
||||
g_return_if_fail (wrapper != NULL);
|
||||
|
||||
if (wrapper->destroy_notify != NULL && wrapper->data != NULL)
|
||||
wrapper->destroy_notify (wrapper->data);
|
||||
|
||||
g_free (wrapper);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
timer_func_wrapper (CallbackWrapper *wrapper)
|
||||
{
|
||||
g_return_val_if_fail (wrapper != NULL, G_SOURCE_REMOVE);
|
||||
|
||||
if (wrapper->callback != NULL)
|
||||
wrapper->callback (wrapper->timer, wrapper->data);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
timer_source_dispatch (GcalTimer *self,
|
||||
GSourceFunc user_callback,
|
||||
CallbackWrapper *user_data)
|
||||
{
|
||||
gboolean result = G_SOURCE_CONTINUE;
|
||||
|
||||
g_return_val_if_fail (self != NULL, G_SOURCE_REMOVE);
|
||||
|
||||
if (user_callback != NULL)
|
||||
result = user_callback (user_data);
|
||||
|
||||
self->last_event = g_source_get_time ((GSource*) self);
|
||||
schedule_next (self);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
schedule_next (GcalTimer *self)
|
||||
{
|
||||
gint64 now;
|
||||
gint64 next;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->last_event >= 0);
|
||||
|
||||
now = g_source_get_time ((GSource*) self);
|
||||
next = self->last_event + self->default_duration*G_GUINT64_CONSTANT(1000000);
|
||||
|
||||
if (next > now)
|
||||
g_source_set_ready_time ((GSource*) self, next);
|
||||
else
|
||||
g_source_set_ready_time ((GSource*) self, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
timer_source_finalize (GcalTimer *self)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*< public >*/
|
||||
/**
|
||||
* gcal_timer_new:
|
||||
* @default_duration: The duration (>0) between two events in seconds.
|
||||
*
|
||||
* Creates a new #GcalTimer object.
|
||||
*
|
||||
* While #GcalTimers are proper #GSources, they are not intended
|
||||
* to be used directly.
|
||||
*
|
||||
* Returns: (transfer full): A new timer object.
|
||||
*/
|
||||
GcalTimer*
|
||||
gcal_timer_new (gint64 default_duration)
|
||||
{
|
||||
GMainContext *cntxt; /* unowned */
|
||||
GcalTimer *self; /* owned */
|
||||
|
||||
g_return_val_if_fail (default_duration > 0, NULL);
|
||||
|
||||
static GSourceFuncs source_funcs =
|
||||
{ NULL, /* prepare */
|
||||
NULL, /* check */
|
||||
(gboolean (*) (GSource*, GSourceFunc, gpointer)) timer_source_dispatch,
|
||||
(void (*) (GSource*)) timer_source_finalize
|
||||
};
|
||||
|
||||
self = (GcalTimer*) g_source_new (&source_funcs, sizeof (GcalTimer));
|
||||
self->default_duration = default_duration;
|
||||
self->last_event = -1;
|
||||
|
||||
cntxt = g_main_context_default ();
|
||||
g_source_set_ready_time ((GSource*) self, -1);
|
||||
g_source_attach ((GSource*) self, cntxt);
|
||||
|
||||
return g_steal_pointer (&self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_timer_set_start:
|
||||
* @self: The #GcalTimer.
|
||||
*
|
||||
* Starts this timer.
|
||||
*/
|
||||
void
|
||||
gcal_timer_start (GcalTimer *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (!gcal_timer_is_running (self));
|
||||
|
||||
self->last_event = g_source_get_time ((GSource*) self);
|
||||
schedule_next (self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_timer_set_reset:
|
||||
* @self: The #GcalTimer.
|
||||
*
|
||||
* Re-schedules next event.
|
||||
*/
|
||||
void
|
||||
gcal_timer_reset (GcalTimer *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (gcal_timer_is_running (self));
|
||||
|
||||
if (g_source_get_ready_time ((GSource*) self) >= 0)
|
||||
{
|
||||
self->last_event = g_source_get_time ((GSource*) self);
|
||||
schedule_next (self);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_timer_set_stop:
|
||||
* @self: The #GcalTimer.
|
||||
*
|
||||
* Stops a previously started timer.
|
||||
*/
|
||||
void
|
||||
gcal_timer_stop (GcalTimer *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (gcal_timer_is_running (self));
|
||||
|
||||
g_source_set_ready_time ((GSource*) self, -1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_timer_is_running
|
||||
* @self: The #GcalTimer.
|
||||
*
|
||||
* Whether this timer is running.
|
||||
*/
|
||||
gboolean
|
||||
gcal_timer_is_running (GcalTimer *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
|
||||
return g_source_get_ready_time ((GSource*) self) >= 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_timer_set_duration:
|
||||
* @self: The #GcalTimer.
|
||||
* @duration: The new duration
|
||||
*
|
||||
* Changes the duration between two events.
|
||||
*/
|
||||
void
|
||||
gcal_timer_set_duration (GcalTimer *self,
|
||||
gint64 duration)
|
||||
{
|
||||
gint64 now;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (duration > 0);
|
||||
|
||||
now = g_source_get_time ((GSource*) self);
|
||||
|
||||
self->default_duration = duration;
|
||||
if (!gcal_timer_is_running (self))
|
||||
/* nothing to do (not running) */;
|
||||
else if (self->last_event + duration < now)
|
||||
g_source_set_ready_time ((GSource*) self, 0);
|
||||
else
|
||||
schedule_next (self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gcal_timer_set_callback:
|
||||
* @self: The #GcalTimer.
|
||||
* @func: The event handler callback function.
|
||||
* @data: (nullable): User data passed to @func.
|
||||
* @notify: (nullable): free or unref function for @data.
|
||||
*
|
||||
* Sets the callback to be triggered after each duration
|
||||
* or given day-times.
|
||||
*/
|
||||
void
|
||||
gcal_timer_set_callback (GcalTimer *self,
|
||||
GCalTimerFunc func,
|
||||
gpointer data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
CallbackWrapper *wrapper; /* owned */
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (func != NULL);
|
||||
|
||||
wrapper = g_new0 (CallbackWrapper, 1);
|
||||
wrapper->timer = self;
|
||||
wrapper->callback = func;
|
||||
wrapper->destroy_notify = notify;
|
||||
wrapper->data = data;
|
||||
|
||||
g_source_set_callback ((GSource*) self,
|
||||
(GSourceFunc) timer_func_wrapper,
|
||||
g_steal_pointer (&wrapper),
|
||||
(GDestroyNotify) timer_func_destroy_notify_wrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gcal_timer_free:
|
||||
* @self: The #GcalTimer.
|
||||
*
|
||||
* Frees #GcalTimer.
|
||||
*/
|
||||
void
|
||||
gcal_timer_free (GcalTimer *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_source_destroy ((GSource *) self);
|
||||
g_source_unref ((GSource *) self);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* gcal-timer.h
|
||||
*
|
||||
* Copyright (C) 2017 - Florian Brosch <flo.brosch@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GCAL_TIMER_H__
|
||||
#define __GCAL_TIMER_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
struct _GcalTimer;
|
||||
typedef struct _GcalTimer GcalTimer;
|
||||
|
||||
typedef void (*GCalTimerFunc) (GcalTimer *self, gpointer data);
|
||||
|
||||
|
||||
GcalTimer* gcal_timer_new (gint64 default_duration);
|
||||
|
||||
void gcal_timer_start (GcalTimer *self);
|
||||
|
||||
void gcal_timer_reset (GcalTimer *self);
|
||||
|
||||
void gcal_timer_stop (GcalTimer *self);
|
||||
|
||||
gboolean gcal_timer_is_running (GcalTimer *self);
|
||||
|
||||
void gcal_timer_set_duration (GcalTimer *self,
|
||||
gint64 duration);
|
||||
|
||||
void gcal_timer_set_callback (GcalTimer *self,
|
||||
GCalTimerFunc func,
|
||||
gpointer data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
void gcal_timer_free (GcalTimer *self);
|
||||
|
||||
|
||||
#endif /* __GCAL_TIMER_H__ */
|
||||
+40
-73
@@ -24,6 +24,7 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "gcal-weather-service.h"
|
||||
#include "gcal-timer.h"
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@@ -44,7 +45,6 @@ typedef struct
|
||||
*
|
||||
* @time_zone: The current time zone
|
||||
* @check_interval: Amount of seconds to wait before re-fetching weather infos.
|
||||
* @timeout_id: Time-out event ID or %0. Timer is used to periodically update weather infos.
|
||||
* @location_service: Used to monitor location changes.
|
||||
* Initialized by gcal_weather_service_run(),
|
||||
* freed by gcal_weather_service_stop().
|
||||
@@ -77,7 +77,7 @@ struct _GcalWeatherService
|
||||
|
||||
/* timer: */
|
||||
guint check_interval;
|
||||
guint timeout_id;
|
||||
GcalTimer *timer;
|
||||
|
||||
/* locations: */
|
||||
GClueSimple *location_service; /* owned, nullable */
|
||||
@@ -117,6 +117,10 @@ static guint gcal_weather_service_signals[SIG_NUM] = { 0 };
|
||||
G_DEFINE_TYPE (GcalWeatherService, gcal_weather_service, G_TYPE_OBJECT)
|
||||
|
||||
|
||||
/* Timer Helpers: */
|
||||
static void update_timeout_interval (GcalWeatherService *self);
|
||||
|
||||
|
||||
/* Internal location API and callbacks: */
|
||||
static void gcal_weather_service_update_location (GcalWeatherService *self,
|
||||
GWeatherLocation *location);
|
||||
@@ -165,11 +169,8 @@ static gchar* get_normalized_icon_name (GWeatherInfo
|
||||
static gint get_icon_name_sortkey (const gchar *icon_name,
|
||||
gboolean *supports_night_icon);
|
||||
|
||||
static void gcal_weather_service_timer_stop (GcalWeatherService *self);
|
||||
|
||||
static void gcal_weather_service_timer_start (GcalWeatherService *self);
|
||||
|
||||
static gboolean on_timer_timeout (GcalWeatherService *self);
|
||||
static void on_timer_timeout (GcalTimer *timer,
|
||||
GcalWeatherService *self);
|
||||
|
||||
static gboolean get_time_day_start (GcalWeatherService *self,
|
||||
GDate *ret_date,
|
||||
@@ -202,6 +203,9 @@ gcal_weather_service_finalize (GObject *object)
|
||||
|
||||
self = (GcalWeatherService *) object;
|
||||
|
||||
gcal_timer_free (self->timer);
|
||||
self->timer = NULL;
|
||||
|
||||
if (self->time_zone != NULL)
|
||||
{
|
||||
g_time_zone_unref (self->time_zone);
|
||||
@@ -371,9 +375,10 @@ gcal_weather_service_class_init (GcalWeatherServiceClass *klass)
|
||||
static void
|
||||
gcal_weather_service_init (GcalWeatherService *self)
|
||||
{
|
||||
self->timer = gcal_timer_new (600);
|
||||
gcal_timer_set_callback (self->timer, (GCalTimerFunc) on_timer_timeout, self, NULL);
|
||||
self->time_zone = NULL;
|
||||
self->check_interval = 0;
|
||||
self->timeout_id = 0;
|
||||
self->location_cancellable = g_cancellable_new ();
|
||||
self->location_service_running = FALSE;
|
||||
self->location_service = NULL;
|
||||
@@ -893,7 +898,7 @@ gcal_weather_service_update_location (GcalWeatherService *self,
|
||||
|
||||
if (self->gweather_info != NULL)
|
||||
{
|
||||
gcal_weather_service_timer_stop (self);
|
||||
gcal_timer_stop (self->timer);
|
||||
g_clear_object (&self->gweather_info);
|
||||
}
|
||||
|
||||
@@ -926,7 +931,8 @@ gcal_weather_service_update_location (GcalWeatherService *self,
|
||||
gcal_weather_service_update_weather (self, NULL, FALSE);
|
||||
gweather_info_update (self->gweather_info);
|
||||
|
||||
gcal_weather_service_timer_start (self);
|
||||
update_timeout_interval (self);
|
||||
gcal_timer_start (self->timer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1225,6 +1231,15 @@ on_gweather_update (GWeatherInfo *info,
|
||||
|
||||
|
||||
|
||||
static void
|
||||
update_timeout_interval (GcalWeatherService *self)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
gcal_timer_set_duration (self->timer, self->check_interval);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_service_set_max_days:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
* @days: Number of days.
|
||||
@@ -1238,79 +1253,28 @@ gcal_weather_service_set_check_interval (GcalWeatherService *self,
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
self->check_interval = interval;
|
||||
update_timeout_interval (self);
|
||||
|
||||
g_object_notify ((GObject*) self, "check-interval");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_service_timer_stop:
|
||||
* @self: The #GcalWeatherService instance.
|
||||
*
|
||||
* Stops the internal timer. Does nothing if
|
||||
* timer is not running.
|
||||
*
|
||||
* This timer is used to update weather information
|
||||
* every :check-interval seconds
|
||||
*/
|
||||
static void
|
||||
gcal_weather_service_timer_stop (GcalWeatherService *self)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
if (self->timeout_id > 0)
|
||||
{
|
||||
g_source_remove (self->timeout_id);
|
||||
self->timeout_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gcal_weather_service_timer_start
|
||||
* @self: The #GcalWeatherService instance.
|
||||
*
|
||||
* Starts the internal timer. Does nothing if
|
||||
* timer is already running.
|
||||
*
|
||||
* Timer is used to update weather information
|
||||
* every :check-interval seconds.
|
||||
*/
|
||||
static void
|
||||
gcal_weather_service_timer_start (GcalWeatherService *self)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
if (self->check_interval == 0)
|
||||
/* timer is disabled */
|
||||
return;
|
||||
|
||||
if (self->timeout_id > 0)
|
||||
/* timer is already running */
|
||||
return;
|
||||
|
||||
self->timeout_id = g_timeout_add_seconds (self->check_interval,
|
||||
(GSourceFunc) on_timer_timeout,
|
||||
self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* on_timer_timeout
|
||||
* @self: A #GcalWeatherService.
|
||||
*
|
||||
* Handles scheduled weather report updates.
|
||||
*
|
||||
* Returns: %G_SOURCE_CONTINUE
|
||||
*/
|
||||
static gboolean
|
||||
on_timer_timeout (GcalWeatherService *self)
|
||||
static void
|
||||
on_timer_timeout (GcalTimer *timer,
|
||||
GcalWeatherService *self)
|
||||
{
|
||||
g_return_val_if_fail (GCAL_IS_WEATHER_SERVICE (self), G_SOURCE_REMOVE);
|
||||
g_return_if_fail (timer != NULL);
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
gcal_weather_service_update (self);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
if (self->gweather_info)
|
||||
gweather_info_update (self->gweather_info);
|
||||
}
|
||||
|
||||
|
||||
@@ -1383,8 +1347,8 @@ gcal_weather_service_run (GcalWeatherService *self,
|
||||
self->location_service_running = FALSE;
|
||||
self->weather_service_running = TRUE;
|
||||
|
||||
/*_update_location starts timer if necessary */
|
||||
gcal_weather_service_update_location (self, location);
|
||||
gcal_weather_service_timer_start (self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1410,8 +1374,6 @@ gcal_weather_service_stop (GcalWeatherService *self)
|
||||
self->location_service_running = FALSE;
|
||||
self->weather_service_running = FALSE;
|
||||
|
||||
gcal_weather_service_timer_stop (self);
|
||||
|
||||
/* Notify all listeners about unknown location: */
|
||||
gcal_weather_service_update_location (self, NULL);
|
||||
|
||||
@@ -1580,5 +1542,10 @@ gcal_weather_service_update (GcalWeatherService *self)
|
||||
g_return_if_fail (GCAL_IS_WEATHER_SERVICE (self));
|
||||
|
||||
if (self->gweather_info != NULL)
|
||||
gweather_info_update (self->gweather_info);
|
||||
{
|
||||
gweather_info_update (self->gweather_info);
|
||||
update_timeout_interval (self);
|
||||
if (gcal_timer_is_running (self->timer))
|
||||
gcal_timer_reset (self->timer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ sources = files(
|
||||
'gcal-window.c',
|
||||
'gcal-weather-service.c',
|
||||
'gcal-weather-info.c',
|
||||
'gcal-timer.c'
|
||||
)
|
||||
|
||||
gnome.mkenums(
|
||||
|
||||
Reference in New Issue
Block a user