forked from flutter/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Linux] read settings from XDG desktop portal if available (flutter#3…
…3100) * [Linux] read settings from XDG desktop portal if available Fixes: flutter/flutter#101438
- Loading branch information
Showing
6 changed files
with
444 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,262 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "flutter/shell/platform/linux/fl_settings_portal.h" | ||
|
||
#include <gio/gio.h> | ||
#include <glib.h> | ||
|
||
static constexpr char kPortalName[] = "org.freedesktop.portal.Desktop"; | ||
static constexpr char kPortalPath[] = "/org/freedesktop/portal/desktop"; | ||
static constexpr char pPortalSettings[] = "org.freedesktop.portal.Settings"; | ||
|
||
struct FlSetting { | ||
const gchar* ns; | ||
const gchar* key; | ||
const GVariantType* type; | ||
}; | ||
|
||
static constexpr char kXdgAppearance[] = "org.freedesktop.appearance"; | ||
static const FlSetting kColorScheme = { | ||
kXdgAppearance, | ||
"color-scheme", | ||
G_VARIANT_TYPE_UINT32, | ||
}; | ||
|
||
static constexpr char kGnomeDesktopInterface[] = "org.gnome.desktop.interface"; | ||
static const FlSetting kClockFormat = { | ||
kGnomeDesktopInterface, | ||
"clock-format", | ||
G_VARIANT_TYPE_STRING, | ||
}; | ||
static const FlSetting kGtkTheme = { | ||
kGnomeDesktopInterface, | ||
"gtk-theme", | ||
G_VARIANT_TYPE_STRING, | ||
}; | ||
static const FlSetting kTextScalingFactor = { | ||
kGnomeDesktopInterface, | ||
"text-scaling-factor", | ||
G_VARIANT_TYPE_DOUBLE, | ||
}; | ||
|
||
static const FlSetting all_settings[] = { | ||
kClockFormat, | ||
kColorScheme, | ||
kGtkTheme, | ||
kTextScalingFactor, | ||
}; | ||
|
||
static constexpr char kClockFormat12Hour[] = "12h"; | ||
static constexpr char kGtkThemeDarkSuffix[] = "-dark"; | ||
|
||
typedef enum { DEFAULT, PREFER_DARK, PREFER_LIGHT } ColorScheme; | ||
|
||
struct _FlSettingsPortal { | ||
GObject parent_instance; | ||
|
||
GDBusProxy* dbus_proxy; | ||
GVariantDict* values; | ||
}; | ||
|
||
static void fl_settings_portal_iface_init(FlSettingsInterface* iface); | ||
|
||
G_DEFINE_TYPE_WITH_CODE(FlSettingsPortal, | ||
fl_settings_portal, | ||
G_TYPE_OBJECT, | ||
G_IMPLEMENT_INTERFACE(fl_settings_get_type(), | ||
fl_settings_portal_iface_init)) | ||
|
||
static gchar* format_key(const FlSetting* setting) { | ||
return g_strconcat(setting->ns, "::", setting->key, nullptr); | ||
} | ||
|
||
static gboolean get_value(FlSettingsPortal* portal, | ||
const FlSetting* setting, | ||
GVariant** value) { | ||
g_autofree const gchar* key = format_key(setting); | ||
*value = g_variant_dict_lookup_value(portal->values, key, setting->type); | ||
return *value != nullptr; | ||
} | ||
|
||
static void set_value(FlSettingsPortal* portal, | ||
const FlSetting* setting, | ||
GVariant* value) { | ||
g_autofree const gchar* key = format_key(setting); | ||
|
||
// ignore redundant changes from multiple XDG desktop portal backends | ||
g_autoptr(GVariant) old_value = | ||
g_variant_dict_lookup_value(portal->values, key, nullptr); | ||
if (old_value != nullptr && value != nullptr && | ||
g_variant_equal(old_value, value)) { | ||
return; | ||
} | ||
|
||
g_variant_dict_insert_value(portal->values, key, value); | ||
fl_settings_emit_changed(FL_SETTINGS(portal)); | ||
} | ||
|
||
// Based on | ||
// https://gitlab.gnome.org/GNOME/Initiatives/-/wikis/Dark-Style-Preference#other | ||
static gboolean settings_portal_read(GDBusProxy* proxy, | ||
const gchar* ns, | ||
const gchar* key, | ||
GVariant** out) { | ||
g_autoptr(GError) error = nullptr; | ||
g_autoptr(GVariant) value = | ||
g_dbus_proxy_call_sync(proxy, "Read", g_variant_new("(ss)", ns, key), | ||
G_DBUS_CALL_FLAGS_NONE, G_MAXINT, nullptr, &error); | ||
|
||
if (error) { | ||
if (error->domain == G_DBUS_ERROR && | ||
error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) { | ||
g_debug("XDG desktop portal unavailable: %s", error->message); | ||
return false; | ||
} | ||
|
||
if (error->domain == G_DBUS_ERROR && | ||
error->code == G_DBUS_ERROR_UNKNOWN_METHOD) { | ||
g_debug("XDG desktop portal settings unavailable: %s", error->message); | ||
return false; | ||
} | ||
|
||
g_critical("Failed to read XDG desktop portal settings: %s", | ||
error->message); | ||
return false; | ||
} | ||
|
||
g_autoptr(GVariant) child = nullptr; | ||
g_variant_get(value, "(v)", &child); | ||
g_variant_get(child, "v", out); | ||
|
||
return true; | ||
} | ||
|
||
static void settings_portal_changed_cb(GDBusProxy* proxy, | ||
const char* sender_name, | ||
const char* signal_name, | ||
GVariant* parameters, | ||
gpointer user_data) { | ||
FlSettingsPortal* portal = FL_SETTINGS_PORTAL(user_data); | ||
if (g_strcmp0(signal_name, "SettingChanged")) { | ||
return; | ||
} | ||
|
||
FlSetting setting; | ||
g_autoptr(GVariant) value = nullptr; | ||
g_variant_get(parameters, "(&s&sv)", &setting.ns, &setting.key, &value); | ||
set_value(portal, &setting, value); | ||
} | ||
|
||
static FlClockFormat fl_settings_portal_get_clock_format(FlSettings* settings) { | ||
FlSettingsPortal* self = FL_SETTINGS_PORTAL(settings); | ||
|
||
FlClockFormat clock_format = FL_CLOCK_FORMAT_24H; | ||
|
||
g_autoptr(GVariant) value = nullptr; | ||
if (get_value(self, &kClockFormat, &value)) { | ||
const gchar* clock_format_str = g_variant_get_string(value, nullptr); | ||
if (g_strcmp0(clock_format_str, kClockFormat12Hour) == 0) { | ||
clock_format = FL_CLOCK_FORMAT_12H; | ||
} | ||
} | ||
|
||
return clock_format; | ||
} | ||
|
||
static FlColorScheme fl_settings_portal_get_color_scheme(FlSettings* settings) { | ||
FlSettingsPortal* self = FL_SETTINGS_PORTAL(settings); | ||
|
||
FlColorScheme color_scheme = FL_COLOR_SCHEME_LIGHT; | ||
|
||
g_autoptr(GVariant) value = nullptr; | ||
if (get_value(self, &kColorScheme, &value)) { | ||
if (g_variant_get_uint32(value) == PREFER_DARK) { | ||
color_scheme = FL_COLOR_SCHEME_DARK; | ||
} | ||
} else if (get_value(self, &kGtkTheme, &value)) { | ||
const gchar* gtk_theme_str = g_variant_get_string(value, nullptr); | ||
if (g_str_has_suffix(gtk_theme_str, kGtkThemeDarkSuffix)) { | ||
color_scheme = FL_COLOR_SCHEME_DARK; | ||
} | ||
} | ||
|
||
return color_scheme; | ||
} | ||
|
||
static gdouble fl_settings_portal_get_text_scaling_factor( | ||
FlSettings* settings) { | ||
FlSettingsPortal* self = FL_SETTINGS_PORTAL(settings); | ||
|
||
gdouble scaling_factor = 1.0; | ||
|
||
g_autoptr(GVariant) value = nullptr; | ||
if (get_value(self, &kTextScalingFactor, &value)) { | ||
scaling_factor = g_variant_get_double(value); | ||
} | ||
|
||
return scaling_factor; | ||
} | ||
|
||
static void fl_settings_portal_dispose(GObject* object) { | ||
FlSettingsPortal* self = FL_SETTINGS_PORTAL(object); | ||
|
||
g_clear_object(&self->dbus_proxy); | ||
g_clear_pointer(&self->values, g_variant_dict_unref); | ||
|
||
G_OBJECT_CLASS(fl_settings_portal_parent_class)->dispose(object); | ||
} | ||
|
||
static void fl_settings_portal_class_init(FlSettingsPortalClass* klass) { | ||
GObjectClass* object_class = G_OBJECT_CLASS(klass); | ||
object_class->dispose = fl_settings_portal_dispose; | ||
} | ||
|
||
static void fl_settings_portal_iface_init(FlSettingsInterface* iface) { | ||
iface->get_clock_format = fl_settings_portal_get_clock_format; | ||
iface->get_color_scheme = fl_settings_portal_get_color_scheme; | ||
iface->get_text_scaling_factor = fl_settings_portal_get_text_scaling_factor; | ||
} | ||
|
||
static void fl_settings_portal_init(FlSettingsPortal* self) {} | ||
|
||
FlSettingsPortal* fl_settings_portal_new() { | ||
g_autoptr(GVariantDict) values = g_variant_dict_new(nullptr); | ||
return fl_settings_portal_new_with_values(values); | ||
} | ||
|
||
FlSettingsPortal* fl_settings_portal_new_with_values(GVariantDict* values) { | ||
g_return_val_if_fail(values != nullptr, nullptr); | ||
FlSettingsPortal* portal = | ||
FL_SETTINGS_PORTAL(g_object_new(fl_settings_portal_get_type(), nullptr)); | ||
portal->values = g_variant_dict_ref(values); | ||
return portal; | ||
} | ||
|
||
gboolean fl_settings_portal_start(FlSettingsPortal* self, GError** error) { | ||
g_return_val_if_fail(FL_IS_SETTINGS_PORTAL(self), false); | ||
g_return_val_if_fail(self->dbus_proxy == nullptr, false); | ||
|
||
self->dbus_proxy = g_dbus_proxy_new_for_bus_sync( | ||
G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, nullptr, kPortalName, | ||
kPortalPath, pPortalSettings, nullptr, error); | ||
|
||
if (self->dbus_proxy == nullptr) { | ||
return false; | ||
} | ||
|
||
for (const FlSetting setting : all_settings) { | ||
g_autoptr(GVariant) value = nullptr; | ||
if (settings_portal_read(self->dbus_proxy, setting.ns, setting.key, | ||
&value)) { | ||
set_value(self, &setting, value); | ||
} | ||
} | ||
|
||
g_signal_connect_object(self->dbus_proxy, "g-signal", | ||
G_CALLBACK(settings_portal_changed_cb), self, | ||
GConnectFlags(0)); | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_PORTAL_H_ | ||
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_PORTAL_H_ | ||
|
||
#include "flutter/shell/platform/linux/fl_settings.h" | ||
|
||
G_BEGIN_DECLS | ||
|
||
G_DECLARE_FINAL_TYPE(FlSettingsPortal, | ||
fl_settings_portal, | ||
FL, | ||
SETTINGS_PORTAL, | ||
GObject); | ||
|
||
/** | ||
* FlSettingsPortal: | ||
* #FlSettingsPortal reads settings from the XDG desktop portal. | ||
*/ | ||
|
||
/** | ||
* fl_settings_portal_new: | ||
* | ||
* Creates a new settings portal instance. | ||
* | ||
* Returns: a new #FlSettingsPortal. | ||
*/ | ||
FlSettingsPortal* fl_settings_portal_new(); | ||
|
||
/** | ||
* fl_settings_portal_new_with_values: | ||
* @values: (nullable): a #GVariantDict. | ||
* | ||
* Creates a new settings portal instance with initial values for testing. | ||
* | ||
* Returns: a new #FlSettingsPortal. | ||
*/ | ||
FlSettingsPortal* fl_settings_portal_new_with_values(GVariantDict* values); | ||
|
||
/** | ||
* fl_settings_portal_start: | ||
* @portal: an #FlSettingsPortal. | ||
* @error: (allow-none): #GError location to store the error occurring, or %NULL | ||
* | ||
* Reads the current settings and starts monitoring for changes in the desktop | ||
* portal settings. | ||
* | ||
* Returns: %TRUE on success, or %FALSE if the portal is not available. | ||
*/ | ||
gboolean fl_settings_portal_start(FlSettingsPortal* portal, GError** error); | ||
|
||
G_END_DECLS | ||
|
||
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_PORTAL_H_ |
Oops, something went wrong.