Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2134,6 +2134,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_renderer_wayland.cc
FILE: ../../../flutter/shell/platform/linux/fl_renderer_wayland.h
FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.cc
FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.h
FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin.cc
FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin.h
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_private.h
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc
Expand Down
1 change: 1 addition & 0 deletions shell/platform/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ source_set("flutter_linux_sources") {
"fl_renderer_headless.cc",
"fl_renderer_wayland.cc",
"fl_renderer_x11.cc",
"fl_settings_plugin.cc",
"fl_standard_message_codec.cc",
"fl_standard_method_codec.cc",
"fl_string_codec.cc",
Expand Down
10 changes: 8 additions & 2 deletions shell/platform/linux/fl_basic_message_channel_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,17 @@ TEST(FlBasicMessageChannelTest, SendMessageWithoutResponse) {
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);

bool called = false;
FlutterEngineSendPlatformMessageFnPtr old_handler =
embedder_api->SendPlatformMessage;
embedder_api->SendPlatformMessage = MOCK_ENGINE_PROC(
SendPlatformMessage,
([&called](auto engine, const FlutterPlatformMessage* message) {
([&called, old_handler](auto engine,
const FlutterPlatformMessage* message) {
if (strcmp(message->channel, "test") != 0) {
return old_handler(engine, message);
}

called = true;
EXPECT_STREQ(message->channel, "test");
EXPECT_EQ(message->response_handle, nullptr);

g_autoptr(GBytes) message_bytes =
Expand Down
6 changes: 6 additions & 0 deletions shell/platform/linux/fl_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "flutter/shell/platform/linux/fl_plugin_registrar_private.h"
#include "flutter/shell/platform/linux/fl_renderer.h"
#include "flutter/shell/platform/linux/fl_renderer_headless.h"
#include "flutter/shell/platform/linux/fl_settings_plugin.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"

static constexpr int kMicrosecondsPerNanosecond = 1000;
Expand All @@ -30,6 +31,7 @@ struct _FlEngine {
FlDartProject* project;
FlRenderer* renderer;
FlBinaryMessenger* binary_messenger;
FlSettingsPlugin* settings_plugin;
FlutterEngineAOTData aot_data;
FLUTTER_API_SYMBOL(FlutterEngine) engine;
FlutterEngineProcTable embedder_api;
Expand Down Expand Up @@ -315,6 +317,7 @@ static void fl_engine_dispose(GObject* object) {
g_clear_object(&self->project);
g_clear_object(&self->renderer);
g_clear_object(&self->binary_messenger);
g_clear_object(&self->settings_plugin);

if (self->platform_message_handler_destroy_notify) {
self->platform_message_handler_destroy_notify(
Expand Down Expand Up @@ -434,6 +437,9 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {

setup_locales(self);

self->settings_plugin = fl_settings_plugin_new(self->binary_messenger);
fl_settings_plugin_start(self->settings_plugin);

return TRUE;
}

Expand Down
60 changes: 58 additions & 2 deletions shell/platform/linux/fl_engine_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
#include "flutter/shell/platform/linux/testing/fl_test.h"

// Checks sending window metrics events works.
Expand Down Expand Up @@ -76,12 +77,18 @@ TEST(FlEngineTest, PlatformMessage) {
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);

bool called = false;
FlutterEngineSendPlatformMessageFnPtr old_handler =
embedder_api->SendPlatformMessage;
embedder_api->SendPlatformMessage = MOCK_ENGINE_PROC(
SendPlatformMessage,
([&called](auto engine, const FlutterPlatformMessage* message) {
([&called, old_handler](auto engine,
const FlutterPlatformMessage* message) {
if (strcmp(message->channel, "test") != 0) {
return old_handler(engine, message);
}

called = true;

EXPECT_STREQ(message->channel, "test");
EXPECT_EQ(message->message_size, static_cast<size_t>(4));
EXPECT_EQ(message->message[0], 't');
EXPECT_EQ(message->message[1], 'e');
Expand Down Expand Up @@ -137,3 +144,52 @@ TEST(FlEngineTest, PlatformMessageResponse) {

EXPECT_TRUE(called);
}

// Checks settings plugin sends settings on startup.
TEST(FlEngineTest, SettingsPlugin) {
g_autoptr(FlEngine) engine = make_mock_engine();
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);

bool called = false;
embedder_api->SendPlatformMessage = MOCK_ENGINE_PROC(
SendPlatformMessage,
([&called](auto engine, const FlutterPlatformMessage* message) {
called = true;

EXPECT_STREQ(message->channel, "flutter/settings");

g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
g_autoptr(GBytes) data =
g_bytes_new(message->message, message->message_size);
g_autoptr(GError) error = nullptr;
g_autoptr(FlValue) settings = fl_message_codec_decode_message(
FL_MESSAGE_CODEC(codec), data, &error);
EXPECT_NE(settings, nullptr);
EXPECT_EQ(error, nullptr);
g_printerr("%s\n", fl_value_to_string(settings));

g_autoptr(FlValue) text_scale_factor =
fl_value_lookup_string(settings, "textScaleFactor");
EXPECT_NE(text_scale_factor, nullptr);
EXPECT_EQ(fl_value_get_type(text_scale_factor), FL_VALUE_TYPE_FLOAT);

g_autoptr(FlValue) always_use_24hr_format =
fl_value_lookup_string(settings, "alwaysUse24HourFormat");
EXPECT_NE(always_use_24hr_format, nullptr);
EXPECT_EQ(fl_value_get_type(always_use_24hr_format),
FL_VALUE_TYPE_BOOL);

g_autoptr(FlValue) platform_brightness =
fl_value_lookup_string(settings, "platformBrightness");
EXPECT_NE(platform_brightness, nullptr);
EXPECT_EQ(fl_value_get_type(platform_brightness), FL_VALUE_TYPE_STRING);

return kSuccess;
}));

g_autoptr(GError) error = nullptr;
EXPECT_TRUE(fl_engine_start(engine, &error));
EXPECT_EQ(error, nullptr);

EXPECT_TRUE(called);
}
120 changes: 120 additions & 0 deletions shell/platform/linux/fl_settings_plugin.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 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_plugin.h"

#include <cstring>

#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"

static constexpr char kChannelName[] = "flutter/settings";
static constexpr char kTextScaleFactorKey[] = "textScaleFactor";
static constexpr char kAlwaysUse24HourFormatKey[] = "alwaysUse24HourFormat";
static constexpr char kPlatformBrightnessKey[] = "platformBrightness";
static constexpr char kPlatformBrightnessLight[] = "light";
static constexpr char kPlatformBrightnessDark[] = "dark";

static constexpr char kDesktopInterfaceSchema[] = "org.gnome.desktop.interface";
static constexpr char kDesktopGtkThemeKey[] = "gtk-theme";
static constexpr char kDesktopTextScalingFactorKey[] = "text-scaling-factor";
static constexpr char kDesktopClockFormatKey[] = "clock-format";
static constexpr char kClockFormat24Hour[] = "24h";

struct _FlSettingsPlugin {
GObject parent_instance;

FlBasicMessageChannel* channel;

GSettings* interface_settings;
};

G_DEFINE_TYPE(FlSettingsPlugin, fl_settings_plugin, G_TYPE_OBJECT)

// Sends the current settings to the Flutter engine.
static void update_settings(FlSettingsPlugin* self) {
gdouble scaling_factor = 1.0;
gboolean always_use_24hr = FALSE;
const gchar* platform_brightness = kPlatformBrightnessLight;

if (self->interface_settings != nullptr) {
scaling_factor = g_settings_get_double(self->interface_settings,
kDesktopTextScalingFactorKey);
g_autofree gchar* clock_format =
g_settings_get_string(self->interface_settings, kDesktopClockFormatKey);
always_use_24hr = g_strcmp0(clock_format, kClockFormat24Hour) == 0;

// GTK doesn't have a specific flag for dark themes, so we have some
// hard-coded themes for Ubuntu (Yaru) and GNOME (Adwaita).
g_autofree gchar* gtk_theme =
g_settings_get_string(self->interface_settings, kDesktopGtkThemeKey);
if (g_strcmp0(gtk_theme, "Yaru-dark") == 0 ||
g_strcmp0(gtk_theme, "Adwaita-dark") == 0) {
platform_brightness = kPlatformBrightnessDark;
}
}

g_autoptr(FlValue) message = fl_value_new_map();
fl_value_set_string_take(message, kTextScaleFactorKey,
fl_value_new_float(scaling_factor));
fl_value_set_string_take(message, kAlwaysUse24HourFormatKey,
fl_value_new_bool(always_use_24hr));
fl_value_set_string_take(message, kPlatformBrightnessKey,
fl_value_new_string(platform_brightness));
fl_basic_message_channel_send(self->channel, message, nullptr, nullptr,
nullptr);
}

static void fl_settings_plugin_dispose(GObject* object) {
FlSettingsPlugin* self = FL_SETTINGS_PLUGIN(object);

g_clear_object(&self->channel);
g_clear_object(&self->interface_settings);

G_OBJECT_CLASS(fl_settings_plugin_parent_class)->dispose(object);
}

static void fl_settings_plugin_class_init(FlSettingsPluginClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_settings_plugin_dispose;
}

static void fl_settings_plugin_init(FlSettingsPlugin* self) {}

FlSettingsPlugin* fl_settings_plugin_new(FlBinaryMessenger* messenger) {
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);

FlSettingsPlugin* self =
FL_SETTINGS_PLUGIN(g_object_new(fl_settings_plugin_get_type(), nullptr));

g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
self->channel = fl_basic_message_channel_new(messenger, kChannelName,
FL_MESSAGE_CODEC(codec));

return self;
}

void fl_settings_plugin_start(FlSettingsPlugin* self) {
g_return_if_fail(FL_IS_SETTINGS_PLUGIN(self));

// If we are on GNOME, get settings from GSettings.
GSettingsSchemaSource* source = g_settings_schema_source_get_default();
if (source != nullptr) {
g_autoptr(GSettingsSchema) schema =
g_settings_schema_source_lookup(source, kDesktopInterfaceSchema, FALSE);
if (schema != nullptr) {
self->interface_settings = g_settings_new_full(schema, nullptr, nullptr);
g_signal_connect_object(
self->interface_settings, "changed::text-scaling-factor",
G_CALLBACK(update_settings), self, G_CONNECT_SWAPPED);
g_signal_connect_object(self->interface_settings, "changed::clock-format",
G_CALLBACK(update_settings), self,
G_CONNECT_SWAPPED);
g_signal_connect_object(self->interface_settings, "changed::gtk-theme",
G_CALLBACK(update_settings), self,
G_CONNECT_SWAPPED);
}
}

update_settings(self);
}
45 changes: 45 additions & 0 deletions shell/platform/linux/fl_settings_plugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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_PLUGIN_H_
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_PLUGIN_H_

#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"

G_BEGIN_DECLS

G_DECLARE_FINAL_TYPE(FlSettingsPlugin,
fl_settings_plugin,
FL,
SETTINGS_PLUGIN,
GObject);

/**
* FlSettingsPlugin:
*
* #FlSettingsPlugin is a plugin that implements the Flutter user settings
* channel.
*/

/**
* fl_settings_plugin_new:
* @messenger: an #FlBinaryMessenger
*
* Creates a new plugin that sends user settings to the Flutter engine.
*
* Returns: a new #FlSettingsPlugin
*/
FlSettingsPlugin* fl_settings_plugin_new(FlBinaryMessenger* messenger);

/**
* fl_settings_plugin_start:
* @self: an #FlSettingsPlugin.
*
* Sends the current settings to the engine and updates when they change.
*/
void fl_settings_plugin_start(FlSettingsPlugin* plugin);

G_END_DECLS

#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_PLUGIN_H_