From 296df113e367aaabb2170434f919731803f24f19 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 31 Jan 2025 01:05:23 +0000 Subject: [PATCH] Implement getting geolocation via geoclue/geocode-glib on Linux --- .../linux/current_geo_location_linux.cpp | 179 +++++++++++++++++- 1 file changed, 177 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/current_geo_location_linux.cpp b/Telegram/SourceFiles/platform/linux/current_geo_location_linux.cpp index f87c343d3..7015af739 100644 --- a/Telegram/SourceFiles/platform/linux/current_geo_location_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/current_geo_location_linux.cpp @@ -8,17 +8,192 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/current_geo_location_linux.h" #include "core/current_geo_location.h" +#include "base/platform/linux/base_linux_library.h" + +#include namespace Platform { +namespace { + +typedef struct _GClueSimple GClueSimple; +typedef struct _GClueLocation GClueLocation; +typedef struct _GeocodeLocation GeocodeLocation; +typedef struct _GeocodeReverse GeocodeReverse; +typedef struct _GeocodePlace GeocodePlace; + +typedef enum { + GCLUE_ACCURACY_LEVEL_NONE = 0, + GCLUE_ACCURACY_LEVEL_COUNTRY = 1, + GCLUE_ACCURACY_LEVEL_CITY = 4, + GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD = 5, + GCLUE_ACCURACY_LEVEL_STREET = 6, + GCLUE_ACCURACY_LEVEL_EXACT = 8, +} GClueAccuracyLevel; + +void (*gclue_simple_new)( + const char *desktop_id, + GClueAccuracyLevel accuracy_level, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GClueSimple *(*gclue_simple_new_finish)(GAsyncResult *result, GError **error); +GClueLocation *(*gclue_simple_get_location)(GClueSimple *simple); + +gdouble (*gclue_location_get_latitude)(GClueLocation *loc); +gdouble (*gclue_location_get_longitude)(GClueLocation *loc); + +GeocodeLocation *(*geocode_location_new)( + gdouble latitude, + gdouble longitude, + gdouble accuracy); + +GeocodeReverse *(*geocode_reverse_new_for_location)( + GeocodeLocation *location); + +void (*geocode_reverse_resolve_async)( + GeocodeReverse *object, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GeocodePlace *(*geocode_reverse_resolve_finish)( + GeocodeReverse *object, + GAsyncResult *res, + GError **error); + +const char *(*geocode_place_get_street_address)(GeocodePlace *place); +const char *(*geocode_place_get_town)(GeocodePlace *place); +const char *(*geocode_place_get_country)(GeocodePlace *place); + +} // namespace void ResolveCurrentExactLocation(Fn callback) { - callback({}); + static const auto Inited = [] { + const auto lib = base::Platform::LoadLibrary( + "libgeoclue-2.so.0", + RTLD_NODELETE); + return lib + && LOAD_LIBRARY_SYMBOL(lib, gclue_simple_new) + && LOAD_LIBRARY_SYMBOL(lib, gclue_simple_new_finish) + && LOAD_LIBRARY_SYMBOL(lib, gclue_simple_get_location) + && LOAD_LIBRARY_SYMBOL(lib, gclue_location_get_latitude) + && LOAD_LIBRARY_SYMBOL(lib, gclue_location_get_longitude); + }(); + + if (!Inited) { + callback({}); + return; + } + + gclue_simple_new( + QGuiApplication::desktopFileName().toUtf8().constData(), + GCLUE_ACCURACY_LEVEL_EXACT, + nullptr, + GAsyncReadyCallback(+[]( + GObject *object, + GAsyncResult* res, + Fn *callback) { + const auto callbackGuard = gsl::finally([&] { + delete callback; + }); + + const auto simple = gclue_simple_new_finish(res, nullptr); + if (!simple) { + (*callback)({}); + return; + } + + const auto simpleGuard = gsl::finally([&] { + g_object_unref(simple); + }); + + const auto location = gclue_simple_get_location(simple); + + (*callback)({ + .point = { + gclue_location_get_latitude(location), + gclue_location_get_longitude(location), + }, + .accuracy = Core::GeoLocationAccuracy::Exact, + }); + }), + new Fn(callback)); } + void ResolveLocationAddress( const Core::GeoLocation &location, const QString &language, Fn callback) { - callback({}); + static const auto Inited = [] { + const auto lib = base::Platform::LoadLibrary( + "libgeocode-glib-2.so.0", + RTLD_NODELETE) ?: base::Platform::LoadLibrary( + "libgeocode-glib.so.0", + RTLD_NODELETE); + return lib + && LOAD_LIBRARY_SYMBOL(lib, geocode_location_new) + && LOAD_LIBRARY_SYMBOL(lib, geocode_reverse_new_for_location) + && LOAD_LIBRARY_SYMBOL(lib, geocode_reverse_resolve_async) + && LOAD_LIBRARY_SYMBOL(lib, geocode_reverse_resolve_finish) + && LOAD_LIBRARY_SYMBOL(lib, geocode_place_get_street_address) + && LOAD_LIBRARY_SYMBOL(lib, geocode_place_get_town) + && LOAD_LIBRARY_SYMBOL(lib, geocode_place_get_country); + }(); + + if (!Inited) { + callback({}); + return; + } + + geocode_reverse_resolve_async( + geocode_reverse_new_for_location(geocode_location_new( + location.point.x(), + location.point.y(), + -1)), + nullptr, + GAsyncReadyCallback(+[]( + GeocodeReverse *reverse, + GAsyncResult* res, + Fn *callback) { + const auto argsGuard = gsl::finally([&] { + delete callback; + g_object_unref(reverse); + }); + + const auto place = geocode_reverse_resolve_finish( + reverse, + res, + nullptr); + + if (!place) { + (*callback)({}); + return; + } + + const auto placeGuard = gsl::finally([&] { + g_object_unref(place); + }); + + const auto values = { + geocode_place_get_street_address(place), + geocode_place_get_town(place), + geocode_place_get_country(place), + }; + + QStringList checked; + for (const auto &value : values) { + if (value) { + const auto qt = QString::fromUtf8(value); + if (!qt.isEmpty()) { + checked.push_back(qt); + } + } + } + + (*callback)({ .name = checked.join(u", "_q) }); + }), + new Fn(callback)); } } // namespace Platform