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
5 changes: 5 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import("//flutter/common/config.gni")
import("//flutter/shell/platform/config.gni")
import("//flutter/shell/platform/glfw/config.gni")
import("//flutter/testing/testing.gni")

# Whether to build the dartdevc sdk, libraries, and source files
Expand Down Expand Up @@ -140,6 +141,10 @@ group("flutter") {
if (is_linux) {
public_deps +=
[ "//flutter/shell/platform/linux:flutter_linux_unittests" ]
if (build_glfw_shell) {
public_deps +=
[ "//flutter/shell/platform/glfw:flutter_glfw_unittests" ]
}
}

if (is_mac) {
Expand Down
3 changes: 3 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,9 @@ FILE: ../../../flutter/shell/platform/glfw/keyboard_hook_handler.h
FILE: ../../../flutter/shell/platform/glfw/platform_handler.cc
FILE: ../../../flutter/shell/platform/glfw/platform_handler.h
FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h
FILE: ../../../flutter/shell/platform/glfw/system_utils.cc
FILE: ../../../flutter/shell/platform/glfw/system_utils.h
FILE: ../../../flutter/shell/platform/glfw/system_utils_test.cc
FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc
FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h
FILE: ../../../flutter/shell/platform/linux/egl_utils.cc
Expand Down
20 changes: 20 additions & 0 deletions shell/platform/glfw/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//flutter/testing/testing.gni")

_public_headers = [ "public/flutter_glfw.h" ]

# Any files that are built by clients (client_wrapper code, library headers for
Expand All @@ -28,6 +30,8 @@ source_set("flutter_glfw_headers") {
}

source_set("flutter_glfw") {
public = [ "system_utils.h" ]

sources = [
"event_loop.cc",
"event_loop.h",
Expand All @@ -41,6 +45,7 @@ source_set("flutter_glfw") {
"keyboard_hook_handler.h",
"platform_handler.cc",
"platform_handler.h",
"system_utils.cc",
"text_input_plugin.cc",
"text_input_plugin.h",
]
Expand Down Expand Up @@ -71,6 +76,21 @@ source_set("flutter_glfw") {
}
}

test_fixtures("flutter_glfw_fixtures") {
fixtures = []
}

executable("flutter_glfw_unittests") {
testonly = true
sources = [ "system_utils_test.cc" ]
deps = [
":flutter_glfw",
":flutter_glfw_fixtures",
"//flutter/shell/platform/embedder:embedder_headers",
"//flutter/testing",
]
}

copy("publish_headers_glfw") {
sources = _public_headers
outputs = [ "$root_out_dir/{{source_file_part}}" ]
Expand Down
24 changes: 24 additions & 0 deletions shell/platform/glfw/flutter_glfw.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "flutter/shell/platform/glfw/key_event_handler.h"
#include "flutter/shell/platform/glfw/keyboard_hook_handler.h"
#include "flutter/shell/platform/glfw/platform_handler.h"
#include "flutter/shell/platform/glfw/system_utils.h"
#include "flutter/shell/platform/glfw/text_input_plugin.h"

// GLFW_TRUE & GLFW_FALSE are introduced since libglfw-3.3,
Expand Down Expand Up @@ -690,6 +691,27 @@ static bool RunFlutterEngine(
return true;
}

// Passes locale information to the Flutter engine.
static void SetUpLocales(FlutterDesktopEngineState* state) {
std::vector<flutter::LanguageInfo> languages =
flutter::GetPreferredLanguageInfo();
std::vector<FlutterLocale> flutter_locales =
flutter::ConvertToFlutterLocale(languages);
// Convert the locale list to the locale pointer list that must be provided.
std::vector<const FlutterLocale*> flutter_locale_list;
flutter_locale_list.reserve(flutter_locales.size());
std::transform(
flutter_locales.begin(), flutter_locales.end(),
std::back_inserter(flutter_locale_list),
[](const auto& arg) -> const auto* { return &arg; });
FlutterEngineResult result = FlutterEngineUpdateLocales(
state->flutter_engine, flutter_locale_list.data(),
flutter_locale_list.size());
if (result != kSuccess) {
std::cerr << "Failed to set up Flutter locales." << std::endl;
}
}

// Populates |state|'s helper object fields that are common to normal and
// headless mode.
//
Expand All @@ -713,6 +735,8 @@ static void SetUpCommonEngineState(FlutterDesktopEngineState* state,
// System channel handler.
state->platform_handler = std::make_unique<flutter::PlatformHandler>(
state->internal_plugin_registrar->messenger(), window);

SetUpLocales(state);
}

bool FlutterDesktopInit() {
Expand Down
154 changes: 154 additions & 0 deletions shell/platform/glfw/system_utils.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// 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/glfw/system_utils.h"

#include <cstdlib>
#include <sstream>

namespace flutter {

namespace {

const char* GetLocaleStringFromEnvironment() {
const char* retval;
retval = getenv("LANGUAGE");
if ((retval != NULL) && (retval[0] != '\0')) {
return retval;
}
retval = getenv("LC_ALL");
if ((retval != NULL) && (retval[0] != '\0')) {
return retval;
}
retval = getenv("LC_MESSAGES");
if ((retval != NULL) && (retval[0] != '\0')) {
return retval;
}
retval = getenv("LANG");
if ((retval != NULL) && (retval[0] != '\0')) {
return retval;
}

return NULL;
}

// The least specific to most specific components of a locale.
enum Component {
kCodeset = 1 << 0,
kTerritory = 1 << 1,
kModifier = 1 << 2,
};

// Construct a mask indicating which of the components in |info| are set.
int ComputeVariantMask(const LanguageInfo& info) {
int mask = 0;
if (!info.territory.empty()) {
mask |= kTerritory;
}
if (!info.codeset.empty()) {
mask |= kCodeset;
}
if (!info.modifier.empty()) {
mask |= kModifier;
}
return mask;
}

// Appends most specific to least specific variants of |info| to |languages|.
// For example, "de_DE@euro" would append "de_DE@euro", "de@euro", "de_DE",
// and "de".
void AppendLocaleVariants(std::vector<LanguageInfo>& languages,
LanguageInfo info) {
int mask = ComputeVariantMask(info);
for (int i = mask; i >= 0; --i) {
if ((i & ~mask) == 0) {
LanguageInfo variant;
variant.language = info.language;

if (i & kTerritory) {
variant.territory = info.territory;
}
if (i & kCodeset) {
variant.codeset = info.codeset;
}
if (i & kModifier) {
variant.modifier = info.modifier;
}
languages.push_back(variant);
}
}
}

// Parses a locale into its components.
LanguageInfo ParseLocale(const std::string& locale) {
// Locales are of the form "language[_territory][.codeset][@modifier]"
LanguageInfo result;
std::string::size_type end = locale.size();
std::string::size_type modifier_pos = locale.rfind('@');
if (modifier_pos != std::string::npos) {
result.modifier = locale.substr(modifier_pos + 1, end - modifier_pos - 1);
end = modifier_pos;
}

std::string::size_type codeset_pos = locale.rfind('.', end);
if (codeset_pos != std::string::npos) {
result.codeset = locale.substr(codeset_pos + 1, end - codeset_pos - 1);
end = codeset_pos;
}

std::string::size_type territory_pos = locale.rfind('_', end);
if (territory_pos != std::string::npos) {
result.territory =
locale.substr(territory_pos + 1, end - territory_pos - 1);
end = territory_pos;
}

result.language = locale.substr(0, end);

return result;
}

} // namespace

std::vector<LanguageInfo> GetPreferredLanguageInfo() {
const char* locale_string;
locale_string = GetLocaleStringFromEnvironment();
if (!locale_string || locale_string[0] == '\0') {
// This is the default locale if none is specified according to ISO C.
locale_string = "C";
}
std::istringstream locales_stream(locale_string);
std::vector<LanguageInfo> languages;
std::string s;
while (getline(locales_stream, s, ':')) {
LanguageInfo info = ParseLocale(s);
AppendLocaleVariants(languages, info);
}
return languages;
}

std::vector<FlutterLocale> ConvertToFlutterLocale(
const std::vector<LanguageInfo>& languages) {
std::vector<FlutterLocale> flutter_locales;
flutter_locales.reserve(languages.size());
for (const auto& info : languages) {
FlutterLocale locale = {};
locale.struct_size = sizeof(FlutterLocale);
locale.language_code = info.language.c_str();
if (!info.territory.empty()) {
locale.country_code = info.territory.c_str();
}
if (!info.codeset.empty()) {
locale.script_code = info.codeset.c_str();
}
if (!info.modifier.empty()) {
locale.variant_code = info.modifier.c_str();
}
flutter_locales.push_back(locale);
}

return flutter_locales;
}

} // namespace flutter
35 changes: 35 additions & 0 deletions shell/platform/glfw/system_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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_GLFW_SYSTEM_UTILS_H_
#define FLUTTER_SHELL_PLATFORM_GLFW_SYSTEM_UTILS_H_

#include <string>
#include <vector>

#include "flutter/shell/platform/embedder/embedder.h"

namespace flutter {

// Components of a system language/locale.
struct LanguageInfo {
std::string language;
std::string territory;
std::string codeset;
std::string modifier;
};

// Returns the list of user-preferred languages, in preference order,
// parsed into LanguageInfo structures.
std::vector<LanguageInfo> GetPreferredLanguageInfo();

// Converts a vector of LanguageInfo structs to a vector of FlutterLocale
// structs. |languages| must outlive the returned value, since the returned
// elements have pointers into it.
std::vector<FlutterLocale> ConvertToFlutterLocale(
const std::vector<LanguageInfo>& languages);

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_GLFW_SYSTEM_UTILS_H_
Loading