From a9af882e8496e61f5407474a8380e9be512182bc Mon Sep 17 00:00:00 2001 From: Olivier Brunel Date: Sun, 18 Mar 2018 14:07:07 +0100 Subject: [PATCH 2/4] Fix button refresh after suspend for Linux only Refreshing the button (to update date/time) is done via timeouts, but the way they're implemented in GLib means they're not suspend aware, thus the timeouts simply "resume"/keep going on resume, and as a result the update/refreshing might come a little while after needed/resuming from suspend. This is fixed by using suspend-aware timeout sources, based on absolute time on CLOCK_REALTIME. However, this is implemented via Linux-specific interface (timerfd) and thus only works there. If needed for other unices, an implementation using POSIX timer_create & co might do the trick (but will force to play with signals). Signed-off-by: Olivier Brunel --- configure.ac.in | 12 ++++ panel-plugin/Makefile.am | 6 ++ panel-plugin/datetime.h | 4 ++ panel-plugin/rt_timeout.c | 143 ++++++++++++++++++++++++++++++++++++++ panel-plugin/rt_timeout.h | 30 ++++++++ 5 files changed, 195 insertions(+) create mode 100644 panel-plugin/rt_timeout.c create mode 100644 panel-plugin/rt_timeout.h diff --git a/configure.ac.in b/configure.ac.in index 679522e..599fed5 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -16,6 +16,18 @@ AM_INIT_AUTOMAKE([AC_PACKAGE_TARNAME()], [AC_PACKAGE_VERSION()]) AM_CONFIG_HEADER([config.h]) AM_MAINTAINER_MODE() +dnl Get host type (host_os) +AC_CANONICAL_HOST + +is_linux=no +case "$host_os" in + linux*) is_linux=yes ;; +esac +AM_CONDITIONAL([LINUX], [test "$is_linux" = "yes"]) +if [test "$is_linux" = "yes"]; then + AC_DEFINE([LINUX], 1, [Linux]) +fi + dnl Check for UNIX variants AC_AIX AC_ISC_POSIX diff --git a/panel-plugin/Makefile.am b/panel-plugin/Makefile.am index 97cd1cf..90d4740 100644 --- a/panel-plugin/Makefile.am +++ b/panel-plugin/Makefile.am @@ -9,6 +9,12 @@ libdatetime_la_SOURCES = \ datetime-dialog.h \ datetime-dialog.c +if LINUX +libdatetime_la_SOURCES += \ + rt_timeout.h \ + rt_timeout.c +endif + libdatetime_la_CFLAGS = \ -I$(top_srcdir) \ -DLOCALEDIR=\"$(localedir)\" \ diff --git a/panel-plugin/datetime.h b/panel-plugin/datetime.h index f1f55e3..3fe434b 100644 --- a/panel-plugin/datetime.h +++ b/panel-plugin/datetime.h @@ -22,6 +22,10 @@ #ifndef DATETIME_H #define DATETIME_H +#ifdef LINUX +#include "rt_timeout.h" +#define g_timeout_add(ms,fn,data) rt_timeout_add (ms, fn, data) +#endif #include diff --git a/panel-plugin/rt_timeout.c b/panel-plugin/rt_timeout.c new file mode 100644 index 0000000..e161fe7 --- /dev/null +++ b/panel-plugin/rt_timeout.c @@ -0,0 +1,143 @@ +/* + * From kalu: https://jjacky.com/kalu | https://github.com/jjk-jacky/kalu + * Copyright (C) 2013-2018 Olivier Brunel + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include /* close() */ +#include +#include "rt_timeout.h" + +struct _RtTimeoutSource +{ + GSource source; + GPollFD pollfd; + guint interval; +}; + +static gboolean rt_timeout_prepare (GSource *source, gint *timeout); +static gboolean rt_timeout_check (GSource *source); +static gboolean rt_timeout_dispatch (GSource *source, GSourceFunc callback, gpointer data); +static void rt_timeout_finalize (GSource *source); + +static GSourceFuncs rt_timeout_funcs = +{ + .prepare = rt_timeout_prepare, + .check = rt_timeout_check, + .dispatch = rt_timeout_dispatch, + .finalize = rt_timeout_finalize +}; + +static gboolean +rt_timeout_prepare (GSource *source __attribute__ ((unused)), gint *timeout) +{ + *timeout = -1; + return FALSE; +} + +static gboolean +rt_timeout_check (GSource *source) +{ + RtTimeoutSource *sce = (RtTimeoutSource *) source; + return (sce->pollfd.revents & G_IO_IN); +} + +#define SECOND 1000000000 +static void set_value (struct timespec *tp, guint ms) +{ + time_t sec; + long nsec; + + sec = ms / 1000; + nsec = (ms - 1000 * sec) * 1000000; + + clock_gettime (CLOCK_REALTIME, tp); + tp->tv_sec += sec; + if (tp->tv_nsec + nsec > SECOND) + { + ++tp->tv_sec; + nsec -= SECOND; + } + tp->tv_nsec += nsec; +} + +static gboolean +rt_timeout_dispatch (GSource *source, GSourceFunc callback, gpointer data) +{ + gboolean again; + + again = callback (data); + if (again) + { + RtTimeoutSource *sce = (RtTimeoutSource *) source; + struct itimerspec ts; + + set_value (&ts.it_value, sce->interval); + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + if (timerfd_settime (sce->pollfd.fd, TFD_TIMER_ABSTIME, &ts, NULL) == -1) + again = FALSE; + } + return again; +} + +static void +rt_timeout_finalize (GSource *source) +{ + RtTimeoutSource *sce = (RtTimeoutSource *) source; + close (sce->pollfd.fd); +} + +guint +rt_timeout_add (guint interval, GSourceFunc function, gpointer data) +{ + GSource *source; + RtTimeoutSource *sce; + guint id; + struct itimerspec ts; + + if (interval == 0) + return 0; + + source = g_source_new (&rt_timeout_funcs, sizeof (RtTimeoutSource)); + sce = (RtTimeoutSource *) source; + + sce->pollfd.fd = timerfd_create (CLOCK_REALTIME, TFD_NONBLOCK); + if (sce->pollfd.fd == -1) + { + g_source_unref (source); + return 0; + } + set_value (&ts.it_value, interval); + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + if (timerfd_settime (sce->pollfd.fd, TFD_TIMER_ABSTIME, &ts, NULL) == -1) + { + g_source_unref (source); + return 0; + } + + sce->pollfd.events = G_IO_IN | G_IO_ERR; + g_source_add_poll (source, &sce->pollfd); + + sce->interval = interval; + g_source_set_callback (source, function, data, NULL); + + id = g_source_attach (source, NULL); + g_source_unref (source); + return id; +} diff --git a/panel-plugin/rt_timeout.h b/panel-plugin/rt_timeout.h new file mode 100644 index 0000000..62d12ed --- /dev/null +++ b/panel-plugin/rt_timeout.h @@ -0,0 +1,30 @@ +/* + * From kalu: https://jjacky.com/kalu | https://github.com/jjk-jacky/kalu + * Copyright (C) 2013-2018 Olivier Brunel + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _KALU_RT_TIMEOUT_H +#define _KALU_RT_TIMEOUT_H + +/* glib */ +#include + +typedef struct _RtTimeoutSource RtTimeoutSource; + +guint rt_timeout_add (guint interval, GSourceFunc function, gpointer data); + +#endif /* _KALU_RT_TIMEOUT_H */ -- 2.19.0