diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 14:51:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 14:51:55 +0000 |
commit | 86b7f1a83d7db9c912f32b29c32e1124c0a6454d (patch) | |
tree | 42a7ff7c6885e99e0669d07b104df11b2bf387b6 /plugins/color/gnome-datetime-source.c | |
parent | Initial commit. (diff) | |
download | gnome-settings-daemon-86b7f1a83d7db9c912f32b29c32e1124c0a6454d.tar.xz gnome-settings-daemon-86b7f1a83d7db9c912f32b29c32e1124c0a6454d.zip |
Adding upstream version 3.38.2.upstream/3.38.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/color/gnome-datetime-source.c')
-rw-r--r-- | plugins/color/gnome-datetime-source.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/plugins/color/gnome-datetime-source.c b/plugins/color/gnome-datetime-source.c new file mode 100644 index 0000000..287ba2d --- /dev/null +++ b/plugins/color/gnome-datetime-source.c @@ -0,0 +1,286 @@ +/* -*- mode: C; c-file-style: "linux"; indent-tabs-mode: t -*- + * gdatetime-source.c - copy&paste from https://bugzilla.gnome.org/show_bug.cgi?id=655129 + * + * Copyright (C) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * Author: Colin Walters <walters@verbum.org> + */ + +#include "config.h" + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include "gnome-datetime-source.h" + +#if HAVE_TIMERFD +#include <sys/timerfd.h> +#include <unistd.h> +#include <string.h> +#endif + +typedef struct _GDateTimeSource GDateTimeSource; +struct _GDateTimeSource +{ + GSource source; + + gint64 real_expiration; + gint64 wakeup_expiration; + + gboolean cancel_on_set : 1; + gboolean initially_expired : 1; + + GPollFD pollfd; +}; + +static inline void +g_datetime_source_reschedule (GDateTimeSource *datetime_source, + gint64 from_monotonic) +{ + datetime_source->wakeup_expiration = from_monotonic + G_TIME_SPAN_SECOND; +} + +static gboolean +g_datetime_source_is_expired (GDateTimeSource *datetime_source) +{ + gint64 real_now; + gint64 monotonic_now; + + real_now = g_get_real_time (); + monotonic_now = g_source_get_time ((GSource*)datetime_source); + + if (datetime_source->initially_expired) + return TRUE; + + if (datetime_source->real_expiration <= real_now) + return TRUE; + + /* We can't really detect without system support when things + * change; so just trigger every second (i.e. our wakeup + * expiration) + */ + if (datetime_source->cancel_on_set && monotonic_now >= datetime_source->wakeup_expiration) + return TRUE; + + return FALSE; +} + +/* In prepare, we're just checking the monotonic time against + * our projected wakeup. + */ +static gboolean +g_datetime_source_prepare (GSource *source, + gint *timeout) +{ + GDateTimeSource *datetime_source = (GDateTimeSource*)source; + gint64 monotonic_now; + +#if HAVE_TIMERFD + if (datetime_source->pollfd.fd != -1) { + *timeout = -1; + return datetime_source->initially_expired; /* Should be TRUE at most one time, FALSE forever after */ + } +#endif + + monotonic_now = g_source_get_time (source); + + if (monotonic_now < datetime_source->wakeup_expiration) { + /* Round up to ensure that we don't try again too early */ + *timeout = (datetime_source->wakeup_expiration - monotonic_now + 999) / 1000; + return FALSE; + } + + *timeout = 0; + return g_datetime_source_is_expired (datetime_source); +} + +/* In check, we're looking at the wall clock. + */ +static gboolean +g_datetime_source_check (GSource *source) +{ + GDateTimeSource *datetime_source = (GDateTimeSource*)source; + +#if HAVE_TIMERFD + if (datetime_source->pollfd.fd != -1) + return datetime_source->pollfd.revents != 0; +#endif + + if (g_datetime_source_is_expired (datetime_source)) + return TRUE; + + g_datetime_source_reschedule (datetime_source, g_source_get_time (source)); + + return FALSE; +} + +static gboolean +g_datetime_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GDateTimeSource *datetime_source = (GDateTimeSource*)source; + + datetime_source->initially_expired = FALSE; + + if (!callback) { + g_warning ("Timeout source dispatched without callback\n" + "You must call g_source_set_callback()."); + return FALSE; + } + + (callback) (user_data); + + /* Always false as this source is documented to run once */ + return FALSE; +} + +static void +g_datetime_source_finalize (GSource *source) +{ +#if HAVE_TIMERFD + GDateTimeSource *datetime_source = (GDateTimeSource*)source; + if (datetime_source->pollfd.fd != -1) + close (datetime_source->pollfd.fd); +#endif +} + +static GSourceFuncs g_datetime_source_funcs = { + g_datetime_source_prepare, + g_datetime_source_check, + g_datetime_source_dispatch, + g_datetime_source_finalize +}; + +#if HAVE_TIMERFD +static gboolean +g_datetime_source_init_timerfd (GDateTimeSource *datetime_source, + gint64 expected_now_seconds, + gint64 unix_seconds) +{ + struct itimerspec its; + int settime_flags; + + datetime_source->pollfd.fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC); + if (datetime_source->pollfd.fd == -1) + return FALSE; + + memset (&its, 0, sizeof (its)); + its.it_value.tv_sec = (time_t) unix_seconds; + + /* http://article.gmane.org/gmane.linux.kernel/1132138 */ +#ifndef TFD_TIMER_CANCEL_ON_SET +#define TFD_TIMER_CANCEL_ON_SET (1 << 1) +#endif + + settime_flags = TFD_TIMER_ABSTIME; + if (datetime_source->cancel_on_set) + settime_flags |= TFD_TIMER_CANCEL_ON_SET; + + if (timerfd_settime (datetime_source->pollfd.fd, settime_flags, &its, NULL) < 0) { + close (datetime_source->pollfd.fd); + datetime_source->pollfd.fd = -1; + return FALSE; + } + + /* Now we need to check that the clock didn't go backwards before we + * had the timerfd set up. See + * https://bugzilla.gnome.org/show_bug.cgi?id=655129 + */ + clock_gettime (CLOCK_REALTIME, &its.it_value); + if (its.it_value.tv_sec < expected_now_seconds) + datetime_source->initially_expired = TRUE; + + datetime_source->pollfd.events = G_IO_IN; + + g_source_add_poll ((GSource*) datetime_source, &datetime_source->pollfd); + + return TRUE; +} +#endif + +/** + * _gnome_date_time_source_new: + * @now: The expected current time + * @expiry: Time to await + * @cancel_on_set: Also invoke callback if the system clock changes discontiguously + * + * This function is designed for programs that want to schedule an + * event based on real (wall clock) time, as returned by + * g_get_real_time(). For example, HOUR:MINUTE wall-clock displays + * and calendaring software. The callback will be invoked when the + * specified wall clock time @expiry is reached. This includes + * events such as the system clock being set past the given time. + * + * Compare versus g_timeout_source_new() which is defined to use + * monotonic time as returned by g_get_monotonic_time(). + * + * The parameter @now is necessary to avoid a race condition in + * between getting the current time and calling this function. + * + * If @cancel_on_set is given, the callback will also be invoked at + * most a second after the system clock is changed. This includes + * being set backwards or forwards, and system + * resume from suspend. Not all operating systems allow detecting all + * relevant events efficiently - this function may cause the process + * to wake up once a second in those cases. + * + * A wall clock display should use @cancel_on_set; a calendaring + * program shouldn't need to. + * + * Note that the return value from the associated callback will be + * ignored; this is a one time watch. + * + * <note><para>This function currently does not detect time zone + * changes. On Linux, your program should also monitor the + * <literal>/etc/timezone</literal> file using + * #GFileMonitor.</para></note> + * + * <example id="gdatetime-example-watch"><title>Clock example</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../glib/tests/glib-clock.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example> + * + * Return value: A newly-constructed #GSource + * + * Since: 2.30 + **/ +GSource * +_gnome_datetime_source_new (GDateTime *now, + GDateTime *expiry, + gboolean cancel_on_set) +{ + GDateTimeSource *datetime_source; + gint64 unix_seconds; + + unix_seconds = g_date_time_to_unix (expiry); + + datetime_source = (GDateTimeSource*) g_source_new (&g_datetime_source_funcs, sizeof (GDateTimeSource)); + + datetime_source->cancel_on_set = cancel_on_set; + +#if HAVE_TIMERFD + { + gint64 expected_now_seconds = g_date_time_to_unix (now); + if (g_datetime_source_init_timerfd (datetime_source, expected_now_seconds, unix_seconds)) + return (GSource*)datetime_source; + /* Fall through to non-timerfd code */ + } +#endif + + datetime_source->real_expiration = unix_seconds * 1000000; + g_datetime_source_reschedule (datetime_source, g_get_monotonic_time ()); + + return (GSource*)datetime_source; +} + |