| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/linux/linux_ui_factory.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/environment.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/nix/xdg_util.h" |
| #include "base/no_destructor.h" |
| #include "base/strings/string_util.h" |
| #include "build/chromecast_buildflags.h" |
| #include "ui/base/buildflags.h" |
| #include "ui/base/ui_base_switches.h" |
| #include "ui/color/system_theme.h" |
| #include "ui/linux/fallback_linux_ui.h" |
| #include "ui/linux/linux_ui.h" |
| #include "ui/linux/linux_ui_delegate.h" |
| |
| #if BUILDFLAG(USE_GTK) |
| #include "ui/gtk/gtk_ui_factory.h" |
| #endif |
| #if BUILDFLAG(USE_QT) |
| #include "ui/qt/qt_ui.h" |
| #endif |
| |
| #if !BUILDFLAG(IS_CASTOS) |
| #include "ui/shell_dialogs/shell_dialog_linux.h" |
| #endif |
| |
| namespace ui { |
| |
| namespace { |
| |
| std::vector<raw_ptr<LinuxUiTheme, VectorExperimental>>& GetLinuxUiThemesImpl() { |
| static base::NoDestructor< |
| std::vector<raw_ptr<LinuxUiTheme, VectorExperimental>>> |
| themes; |
| return *themes; |
| } |
| |
| template <typename CreateLinuxUiFunc> |
| LinuxUiAndTheme* GetLinuxUi(CreateLinuxUiFunc&& create_linux_ui) { |
| // LinuxUi creation will fail without a delegate. |
| if (!ui::LinuxUiDelegate::GetInstance()) { |
| return nullptr; |
| } |
| |
| static base::NoDestructor<std::optional<std::unique_ptr<LinuxUiAndTheme>>> |
| linux_ui; |
| |
| if (linux_ui->has_value()) { |
| return linux_ui->value().get(); |
| } |
| |
| linux_ui->emplace(create_linux_ui()); |
| LinuxUiAndTheme* ui_and_theme = linux_ui->value().get(); |
| if (!ui_and_theme) { |
| return nullptr; |
| } |
| |
| // This function is reentrant: it may be called while Initialize() is running. |
| // In that case, return `linux_ui`. However, if Initialize() fails, |
| // `linux_ui` is reset and future calls will not try to initialize again. |
| if (!ui_and_theme->Initialize()) { |
| linux_ui->value().reset(); |
| return nullptr; |
| } |
| |
| GetLinuxUiThemesImpl().push_back(ui_and_theme); |
| return ui_and_theme; |
| } |
| |
| LinuxUiAndTheme* GetGtkUi() { |
| auto create_gtk_ui = []() { |
| #if BUILDFLAG(USE_GTK) |
| return BuildGtkUi(); |
| #else |
| return nullptr; |
| #endif |
| }; |
| return GetLinuxUi(create_gtk_ui); |
| } |
| |
| LinuxUiAndTheme* GetQtUi() { |
| auto create_qt_ui = []() { |
| #if BUILDFLAG(USE_QT) |
| return qt::CreateQtUi(GetGtkUi()); |
| #else |
| return nullptr; |
| #endif |
| }; |
| return GetLinuxUi(create_qt_ui); |
| } |
| |
| std::unique_ptr<LinuxUiAndTheme> CreateFallbackUi() { |
| return std::make_unique<FallbackLinuxUi>(); |
| } |
| |
| LinuxUiAndTheme* GetFallbackUi() { |
| static LinuxUiAndTheme* fallback_ui = CreateFallbackUi().release(); |
| return fallback_ui; |
| } |
| |
| LinuxUiAndTheme* GetDefaultLinuxUiAndTheme() { |
| auto* cmd_line = base::CommandLine::ForCurrentProcess(); |
| std::string ui_toolkit = base::ToLowerASCII( |
| cmd_line->GetSwitchValueASCII(switches::kUiToolkitFlag)); |
| if (ui_toolkit == "gtk") { |
| if (auto* gtk_ui = GetGtkUi()) { |
| return gtk_ui; |
| } |
| } else if (ui_toolkit == "qt") { |
| if (auto* qt_ui = GetQtUi()) { |
| return qt_ui; |
| } |
| } else if (ui_toolkit == "fallback") { |
| if (auto* fallback_ui = GetFallbackUi()) { |
| return fallback_ui; |
| } |
| } |
| |
| std::unique_ptr<base::Environment> env = base::Environment::Create(); |
| switch (GetDefaultSystemTheme()) { |
| case SystemTheme::kQt: |
| if (auto* qt_ui = GetQtUi()) { |
| return qt_ui; |
| } |
| if (auto* gtk_ui = GetGtkUi()) { |
| return gtk_ui; |
| } |
| return GetFallbackUi(); |
| case SystemTheme::kGtk: |
| case SystemTheme::kDefault: |
| if (auto* gtk_ui = GetGtkUi()) { |
| return gtk_ui; |
| } |
| if (auto* qt_ui = GetQtUi()) { |
| return qt_ui; |
| } |
| return GetFallbackUi(); |
| } |
| } |
| |
| } // namespace |
| |
| LinuxUi* GetDefaultLinuxUi() { |
| auto* linux_ui = GetDefaultLinuxUiAndTheme(); |
| #if !BUILDFLAG(IS_CASTOS) |
| // This may create an extra thread that may race against the LinuxUi instance |
| // initialization, GtkInitFromCommandLine, in GtkUi for example, so this must |
| // be done after the call to GetDefaultLinuxUiAndTheme above, so the race |
| // condition is avoided. |
| shell_dialog_linux::Initialize(); |
| #endif |
| return linux_ui; |
| } |
| |
| LinuxUiTheme* GetDefaultLinuxUiTheme() { |
| return GetDefaultLinuxUiAndTheme(); |
| } |
| |
| LinuxUiTheme* GetLinuxUiTheme(SystemTheme system_theme) { |
| switch (system_theme) { |
| case SystemTheme::kQt: |
| return GetQtUi(); |
| case SystemTheme::kGtk: |
| return GetGtkUi(); |
| case SystemTheme::kDefault: |
| return nullptr; |
| } |
| } |
| |
| const std::vector<raw_ptr<LinuxUiTheme, VectorExperimental>>& |
| GetLinuxUiThemes() { |
| return GetLinuxUiThemesImpl(); |
| } |
| |
| SystemTheme GetDefaultSystemTheme() { |
| std::unique_ptr<base::Environment> env = base::Environment::Create(); |
| |
| switch (base::nix::GetDesktopEnvironment(env.get())) { |
| case base::nix::DESKTOP_ENVIRONMENT_CINNAMON: |
| case base::nix::DESKTOP_ENVIRONMENT_GNOME: |
| case base::nix::DESKTOP_ENVIRONMENT_PANTHEON: |
| case base::nix::DESKTOP_ENVIRONMENT_UNITY: |
| case base::nix::DESKTOP_ENVIRONMENT_XFCE: |
| return SystemTheme::kGtk; |
| case base::nix::DESKTOP_ENVIRONMENT_KDE3: |
| case base::nix::DESKTOP_ENVIRONMENT_KDE4: |
| case base::nix::DESKTOP_ENVIRONMENT_KDE5: |
| case base::nix::DESKTOP_ENVIRONMENT_KDE6: |
| case base::nix::DESKTOP_ENVIRONMENT_UKUI: |
| case base::nix::DESKTOP_ENVIRONMENT_DEEPIN: |
| case base::nix::DESKTOP_ENVIRONMENT_LXQT: |
| return SystemTheme::kQt; |
| case base::nix::DESKTOP_ENVIRONMENT_OTHER: |
| return SystemTheme::kDefault; |
| } |
| } |
| |
| } // namespace ui |