From 153dec0e21530d52b8ee82f6ec588620ec0857d2 Mon Sep 17 00:00:00 2001 From: Jon Lund Steffensen Date: Wed, 16 Aug 2017 21:20:06 -0700 Subject: Change location providers to allow updates Change location provider implementations so it is possible for location providers to dynamically update the location. This commit adds the interfaces and infrastructure in redshift.c but none of the location provides are changed to become dynamic. --- src/location-geoclue2.c | 97 +++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 44 deletions(-) (limited to 'src/location-geoclue2.c') diff --git a/src/location-geoclue2.c b/src/location-geoclue2.c index abccbd3..ab57535 100644 --- a/src/location-geoclue2.c +++ b/src/location-geoclue2.c @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with Redshift. If not, see . - Copyright (c) 2014 Jon Lund Steffensen + Copyright (c) 2014-2017 Jon Lund Steffensen */ #include @@ -39,50 +39,11 @@ typedef struct { GMainLoop *loop; int available; + int error; location_t location; } get_location_data_t; -int -location_geoclue2_init(void *state) -{ -#if !GLIB_CHECK_VERSION(2, 35, 0) - g_type_init(); -#endif - return 0; -} - -int -location_geoclue2_start(void *state) -{ - return 0; -} - -void -location_geoclue2_free(void *state) -{ -} - -void -location_geoclue2_print_help(FILE *f) -{ - fputs(_("Use the location as discovered by a GeoClue2 provider.\n"), f); - fputs("\n", f); - - fprintf(f, _("NOTE: currently Redshift doesn't recheck %s once started,\n" - "which means it has to be restarted to take notice after travel.\n"), - "GeoClue2"); - fputs("\n", f); -} - -int -location_geoclue2_set_option(void *state, - const char *key, const char *value) -{ - fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); - return -1; -} - /* Handle position change callbacks */ static void geoclue_client_signal_cb(GDBusProxy *client, gchar *sender_name, @@ -267,16 +228,63 @@ on_name_vanished(GDBusConnection *connection, const gchar *name, get_location_data_t *data = (get_location_data_t *)user_data; g_fprintf(stderr, _("Unable to connect to GeoClue.\n")); + data->error = 1; g_main_loop_quit(data->loop); } int -location_geoclue2_get_location(void *state, - location_t *location) +location_geoclue2_init(void *state) +{ +#if !GLIB_CHECK_VERSION(2, 35, 0) + g_type_init(); +#endif + return 0; +} + +int +location_geoclue2_start(void *state) +{ + return 0; +} + +void +location_geoclue2_free(void *state) +{ +} + +void +location_geoclue2_print_help(FILE *f) +{ + fputs(_("Use the location as discovered by a GeoClue2 provider.\n"), f); + fputs("\n", f); + + fprintf(f, _("NOTE: currently Redshift doesn't recheck %s once started,\n" + "which means it has to be restarted to take notice after travel.\n"), + "GeoClue2"); + fputs("\n", f); +} + +int +location_geoclue2_set_option(void *state, + const char *key, const char *value) +{ + fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); + return -1; +} + +int +location_geoclue2_get_fd(void *state) +{ + return -1; +} + +int +location_geoclue2_handle(void *state, location_t *location, int *available) { get_location_data_t data; data.available = 0; + data.error = 0; guint watcher_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, "org.freedesktop.GeoClue2", @@ -289,8 +297,9 @@ location_geoclue2_get_location(void *state, g_bus_unwatch_name(watcher_id); - if (!data.available) return -1; + if (data.error) return -1; + *available = data.available; *location = data.location; return 0; -- cgit v1.2.3-70-g09d2 From 00e2d538e8774ab9e3cf1ec220ac1c76ecdb585e Mon Sep 17 00:00:00 2001 From: Jon Lund Steffensen Date: Wed, 16 Aug 2017 22:28:53 -0700 Subject: geoclue2: Update continuously --- src/location-geoclue2.c | 280 ++++++++++++++++++++++++++++++++---------------- src/location-geoclue2.h | 26 +++-- src/redshift.c | 3 + 3 files changed, 209 insertions(+), 100 deletions(-) (limited to 'src/location-geoclue2.c') diff --git a/src/location-geoclue2.c b/src/location-geoclue2.c index ab57535..9641507 100644 --- a/src/location-geoclue2.c +++ b/src/location-geoclue2.c @@ -26,6 +26,7 @@ #include "location-geoclue2.h" #include "redshift.h" +#include "pipeutils.h" #ifdef ENABLE_NLS # include @@ -35,22 +36,26 @@ #endif -typedef struct { - GMainLoop *loop; +/* Indicate an unrecoverable error during GeoClue2 communication. */ +static void +mark_error(location_geoclue2_state_t *state) +{ + g_mutex_lock(&state->lock); - int available; - int error; - location_t location; -} get_location_data_t; + state->error = 1; + g_mutex_unlock(&state->lock); + + pipeutils_signal(state->pipe_fd_write); +} /* Handle position change callbacks */ static void geoclue_client_signal_cb(GDBusProxy *client, gchar *sender_name, gchar *signal_name, GVariant *parameters, - gpointer *user_data) + gpointer user_data) { - get_location_data_t *data = (get_location_data_t *)user_data; + location_geoclue2_state_t *state = user_data; /* Only handle LocationUpdated signals */ if (g_strcmp0(signal_name, "LocationUpdated") != 0) { @@ -63,34 +68,38 @@ geoclue_client_signal_cb(GDBusProxy *client, gchar *sender_name, /* Obtain location */ GError *error = NULL; - GDBusProxy *location = - g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.freedesktop.GeoClue2", - location_path, - "org.freedesktop.GeoClue2.Location", - NULL, &error); + GDBusProxy *location = g_dbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.GeoClue2", + location_path, + "org.freedesktop.GeoClue2.Location", + NULL, &error); if (location == NULL) { g_printerr(_("Unable to obtain location: %s.\n"), error->message); g_error_free(error); + mark_error(state); return; } + g_mutex_lock(&state->lock); + /* Read location properties */ - GVariant *lat_v = g_dbus_proxy_get_cached_property(location, - "Latitude"); - data->location.lat = g_variant_get_double(lat_v); + GVariant *lat_v = g_dbus_proxy_get_cached_property( + location, "Latitude"); + state->latitude = g_variant_get_double(lat_v); - GVariant *lon_v = g_dbus_proxy_get_cached_property(location, - "Longitude"); - data->location.lon = g_variant_get_double(lon_v); + GVariant *lon_v = g_dbus_proxy_get_cached_property( + location, "Longitude"); + state->longitude = g_variant_get_double(lon_v); - data->available = 1; + state->available = 1; - /* Return from main loop */ - g_main_loop_quit(data->loop); + g_mutex_unlock(&state->lock); + + pipeutils_signal(state->pipe_fd_write); } /* Callback when GeoClue name appears on the bus */ @@ -98,22 +107,23 @@ static void on_name_appeared(GDBusConnection *conn, const gchar *name, const gchar *name_owner, gpointer user_data) { - get_location_data_t *data = (get_location_data_t *)user_data; + location_geoclue2_state_t *state = user_data; /* Obtain GeoClue Manager */ GError *error = NULL; - GDBusProxy *geoclue_manager = - g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.freedesktop.GeoClue2", - "/org/freedesktop/GeoClue2/Manager", - "org.freedesktop.GeoClue2.Manager", - NULL, &error); + GDBusProxy *geoclue_manager = g_dbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.GeoClue2", + "/org/freedesktop/GeoClue2/Manager", + "org.freedesktop.GeoClue2.Manager", + NULL, &error); if (geoclue_manager == NULL) { g_printerr(_("Unable to obtain GeoClue Manager: %s.\n"), error->message); g_error_free(error); + mark_error(state); return; } @@ -130,6 +140,7 @@ on_name_appeared(GDBusConnection *conn, const gchar *name, error->message); g_error_free(error); g_object_unref(geoclue_manager); + mark_error(state); return; } @@ -138,20 +149,21 @@ on_name_appeared(GDBusConnection *conn, const gchar *name, /* Obtain GeoClue client */ error = NULL; - GDBusProxy *geoclue_client = - g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.freedesktop.GeoClue2", - client_path, - "org.freedesktop.GeoClue2.Client", - NULL, &error); + GDBusProxy *geoclue_client = g_dbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.GeoClue2", + client_path, + "org.freedesktop.GeoClue2.Client", + NULL, &error); if (geoclue_client == NULL) { g_printerr(_("Unable to obtain GeoClue Client: %s.\n"), error->message); g_error_free(error); g_variant_unref(client_path_v); g_object_unref(geoclue_manager); + mark_error(state); return; } @@ -159,15 +171,15 @@ on_name_appeared(GDBusConnection *conn, const gchar *name, /* Set desktop id (basename of the .desktop file) */ error = NULL; - GVariant *ret_v = - g_dbus_proxy_call_sync(geoclue_client, - "org.freedesktop.DBus.Properties.Set", - g_variant_new("(ssv)", - "org.freedesktop.GeoClue2.Client", - "DesktopId", - g_variant_new("s", "redshift")), - G_DBUS_CALL_FLAGS_NONE, - -1, NULL, &error); + GVariant *ret_v = g_dbus_proxy_call_sync( + geoclue_client, + "org.freedesktop.DBus.Properties.Set", + g_variant_new("(ssv)", + "org.freedesktop.GeoClue2.Client", + "DesktopId", + g_variant_new("s", "redshift")), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &error); if (ret_v == NULL) { /* Ignore this error for now. The property is not available in early versions of GeoClue2. */ @@ -177,20 +189,22 @@ on_name_appeared(GDBusConnection *conn, const gchar *name, /* Set distance threshold */ error = NULL; - ret_v = g_dbus_proxy_call_sync(geoclue_client, - "org.freedesktop.DBus.Properties.Set", - g_variant_new("(ssv)", - "org.freedesktop.GeoClue2.Client", - "DistanceThreshold", - g_variant_new("u", 50000)), - G_DBUS_CALL_FLAGS_NONE, - -1, NULL, &error); + ret_v = g_dbus_proxy_call_sync( + geoclue_client, + "org.freedesktop.DBus.Properties.Set", + g_variant_new("(ssv)", + "org.freedesktop.GeoClue2.Client", + "DistanceThreshold", + g_variant_new("u", 50000)), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &error); if (ret_v == NULL) { g_printerr(_("Unable to set distance threshold: %s.\n"), error->message); g_error_free(error); g_object_unref(geoclue_client); g_object_unref(geoclue_manager); + mark_error(state); return; } @@ -199,7 +213,7 @@ on_name_appeared(GDBusConnection *conn, const gchar *name, /* Attach signal callback to client */ g_signal_connect(geoclue_client, "g-signal", G_CALLBACK(geoclue_client_signal_cb), - data); + user_data); /* Start GeoClue client */ error = NULL; @@ -214,6 +228,7 @@ on_name_appeared(GDBusConnection *conn, const gchar *name, g_error_free(error); g_object_unref(geoclue_client); g_object_unref(geoclue_manager); + mark_error(state); return; } @@ -225,16 +240,70 @@ static void on_name_vanished(GDBusConnection *connection, const gchar *name, gpointer user_data) { - get_location_data_t *data = (get_location_data_t *)user_data; + location_geoclue2_state_t *state = user_data; + + g_mutex_lock(&state->lock); + + state->available = 0; + + g_mutex_unlock(&state->lock); + + pipeutils_signal(state->pipe_fd_write); +} + +/* Callback when the pipe to the main thread is closed. */ +static gboolean +on_pipe_closed(GIOChannel *channel, GIOCondition condition, gpointer user_data) +{ + location_geoclue2_state_t *state = user_data; + g_main_loop_quit(state->loop); + + return FALSE; +} + + +/* Run loop for location provider thread. */ +void * +run_geoclue2_loop(void *state_) +{ + location_geoclue2_state_t *state = state_; + + GMainContext *context = g_main_context_new(); + g_main_context_push_thread_default(context); + state->loop = g_main_loop_new(context, FALSE); + + guint watcher_id = g_bus_watch_name( + G_BUS_TYPE_SYSTEM, + "org.freedesktop.GeoClue2", + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + on_name_appeared, + on_name_vanished, + state, NULL); + + /* Listen for closure of pipe */ + GIOChannel *pipe_channel = g_io_channel_unix_new(state->pipe_fd_write); + GSource *pipe_source = g_io_create_watch( + pipe_channel, G_IO_IN | G_IO_HUP | G_IO_ERR); + g_source_set_callback( + pipe_source, (GSourceFunc)on_pipe_closed, state, NULL); + g_source_attach(pipe_source, context); + + g_main_loop_run(state->loop); + + g_source_unref(pipe_source); + g_io_channel_unref(pipe_channel); + close(state->pipe_fd_write); + + g_bus_unwatch_name(watcher_id); - g_fprintf(stderr, _("Unable to connect to GeoClue.\n")); - data->error = 1; + g_main_loop_unref(state->loop); + g_main_context_unref(context); - g_main_loop_quit(data->loop); + return NULL; } int -location_geoclue2_init(void *state) +location_geoclue2_init(location_geoclue2_state_t *state) { #if !GLIB_CHECK_VERSION(2, 35, 0) g_type_init(); @@ -243,64 +312,87 @@ location_geoclue2_init(void *state) } int -location_geoclue2_start(void *state) +location_geoclue2_start(location_geoclue2_state_t *state) { + state->pipe_fd_read = -1; + state->pipe_fd_write = -1; + + state->available = 0; + state->error = 0; + state->latitude = 0; + state->longitude = 0; + + int pipefds[2]; + int r = pipeutils_create_nonblocking(pipefds); + if (r < 0) { + fputs(_("Failed to start GeoClue2 provider!\n"), stderr); + return -1; + } + + state->pipe_fd_read = pipefds[0]; + state->pipe_fd_write = pipefds[1]; + + pipeutils_signal(state->pipe_fd_write); + + g_mutex_init(&state->lock); + state->thread = g_thread_new("geoclue2", run_geoclue2_loop, state); + return 0; } void -location_geoclue2_free(void *state) +location_geoclue2_free(location_geoclue2_state_t *state) { + if (state->pipe_fd_read != -1) { + close(state->pipe_fd_read); + } + + /* Closing the pipe should cause the thread to exit. */ + g_thread_join(state->thread); + state->thread = NULL; + + g_mutex_clear(&state->lock); } void location_geoclue2_print_help(FILE *f) { - fputs(_("Use the location as discovered by a GeoClue2 provider.\n"), f); - fputs("\n", f); - - fprintf(f, _("NOTE: currently Redshift doesn't recheck %s once started,\n" - "which means it has to be restarted to take notice after travel.\n"), - "GeoClue2"); + fputs(_("Use the location as discovered by a GeoClue2 provider.\n"), + f); fputs("\n", f); } int -location_geoclue2_set_option(void *state, - const char *key, const char *value) +location_geoclue2_set_option(location_geoclue2_state_t *state, + const char *key, const char *value) { fprintf(stderr, _("Unknown method parameter: `%s'.\n"), key); return -1; } int -location_geoclue2_get_fd(void *state) +location_geoclue2_get_fd(location_geoclue2_state_t *state) { - return -1; + return state->pipe_fd_read; } int -location_geoclue2_handle(void *state, location_t *location, int *available) +location_geoclue2_handle( + location_geoclue2_state_t *state, + location_t *location, int *available) { - get_location_data_t data; - data.available = 0; - data.error = 0; - - guint watcher_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, - "org.freedesktop.GeoClue2", - G_BUS_NAME_WATCHER_FLAGS_AUTO_START, - on_name_appeared, - on_name_vanished, - &data, NULL); - data.loop = g_main_loop_new(NULL, FALSE); - g_main_loop_run(data.loop); + pipeutils_handle_signal(state->pipe_fd_read); - g_bus_unwatch_name(watcher_id); + g_mutex_lock(&state->lock); + + int error = state->error; + location->lat = state->latitude; + location->lon = state->longitude; + *available = state->available; - if (data.error) return -1; + g_mutex_unlock(&state->lock); - *available = data.available; - *location = data.location; + if (error) return -1; return 0; } diff --git a/src/location-geoclue2.h b/src/location-geoclue2.h index 095d86f..2f04eea 100644 --- a/src/location-geoclue2.h +++ b/src/location-geoclue2.h @@ -22,19 +22,33 @@ #include +#include + #include "redshift.h" +typedef struct { + GMainLoop *loop; + GThread *thread; + GMutex lock; + int pipe_fd_read; + int pipe_fd_write; + int available; + int error; + float latitude; + float longitude; +} location_geoclue2_state_t; + -int location_geoclue2_init(void *state); -int location_geoclue2_start(void *state); -void location_geoclue2_free(void *state); +int location_geoclue2_init(location_geoclue2_state_t *state); +int location_geoclue2_start(location_geoclue2_state_t *state); +void location_geoclue2_free(location_geoclue2_state_t *state); void location_geoclue2_print_help(FILE *f); -int location_geoclue2_set_option(void *state, +int location_geoclue2_set_option(location_geoclue2_state_t *state, const char *key, const char *value); -int location_geoclue2_get_fd(void *state); -int location_geoclue2_handle(void *state, +int location_geoclue2_get_fd(location_geoclue2_state_t *state); +int location_geoclue2_handle(location_geoclue2_state_t *state, location_t *location, int *available); diff --git a/src/redshift.c b/src/redshift.c index e1b9593..32def0b 100644 --- a/src/redshift.c +++ b/src/redshift.c @@ -206,6 +206,9 @@ static const gamma_method_t gamma_methods[] = { /* Union of state data for location providers */ typedef union { location_manual_state_t manual; +#ifdef ENABLE_GEOCLUE2 + location_geoclue2_state_t geoclue2; +#endif #ifdef ENABLE_CORELOCATION location_corelocation_state_t corelocation; #endif -- cgit v1.2.3-70-g09d2