Skip to content

Commit

Permalink
Implement getting geolocation via geoclue/geocode-glib on Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
ilya-fedin authored and john-preston committed Feb 1, 2025
1 parent 97c4e79 commit 296df11
Showing 1 changed file with 177 additions and 2 deletions.
179 changes: 177 additions & 2 deletions Telegram/SourceFiles/platform/linux/current_geo_location_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,192 @@ For license and copyright information please follow this link:
#include "platform/linux/current_geo_location_linux.h"

#include "core/current_geo_location.h"
#include "base/platform/linux/base_linux_library.h"

#include <gio/gio.h>

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<void(Core::GeoLocation)> 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<void(Core::GeoLocation)> *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<void(Core::GeoAddress)> 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<void(Core::GeoAddress)> *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

0 comments on commit 296df11

Please sign in to comment.