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 8 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 @@ -1294,6 +1294,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