diff --git a/meson.build b/meson.build index f81e5974..71b3c3c1 100644 --- a/meson.build +++ b/meson.build @@ -10,7 +10,7 @@ project( 'c_std=gnu89', 'warning_level=2', ], - meson_version: '>= 0.53.0', + meson_version: '>= 0.60.0', ) meson_version = meson.version() @@ -31,11 +31,13 @@ mm_includedir = get_option('includedir') mm_libdir = get_option('libdir') mm_sbindir = get_option('sbindir') mm_sysconfdir = get_option('sysconfdir') +mm_sharedstatedir = get_option('sharedstatedir') mm_pkgdatadir = mm_datadir / mm_name mm_pkgincludedir = mm_includedir / mm_name mm_pkglibdir = mm_libdir / mm_name mm_pkgsysconfdir = mm_sysconfdir / mm_name +mm_pkgsharedstatedir = mm_sharedstatedir / mm_name mm_glib_name = 'libmm-glib' mm_glib_pkgincludedir = mm_includedir / mm_glib_name diff --git a/src/meson.build b/src/meson.build index 04d199d1..78d8794d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -46,6 +46,7 @@ sources = files( 'mm-cbm-part.c', 'mm-charsets.c', 'mm-error-helpers.c', + 'mm-location-cache.c', 'mm-log.c', 'mm-log-object.c', 'mm-modem-helpers.c', @@ -80,11 +81,16 @@ if enable_systemd_journal private_deps += libsystemd_dep endif +c_args = [ + '-DPKGSTATEDIR="@0@"'.format(mm_pkgsharedstatedir), +] + libhelpers = static_library( 'helpers', sources: sources + enums_sources, include_directories: incs, dependencies: deps + private_deps, + c_args: c_args, ) libhelpers_dep = declare_dependency( @@ -506,6 +512,11 @@ install_data( install_dir: udev_rulesdir, ) +# empty directory for cached position +install_emptydir( + mm_pkgsharedstatedir, +) + if enable_tests subdir('tests') endif diff --git a/src/mm-location-cache.c b/src/mm-location-cache.c new file mode 100644 index 00000000..e27921ff --- /dev/null +++ b/src/mm-location-cache.c @@ -0,0 +1,241 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 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 General Public License for more details: + * + * Copyright (C) 2026 Andrey Skvortsov + */ + + +#include +#include + +#define _LIBMM_INSIDE_MM +#include + +#include "mm-location-cache.h" +#include "mm-log-object.h" + +#if !defined PKGSTATEDIR +# error PKGSTATEDIR is not defined +#endif + +#define LOCATION_STATE_FILE "location.ini" +#define LOCATION_LAST_POS_GROUP "last_position" +#define LOCATION_LAST_POS_LATITUDE_KEY "latitude" +#define LOCATION_LAST_POS_LONGITUDE_KEY "longitude" + +/*****************************************************************************/ + +static void log_object_iface_init (MMLogObjectInterface *iface); + +G_DEFINE_TYPE_EXTENDED (MMLocationCache, mm_location_cache, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) + +struct _MMLocationCachePrivate { + MMLocationGpsRaw *gps_raw; + gdouble last_latitude; + gdouble last_longitude; + gchar* filename; +}; + +/*****************************************************************************/ + +static gchar * +log_object_build_id (MMLogObject *_self) +{ + return g_strdup ("location-cache"); +} + +gboolean +mm_location_cache_load_from_file (MMLocationCache *self, + const gchar *file, + GError **error) +{ + g_autoptr(GKeyFile) key_file = g_key_file_new (); + gdouble lat; + gdouble lon; + + self->priv->last_latitude = MM_LOCATION_LATITUDE_UNKNOWN; + self->priv->last_longitude = MM_LOCATION_LONGITUDE_UNKNOWN; + + if (!g_key_file_load_from_file (key_file, file, G_KEY_FILE_NONE, error)) { + g_prefix_error (error, "Error loading cached location from %s: ", file); + return FALSE; + } + + lat = g_key_file_get_double (key_file, + LOCATION_LAST_POS_GROUP, + LOCATION_LAST_POS_LATITUDE_KEY, + error); + if (*error) { + g_prefix_error (error, "Error finding latitude in file: "); + return FALSE; + } + + lon = g_key_file_get_double (key_file, + LOCATION_LAST_POS_GROUP, + LOCATION_LAST_POS_LONGITUDE_KEY, + error); + if (*error) { + g_prefix_error (error, "Error finding longitude in file: "); + return FALSE; + } + + self->priv->last_latitude = lat; + self->priv->last_longitude = lon; + return TRUE; +} + +gboolean +mm_location_cache_load (MMLocationCache *self, + GError **error) +{ + return mm_location_cache_load_from_file (self, self->priv->filename, error); +} + +gboolean +mm_location_cache_save_to_file (MMLocationCache *self, + const gchar *file, + GError **error) +{ + g_autoptr(GKeyFile) key_file = g_key_file_new (); + + if ((self->priv->last_latitude == MM_LOCATION_LATITUDE_UNKNOWN) || + (self->priv->last_longitude == MM_LOCATION_LONGITUDE_UNKNOWN)) { + return TRUE; + } + + key_file = g_key_file_new (); + g_key_file_set_double (key_file, + LOCATION_LAST_POS_GROUP, + LOCATION_LAST_POS_LATITUDE_KEY, + self->priv->last_latitude); + g_key_file_set_double (key_file, + LOCATION_LAST_POS_GROUP, + LOCATION_LAST_POS_LONGITUDE_KEY, + self->priv->last_longitude); + + if (!g_key_file_save_to_file (key_file, file, error)) { + g_prefix_error (error, "Error saving cached location to %s", file); + return FALSE; + } + + return TRUE; +} + +gboolean +mm_location_cache_save (MMLocationCache *self, + GError **error) +{ + return mm_location_cache_save_to_file (self, self->priv->filename, error); +} + +void +mm_location_cache_set_filename (MMLocationCache *self, + const gchar *file) +{ + g_free (self->priv->filename); + self->priv->filename = g_strdup (file); +} + +void +mm_location_cache_update_from_lat_lon (MMLocationCache *self, + const double lat, + const double lon) +{ + if (lat != MM_LOCATION_LATITUDE_UNKNOWN && + lon != MM_LOCATION_LONGITUDE_UNKNOWN) { + self->priv->last_latitude = lat; + self->priv->last_longitude = lon; + } +} + +void +mm_location_cache_update_from_nmea (MMLocationCache *self, + const gchar *trace) +{ + gdouble lat; + gdouble lon; + + g_assert (self->priv->gps_raw != NULL); + if (mm_location_gps_raw_add_trace (self->priv->gps_raw, trace)) { + lat = mm_location_gps_raw_get_latitude (self->priv->gps_raw); + lon = mm_location_gps_raw_get_longitude (self->priv->gps_raw); + mm_location_cache_update_from_lat_lon (self, lat, lon); + } +} + +void +mm_location_cache_get_lat_lon (MMLocationCache *self, + double *lat, + double *lon) +{ + *lat = self->priv->last_latitude; + *lon = self->priv->last_longitude; +} + +MMLocationCache * +mm_location_cache_new (void) +{ + return MM_LOCATION_CACHE (g_object_new (MM_TYPE_LOCATION_CACHE, NULL)); +} + +static void +mm_location_cache_init (MMLocationCache *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_LOCATION_CACHE, MMLocationCachePrivate); + + self->priv->last_longitude = MM_LOCATION_LONGITUDE_UNKNOWN; + self->priv->last_latitude = MM_LOCATION_LATITUDE_UNKNOWN; + self->priv->gps_raw = mm_location_gps_raw_new (); + + self->priv->filename = g_build_path (G_DIR_SEPARATOR_S, PKGSTATEDIR, LOCATION_STATE_FILE, NULL); +} + +static void +finalize (GObject *object) +{ + MMLocationCache *self = MM_LOCATION_CACHE (object); + + g_object_unref (self->priv->gps_raw); + g_free (self->priv->filename); + + G_OBJECT_CLASS (mm_location_cache_parent_class)->finalize (object); +} + +static void +dispose (GObject *object) +{ + g_autoptr(GError) error = NULL; + MMLocationCache *self = MM_LOCATION_CACHE (object); + + if (!mm_location_cache_save (self, &error)) + mm_obj_warn (self, "%s", error->message); + + G_OBJECT_CLASS (mm_location_cache_parent_class)->dispose (object); +} + +static void +log_object_iface_init (MMLogObjectInterface *iface) +{ + iface->build_id = log_object_build_id; +} + +static void +mm_location_cache_class_init (MMLocationCacheClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMLocationCachePrivate)); + + object_class->finalize = finalize; + object_class->dispose = dispose; +} diff --git a/src/mm-location-cache.h b/src/mm-location-cache.h new file mode 100644 index 00000000..6c8ed291 --- /dev/null +++ b/src/mm-location-cache.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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 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 General Public License for more details: + * + * Copyright (C) 2026 Andrey Skvortsov + */ + +#ifndef MM_LOCATION_CACHE_H +#define MM_LOCATION_CACHE_H + +#include +#include + +#define MM_TYPE_LOCATION_CACHE (mm_location_cache_get_type ()) +#define MM_LOCATION_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_LOCATION_CACHE, MMLocationCache)) +#define MM_LOCATION_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_LOCATION_CACHE, MMLocationCacheClass)) +#define MM_IS_LOCATION_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_LOCATION_CACHE)) +#define MM_IS_LOCATION_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_LOCATION_CACHE)) +#define MM_LOCATION_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_LOCATION_CACHE, MMLocationCacheClass)) + +typedef struct _MMLocationCache MMLocationCache; +typedef struct _MMLocationCacheClass MMLocationCacheClass; +typedef struct _MMLocationCachePrivate MMLocationCachePrivate; + +struct _MMLocationCache { + GObject parent; + MMLocationCachePrivate *priv; +}; + +struct _MMLocationCacheClass { + GObjectClass parent; +}; + +GType mm_location_cache_get_type (void); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocationCache, g_object_unref) + +MMLocationCache *mm_location_cache_new (void); + +gboolean mm_location_cache_load_from_file (MMLocationCache *self, const gchar *file, GError **error); +gboolean mm_location_cache_load (MMLocationCache *self, GError **error); +gboolean mm_location_cache_save_to_file (MMLocationCache *self, const gchar *file, GError **error); +gboolean mm_location_cache_save (MMLocationCache *self, GError **error); +void mm_location_cache_set_filename (MMLocationCache *self, const gchar *file); +void mm_location_cache_update_from_lat_lon (MMLocationCache *self, const double lat, const double lon); +void mm_location_cache_update_from_nmea (MMLocationCache *self, const gchar *nmea); +void mm_location_cache_get_lat_lon (MMLocationCache *self, double *lat, double *lon); + +#endif /* MM_LOCATION_CACHE_H */ +