diff --git a/DistroLauncher-Appx/MyDistro.appxmanifest b/DistroLauncher-Appx/MyDistro.appxmanifest index 7af4f8e6c..9afa20935 100644 --- a/DistroLauncher-Appx/MyDistro.appxmanifest +++ b/DistroLauncher-Appx/MyDistro.appxmanifest @@ -1,5 +1,5 @@  - + @@ -18,7 +18,7 @@ - + diff --git a/DistroLauncher-Tests/DistroLauncher-Tests/ExtendedCliParserTests.cpp b/DistroLauncher-Tests/DistroLauncher-Tests/ExtendedCliParserTests.cpp index def8de5a7..f55a6d622 100644 --- a/DistroLauncher-Tests/DistroLauncher-Tests/ExtendedCliParserTests.cpp +++ b/DistroLauncher-Tests/DistroLauncher-Tests/ExtendedCliParserTests.cpp @@ -125,6 +125,39 @@ namespace Oobe::internal ASSERT_TRUE(std::holds_alternative(opts)); } + // Tests the ManifestMatchedInstall and combinations with it. + TEST(ExtendedCliParserTests, StoreStartMenuInvocation) + { + std::vector args{ARG_EXT_UAP10_PARAMETERS}; + auto opts = parseExtendedOptions(args); + ASSERT_TRUE(std::holds_alternative(opts)); + ASSERT_TRUE(args.empty()); + } + + TEST(ExtendedCliParserTests, InvalidCombinationWithManifestMatched1) + { + std::vector args{L"install", ARG_EXT_UAP10_PARAMETERS}; + auto opts = parseExtendedOptions(args); + ASSERT_TRUE(std::holds_alternative(opts)); + ASSERT_EQ(args.size(), 1); + } + + TEST(ExtendedCliParserTests, InvalidCombinationWithManifestMatched2) + { + std::vector args{L"install", ARG_EXT_INSTALLER_GUI, ARG_EXT_UAP10_PARAMETERS}; + auto opts = parseExtendedOptions(args); + ASSERT_TRUE(std::holds_alternative(opts)); + ASSERT_EQ(args.size(), 1); + } + + TEST(ExtendedCliParserTests, InvalidCombinationWithManifestMatched3) + { + std::vector args{L"config", ARG_EXT_UAP10_PARAMETERS}; + auto opts = parseExtendedOptions(args); + ASSERT_TRUE(std::holds_alternative(opts)); + ASSERT_EQ(args.size(), 1); + } + // Tests whether the upstream options remain preserved: TEST(ExtendedCliParserTests, InstallRootIsUpstream) { diff --git a/DistroLauncher/Application.h b/DistroLauncher/Application.h index 869eedaf8..51b31eb97 100644 --- a/DistroLauncher/Application.h +++ b/DistroLauncher/Application.h @@ -38,6 +38,11 @@ namespace Oobe return std::holds_alternative(arg); } + bool canHideConsole() const + { + return std::holds_alternative(arg); + } + public: /// Returns true if the command line parsing doesn't require running the OOBE. /// That implies partial command line parsing, with the required actions deferred to the upstream code. @@ -65,6 +70,7 @@ namespace Oobe HRESULT hr = std::visit(internal::overloaded{ [&](AutoInstall& option) { return impl_.do_autoinstall(option.autoInstallFile); }, + [&](ManifestMatchedInstall& option) { return impl_.do_install(Mode::AutoDetect); }, [&](InstallDefault& option) { return impl_.do_install(Mode::AutoDetect); }, [&](InstallOnlyDefault& option) { return impl_.do_install(Mode::AutoDetect); }, [&](InteractiveInstallOnly& option) { return impl_.do_install(Mode::Gui); }, @@ -104,7 +110,7 @@ namespace Oobe if (isAutoInstall() || shouldSkipInstaller()) { return; } - impl_.do_run_splash(); + impl_.do_run_splash(canHideConsole()); } }; } diff --git a/DistroLauncher/ApplicationStrategy.cpp b/DistroLauncher/ApplicationStrategy.cpp index 765003d1e..9b5f99428 100644 --- a/DistroLauncher/ApplicationStrategy.cpp +++ b/DistroLauncher/ApplicationStrategy.cpp @@ -100,7 +100,7 @@ namespace Oobe SplashEnabledStrategy::SplashEnabledStrategy() : splashExePath{splashPath()} { } - void SplashEnabledStrategy::do_run_splash() + void SplashEnabledStrategy::do_run_splash(bool hideConsole) { if (!std::filesystem::exists(splashExePath)) { wprintf(L"Splash executable [%s] not found.\n", splashExePath.c_str()); @@ -142,8 +142,9 @@ namespace Oobe return; } splashWindow = std::get::States::Visible>(transition.value()).window; - console.value().hideConsoleWindow(); - consoleIsVisible = false; + if (hideConsole) { + consoleIsVisible = !console.value().hideConsoleWindow(); + } splashIsRunning = true; } @@ -154,7 +155,7 @@ namespace Oobe void SplashEnabledStrategy::do_show_console() { - if (consoleIsVisible) { + if (!console.has_value()) { return; } std::unique_lock guard{consoleGuard, std::defer_lock}; @@ -169,8 +170,9 @@ namespace Oobe if (splashWindow.has_value()) { topWindow = splashWindow.value(); } - console.value().showConsoleWindow(topWindow); - consoleIsVisible = true; + if (!consoleIsVisible) { + consoleIsVisible = console.value().showConsoleWindow(topWindow); + } } void SplashEnabledStrategy::do_close_splash() diff --git a/DistroLauncher/ApplicationStrategy.h b/DistroLauncher/ApplicationStrategy.h index b6a568917..cace0ce86 100644 --- a/DistroLauncher/ApplicationStrategy.h +++ b/DistroLauncher/ApplicationStrategy.h @@ -77,7 +77,7 @@ namespace Oobe HRESULT do_reconfigure(); /// Triggers the console redirection and launch the splash screen application. - void do_run_splash(); + void do_run_splash(bool hideConsole = false); SplashEnabledStrategy(); ~SplashEnabledStrategy() = default; @@ -100,7 +100,7 @@ namespace Oobe /// Prints to the console an error message informing that this platform doesn't support running the splash /// screen. - void do_run_splash() + void do_run_splash(bool hideConsole = false) { wprintf(L"This device architecture doesn't support running the splash screen.\n"); } diff --git a/DistroLauncher/console_service.h b/DistroLauncher/console_service.h index fe20d0872..701ed5f16 100644 --- a/DistroLauncher/console_service.h +++ b/DistroLauncher/console_service.h @@ -161,9 +161,12 @@ namespace Win32Utils bool showConsoleWindow(HWND topWindow) const { - if (auto res = Win32Utils::resize_to(window_, topWindow); res != 0) { - Helpers::PrintErrorMessage(HRESULT_FROM_WIN32(res)); + if (IsWindowVisible(topWindow) == TRUE) { + if (auto res = Win32Utils::resize_to(window_, topWindow); res != 0) { + Helpers::PrintErrorMessage(HRESULT_FROM_WIN32(res)); + } } + // If the window was previously visible, the return value is nonzero. // If the window was previously hidden, the return value is zero. return ShowWindow(window_, SW_SHOWNORMAL) == FALSE; diff --git a/DistroLauncher/extended_cli_parser.cpp b/DistroLauncher/extended_cli_parser.cpp index cfbf3e5e3..a773b7f79 100644 --- a/DistroLauncher/extended_cli_parser.cpp +++ b/DistroLauncher/extended_cli_parser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Canonical Ltd + * Copyright (C) 2022 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as @@ -54,6 +54,11 @@ namespace Oobe::internal Opts parse(const std::vector& arguments) { + // launcher.exe --hide-console - Windows Shell GUI invocation as declared in the appxmanifest. Hides the + // console, runs the OOBE (auto detect graphics support) and brings the shell at the end. + if (auto result = tryParse(arguments); result.has_value()) { + return result.value(); + } // launcher.exe - Runs the OOBE (auto detect graphics support) and brings the shell at the end. if (auto result = tryParse(arguments); result.has_value()) { return result.value(); @@ -110,11 +115,20 @@ namespace Oobe::internal Opts parseExtendedOptions(std::vector& arguments) { Opts options{parse(arguments)}; + // Erasing the extended command line options to avoid confusion in the upstream code. auto it = std::remove_if(arguments.begin(), arguments.end(), [](auto arg) { return std::find(allExtendedArgs.begin(), allExtendedArgs.end(), arg) != allExtendedArgs.end(); }); arguments.erase(it, arguments.end()); + +#ifdef UNSUPPORTED_EXTENDED_CLI + if (shouldWarnUnsupported(options)) { + wprintf(L"Warning: ignoring command line options invalid for this Ubuntu release.\n"); + } + return {}; +#else return options; +#endif // UNSUPPORTED_EXTENDED_CLI } -} \ No newline at end of file +} diff --git a/DistroLauncher/extended_cli_parser.h b/DistroLauncher/extended_cli_parser.h index df38283a2..ad8a01dc7 100644 --- a/DistroLauncher/extended_cli_parser.h +++ b/DistroLauncher/extended_cli_parser.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Canonical Ltd + * Copyright (C) 2022 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as @@ -20,11 +20,12 @@ #define ARG_EXT_INSTALLER_TUI ARG_EXT_ENABLE_INSTALLER L"tui" #define ARG_EXT_DISABLE_INSTALLER ARG_EXT_ENABLE_INSTALLER L"none" #define ARG_EXT_AUTOINSTALL L"--autoinstall" +#define ARG_EXT_UAP10_PARAMETERS L"--hide-console" namespace Oobe::internal { static constexpr std::array allExtendedArgs{ARG_EXT_AUTOINSTALL, ARG_EXT_DISABLE_INSTALLER, ARG_EXT_INSTALLER_GUI, - ARG_EXT_INSTALLER_TUI}; + ARG_EXT_INSTALLER_TUI, ARG_EXT_UAP10_PARAMETERS}; // compile-time array concatenation. template constexpr std::array join(const std::array& a, const std::array& b) @@ -56,6 +57,10 @@ namespace Oobe::internal { static constexpr std::array requirements{ARG_EXT_INSTALLER_TUI}; }; + struct ManifestMatchedInstall // matches the invocation declared in the .appxmanifest. + { + static constexpr std::array requirements{ARG_EXT_UAP10_PARAMETERS}; + }; struct InstallDefault { static constexpr std::array requirements{}; @@ -79,13 +84,35 @@ namespace Oobe::internal // InteractiveInstallOnly and InteractiveInstallShell are actually std::monostate, // i.e. both go to upstream resolution. - using Opts = std::variant, InteractiveInstallOnly, - InteractiveInstallShell, InteractiveInstallShell, Reconfig>; - + // clang-format off + // [clang-format appears to have trouble with long declarations like the following.] + using Opts = std::variant, + InteractiveInstallOnly, + InteractiveInstallShell, + InteractiveInstallShell, + Reconfig>; + // clang-format on /// Parses a vector of command line arguments according to the extended command line options declared above. The /// extended options are removed from the original vector as a side effect to avoid confusing the "upstream command /// line parsing" routines. Notice that argv[0], i.e. the program name, is presumed not to be part of the arguments /// vector. See DistroLauncher.cpp main() function. Opts parseExtendedOptions(std::vector& arguments); + + /// Returns true if the command line parsing result is invalid for the case when the extended CLI is unsupported + /// (the case for older releases). + inline bool shouldWarnUnsupported(const Opts& options) + { + // [ARG_EXT_UAP10_PARAMETERS] must be accepted in the old releases, even though it will be jsut discarded, + // thus we should not warn for this case. The same stands for the empty CLI case. + bool noWarnIf = std::holds_alternative(options) || + std::holds_alternative(options) || + std::holds_alternative(options); + + return !noWarnIf; + } } diff --git a/meta/Ubuntu/generated/DistroLauncher-Appx/MyDistro.appxmanifest b/meta/Ubuntu/generated/DistroLauncher-Appx/MyDistro.appxmanifest index 27304b5d7..0664b6977 100644 --- a/meta/Ubuntu/generated/DistroLauncher-Appx/MyDistro.appxmanifest +++ b/meta/Ubuntu/generated/DistroLauncher-Appx/MyDistro.appxmanifest @@ -1,5 +1,5 @@  - + @@ -18,7 +18,7 @@ - + diff --git a/meta/Ubuntu18.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest b/meta/Ubuntu18.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest index 6a6960bbb..c8aa62241 100644 --- a/meta/Ubuntu18.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest +++ b/meta/Ubuntu18.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest @@ -1,5 +1,5 @@  - + @@ -18,7 +18,7 @@ - + diff --git a/meta/Ubuntu18.04LTS/generated/DistroLauncher/extended_messages.h b/meta/Ubuntu18.04LTS/generated/DistroLauncher/extended_messages.h index 6f70f09be..43368b06a 100644 --- a/meta/Ubuntu18.04LTS/generated/DistroLauncher/extended_messages.h +++ b/meta/Ubuntu18.04LTS/generated/DistroLauncher/extended_messages.h @@ -1 +1,2 @@ -#pragma once +#pragma once +#define UNSUPPORTED_EXTENDED_CLI diff --git a/meta/Ubuntu18.04LTS/src/DistroLauncher/extended_messages.h b/meta/Ubuntu18.04LTS/src/DistroLauncher/extended_messages.h index 6f70f09be..43368b06a 100644 --- a/meta/Ubuntu18.04LTS/src/DistroLauncher/extended_messages.h +++ b/meta/Ubuntu18.04LTS/src/DistroLauncher/extended_messages.h @@ -1 +1,2 @@ -#pragma once +#pragma once +#define UNSUPPORTED_EXTENDED_CLI diff --git a/meta/Ubuntu20.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest b/meta/Ubuntu20.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest index 69e2906c0..80dbaae8d 100644 --- a/meta/Ubuntu20.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest +++ b/meta/Ubuntu20.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest @@ -1,5 +1,5 @@  - + @@ -18,7 +18,7 @@ - + diff --git a/meta/Ubuntu20.04LTS/generated/DistroLauncher/extended_messages.h b/meta/Ubuntu20.04LTS/generated/DistroLauncher/extended_messages.h index 6f70f09be..43368b06a 100644 --- a/meta/Ubuntu20.04LTS/generated/DistroLauncher/extended_messages.h +++ b/meta/Ubuntu20.04LTS/generated/DistroLauncher/extended_messages.h @@ -1 +1,2 @@ -#pragma once +#pragma once +#define UNSUPPORTED_EXTENDED_CLI diff --git a/meta/Ubuntu20.04LTS/src/DistroLauncher/extended_messages.h b/meta/Ubuntu20.04LTS/src/DistroLauncher/extended_messages.h index 6f70f09be..43368b06a 100644 --- a/meta/Ubuntu20.04LTS/src/DistroLauncher/extended_messages.h +++ b/meta/Ubuntu20.04LTS/src/DistroLauncher/extended_messages.h @@ -1 +1,2 @@ -#pragma once +#pragma once +#define UNSUPPORTED_EXTENDED_CLI diff --git a/meta/Ubuntu22.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest b/meta/Ubuntu22.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest index a5aa4ec92..d006f7735 100644 --- a/meta/Ubuntu22.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest +++ b/meta/Ubuntu22.04LTS/generated/DistroLauncher-Appx/MyDistro.appxmanifest @@ -1,5 +1,5 @@  - + @@ -18,7 +18,7 @@ - + diff --git a/meta/UbuntuPreview/generated/DistroLauncher-Appx/MyDistro.appxmanifest b/meta/UbuntuPreview/generated/DistroLauncher-Appx/MyDistro.appxmanifest index b83030514..4c7a5b708 100644 --- a/meta/UbuntuPreview/generated/DistroLauncher-Appx/MyDistro.appxmanifest +++ b/meta/UbuntuPreview/generated/DistroLauncher-Appx/MyDistro.appxmanifest @@ -1,5 +1,5 @@  - + @@ -18,7 +18,7 @@ - +