Support running Big Picture as separate process
Enable Big Picture mode to run concurrently with Desktop mode by: - Implementing cross-platform process launching (LaunchSiblingExecutable) for Windows, macOS, and Linux - Adding app profile system to isolate CEF user data between modes - Making window class/title and mutex names profile-aware - Changing mode switching to launch sibling executable instead of in-process switching - Updating settings.js to use native bridge for Big Picture launch - Adding build.bat helper for Windows builds
This commit is contained in:
@@ -56,6 +56,7 @@ if(OS_WINDOWS)
|
|||||||
set(NEBULA_PLATFORM_SOURCES
|
set(NEBULA_PLATFORM_SOURCES
|
||||||
src/platform/win/default_browser_win.cpp
|
src/platform/win/default_browser_win.cpp
|
||||||
src/platform/win/paths_win.cpp
|
src/platform/win/paths_win.cpp
|
||||||
|
src/platform/win/process_win.cpp
|
||||||
src/platform/win/startup_win.cpp
|
src/platform/win/startup_win.cpp
|
||||||
src/platform/win/browser_host_win.cpp
|
src/platform/win/browser_host_win.cpp
|
||||||
src/platform/win/nebula_window_win.cpp
|
src/platform/win/nebula_window_win.cpp
|
||||||
@@ -64,6 +65,7 @@ elseif(OS_MACOSX)
|
|||||||
set(NEBULA_PLATFORM_SOURCES
|
set(NEBULA_PLATFORM_SOURCES
|
||||||
src/platform/mac/default_browser_mac.mm
|
src/platform/mac/default_browser_mac.mm
|
||||||
src/platform/mac/paths_mac.cpp
|
src/platform/mac/paths_mac.cpp
|
||||||
|
src/platform/mac/process_mac.cpp
|
||||||
src/platform/mac/startup_mac.mm
|
src/platform/mac/startup_mac.mm
|
||||||
src/platform/mac/browser_host_mac.mm
|
src/platform/mac/browser_host_mac.mm
|
||||||
src/platform/mac/nebula_window_mac.mm
|
src/platform/mac/nebula_window_mac.mm
|
||||||
@@ -79,6 +81,7 @@ elseif(OS_LINUX)
|
|||||||
set(NEBULA_PLATFORM_SOURCES
|
set(NEBULA_PLATFORM_SOURCES
|
||||||
src/platform/linux/default_browser_linux.cpp
|
src/platform/linux/default_browser_linux.cpp
|
||||||
src/platform/linux/paths_linux.cpp
|
src/platform/linux/paths_linux.cpp
|
||||||
|
src/platform/linux/process_linux.cpp
|
||||||
src/platform/linux/startup_linux.cpp
|
src/platform/linux/startup_linux.cpp
|
||||||
src/platform/linux/browser_host_linux.cpp
|
src/platform/linux/browser_host_linux.cpp
|
||||||
src/platform/linux/nebula_window_linux.cpp
|
src/platform/linux/nebula_window_linux.cpp
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
rem Build Nebula Browser (Windows).
|
||||||
|
rem Usage:
|
||||||
|
rem build.bat - configure + Release build
|
||||||
|
rem build.bat Debug - configure + Debug build
|
||||||
|
rem build.bat Release - configure + Release build
|
||||||
|
rem build.bat configure - configure only
|
||||||
|
rem build.bat clean - remove the build folder
|
||||||
|
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
set "CONFIG=Release"
|
||||||
|
if /I "%~1"=="Debug" set "CONFIG=Debug"
|
||||||
|
if /I "%~1"=="Release" set "CONFIG=Release"
|
||||||
|
|
||||||
|
if /I "%~1"=="clean" (
|
||||||
|
echo Removing build\ ...
|
||||||
|
if exist build rmdir /s /q build
|
||||||
|
echo Done.
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exist "thirdparty\cef\cmake\FindCEF.cmake" (
|
||||||
|
echo ERROR: CEF not found at thirdparty\cef\
|
||||||
|
echo Download a Windows 64-bit CEF standard binary distribution and extract it
|
||||||
|
echo so that thirdparty\cef\cmake\FindCEF.cmake exists.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Configuring CMake...
|
||||||
|
cmake -B build
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Configure failed.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if /I "%~1"=="configure" (
|
||||||
|
echo Configure complete.
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Building %CONFIG%...
|
||||||
|
cmake --build build --config %CONFIG%
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Build failed.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Build succeeded: build\%CONFIG%\NebulaBrowser.exe
|
||||||
|
exit /b 0
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "include/wrapper/cef_helpers.h"
|
#include "include/wrapper/cef_helpers.h"
|
||||||
#include "platform/browser_host.h"
|
#include "platform/browser_host.h"
|
||||||
#include "platform/default_browser.h"
|
#include "platform/default_browser.h"
|
||||||
|
#include "platform/process.h"
|
||||||
#include "ui/paths.h"
|
#include "ui/paths.h"
|
||||||
|
|
||||||
namespace nebula::app {
|
namespace nebula::app {
|
||||||
@@ -243,6 +244,13 @@ void NebulaController::OnWindowCloseRequested() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (launch_options_.mode == AppMode::BigPicture && !closing_) {
|
||||||
|
if (!SwitchToSiblingMode(AppMode::Desktop)) {
|
||||||
|
BeginShutdown();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BeginShutdown();
|
BeginShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,7 +428,8 @@ void NebulaController::OnChromeCommand(const std::string& command, const std::st
|
|||||||
CloseMenuPopup();
|
CloseMenuPopup();
|
||||||
tabs_.LoadURL(nebula::ui::GetSettingsUrl());
|
tabs_.LoadURL(nebula::ui::GetSettingsUrl());
|
||||||
} else if (command == "big-picture") {
|
} else if (command == "big-picture") {
|
||||||
EnterBigPictureMode();
|
CloseMenuPopup();
|
||||||
|
SwitchToSiblingMode(AppMode::BigPicture);
|
||||||
} else if (command == "gpu-diagnostics") {
|
} else if (command == "gpu-diagnostics") {
|
||||||
CloseMenuPopup();
|
CloseMenuPopup();
|
||||||
tabs_.LoadURL(nebula::ui::GetGpuDiagnosticsUrl());
|
tabs_.LoadURL(nebula::ui::GetGpuDiagnosticsUrl());
|
||||||
@@ -488,7 +497,9 @@ void NebulaController::OnChromeCommand(const std::string& command, const std::st
|
|||||||
BeginShutdown();
|
BeginShutdown();
|
||||||
} else if (command == "exit-bigpicture" && window_) {
|
} else if (command == "exit-bigpicture" && window_) {
|
||||||
if (launch_options_.mode == AppMode::BigPicture) {
|
if (launch_options_.mode == AppMode::BigPicture) {
|
||||||
BeginShutdown();
|
if (!SwitchToSiblingMode(AppMode::Desktop)) {
|
||||||
|
BeginShutdown();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ExitBigPictureMode();
|
ExitBigPictureMode();
|
||||||
@@ -728,6 +739,23 @@ void NebulaController::ExitBigPictureMode() {
|
|||||||
ResizeBrowsers();
|
ResizeBrowsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NebulaController::SwitchToSiblingMode(AppMode mode) {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
const char* executable =
|
||||||
|
mode == AppMode::BigPicture ? "NebulaBigPicture.exe" : "NebulaBrowser.exe";
|
||||||
|
#else
|
||||||
|
const char* executable =
|
||||||
|
mode == AppMode::BigPicture ? "NebulaBigPicture" : "NebulaBrowser";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!nebula::platform::LaunchSiblingExecutable(executable)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeginShutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
nebula::window::BrowserLayout NebulaController::CurrentBrowserLayout() const {
|
nebula::window::BrowserLayout NebulaController::CurrentBrowserLayout() const {
|
||||||
if (!window_) {
|
if (!window_) {
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ private:
|
|||||||
void CreateContentBrowser();
|
void CreateContentBrowser();
|
||||||
void EnterBigPictureMode();
|
void EnterBigPictureMode();
|
||||||
void ExitBigPictureMode();
|
void ExitBigPictureMode();
|
||||||
|
bool SwitchToSiblingMode(AppMode mode);
|
||||||
nebula::window::BrowserLayout CurrentBrowserLayout() const;
|
nebula::window::BrowserLayout CurrentBrowserLayout() const;
|
||||||
void ToggleMenuPopup();
|
void ToggleMenuPopup();
|
||||||
void CloseMenuPopup();
|
void CloseMenuPopup();
|
||||||
|
|||||||
@@ -122,6 +122,10 @@ int RunNebula(const nebula::platform::AppStartup& startup, LaunchOptions options
|
|||||||
return subprocess_exit_code;
|
return subprocess_exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Isolate Big Picture from the desktop browser so both can run together.
|
||||||
|
nebula::ui::SetAppProfile(
|
||||||
|
options.mode == AppMode::BigPicture ? "bigpicture" : "desktop");
|
||||||
|
|
||||||
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
|
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
|
||||||
nebula::platform::InitCommandLine(command_line, startup);
|
nebula::platform::InitCommandLine(command_line, startup);
|
||||||
std::string initial_url = GetLaunchTarget(command_line);
|
std::string initial_url = GetLaunchTarget(command_line);
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ bool NebulaBrowserClient::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser
|
|||||||
const bool allowed_settings_command =
|
const bool allowed_settings_command =
|
||||||
IsSettingsFrame(frame) && (command == "navigate" ||
|
IsSettingsFrame(frame) && (command == "navigate" ||
|
||||||
command == "new-tab" ||
|
command == "new-tab" ||
|
||||||
|
command == "big-picture" ||
|
||||||
command == "check-default-browser" ||
|
command == "check-default-browser" ||
|
||||||
command == "set-default-browser" ||
|
command == "set-default-browser" ||
|
||||||
command == "clear-site-history" ||
|
command == "clear-site-history" ||
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#include "platform/process.h"
|
||||||
|
|
||||||
|
#include "platform/paths_platform.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
namespace nebula::platform {
|
||||||
|
|
||||||
|
bool LaunchSiblingExecutable(const std::string& executable_name) {
|
||||||
|
const auto directory = ExecutableDirectory();
|
||||||
|
if (directory.empty() || executable_name.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::filesystem::path path = directory / executable_name;
|
||||||
|
std::error_code error;
|
||||||
|
if (!std::filesystem::exists(path, error)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
execl(path.c_str(), path.c_str(), static_cast<char*>(nullptr));
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nebula::platform
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#include "platform/process.h"
|
||||||
|
|
||||||
|
#include "platform/paths_platform.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
namespace nebula::platform {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::filesystem::path ResolveSiblingPath(const std::string& executable_name) {
|
||||||
|
const auto directory = ExecutableDirectory();
|
||||||
|
if (directory.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::filesystem::path same_dir = directory / executable_name;
|
||||||
|
std::error_code error;
|
||||||
|
if (std::filesystem::exists(same_dir, error)) {
|
||||||
|
return same_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NebulaBrowser.app/Contents/MacOS -> sibling NebulaBigPicture.app/Contents/MacOS
|
||||||
|
const std::filesystem::path sibling_app =
|
||||||
|
directory / ".." / ".." / ".." / (executable_name + ".app") / "Contents" / "MacOS" /
|
||||||
|
executable_name;
|
||||||
|
if (std::filesystem::exists(sibling_app, error)) {
|
||||||
|
return sibling_app.lexically_normal();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool LaunchSiblingExecutable(const std::string& executable_name) {
|
||||||
|
const std::filesystem::path path = ResolveSiblingPath(executable_name);
|
||||||
|
if (path.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
execl(path.c_str(), path.c_str(), static_cast<char*>(nullptr));
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nebula::platform
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace nebula::platform {
|
||||||
|
|
||||||
|
// Launches an executable that lives next to the current process.
|
||||||
|
bool LaunchSiblingExecutable(const std::string& executable_name);
|
||||||
|
|
||||||
|
} // namespace nebula::platform
|
||||||
@@ -7,11 +7,22 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "ui/paths.h"
|
||||||
|
|
||||||
namespace nebula::window {
|
namespace nebula::window {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr wchar_t kWindowClassName[] = L"NebulaBrowserWindow";
|
const wchar_t* WindowClassName() {
|
||||||
constexpr wchar_t kWindowTitle[] = L"Nebula Browser";
|
return nebula::ui::GetAppProfile() == "bigpicture"
|
||||||
|
? L"NebulaBigPictureWindow"
|
||||||
|
: L"NebulaBrowserWindow";
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* WindowTitle() {
|
||||||
|
return nebula::ui::GetAppProfile() == "bigpicture"
|
||||||
|
? L"Nebula Big Picture"
|
||||||
|
: L"Nebula Browser";
|
||||||
|
}
|
||||||
constexpr wchar_t kChildFrameHitTestOldProcProp[] = L"NebulaChildFrameHitTestOldProc";
|
constexpr wchar_t kChildFrameHitTestOldProcProp[] = L"NebulaChildFrameHitTestOldProc";
|
||||||
constexpr wchar_t kChildFrameHitTestParentProp[] = L"NebulaChildFrameHitTestParent";
|
constexpr wchar_t kChildFrameHitTestParentProp[] = L"NebulaChildFrameHitTestParent";
|
||||||
constexpr ULONG_PTR kOpenTargetCopyDataId = 0x4E42554CUL;
|
constexpr ULONG_PTR kOpenTargetCopyDataId = 0x4E42554CUL;
|
||||||
@@ -415,7 +426,7 @@ void nebula::window::NebulaWindowImpl::RegisterClass(HINSTANCE instance_handle)
|
|||||||
window_class.lpfnWndProc = StaticWndProc;
|
window_class.lpfnWndProc = StaticWndProc;
|
||||||
window_class.hInstance = instance_handle;
|
window_class.hInstance = instance_handle;
|
||||||
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||||
window_class.lpszClassName = kWindowClassName;
|
window_class.lpszClassName = WindowClassName();
|
||||||
|
|
||||||
RegisterClassExW(&window_class);
|
RegisterClassExW(&window_class);
|
||||||
}
|
}
|
||||||
@@ -522,8 +533,8 @@ bool NebulaWindow::Create(const platform::AppStartup& startup) {
|
|||||||
|
|
||||||
impl_->hwnd = CreateWindowExW(
|
impl_->hwnd = CreateWindowExW(
|
||||||
0,
|
0,
|
||||||
kWindowClassName,
|
WindowClassName(),
|
||||||
kWindowTitle,
|
WindowTitle(),
|
||||||
WS_POPUP | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN,
|
WS_POPUP | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
@@ -654,7 +665,7 @@ void NebulaWindow::SetTitle(const std::string& title) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::wstring wide = title.empty() ? kWindowTitle : Utf8ToWide(title);
|
const std::wstring wide = title.empty() ? WindowTitle() : Utf8ToWide(title);
|
||||||
SetWindowTextW(impl_->hwnd, wide.c_str());
|
SetWindowTextW(impl_->hwnd, wide.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#include "platform/process.h"
|
||||||
|
|
||||||
|
#include "platform/paths_platform.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
namespace nebula::platform {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::wstring Utf8ToWide(const std::string& utf8) {
|
||||||
|
if (utf8.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const int size = MultiByteToWideChar(
|
||||||
|
CP_UTF8, 0, utf8.data(), static_cast<int>(utf8.size()), nullptr, 0);
|
||||||
|
if (size <= 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring result(static_cast<size_t>(size), L'\0');
|
||||||
|
MultiByteToWideChar(
|
||||||
|
CP_UTF8, 0, utf8.data(), static_cast<int>(utf8.size()), result.data(), size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool LaunchSiblingExecutable(const std::string& executable_name) {
|
||||||
|
const auto directory = ExecutableDirectory();
|
||||||
|
if (directory.empty() || executable_name.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::wstring exe_name = Utf8ToWide(executable_name);
|
||||||
|
if (exe_name.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::filesystem::path path = directory / exe_name;
|
||||||
|
std::error_code error;
|
||||||
|
if (!std::filesystem::exists(path, error)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::wstring path_wide = path.wstring();
|
||||||
|
const std::wstring dir_wide = directory.wstring();
|
||||||
|
|
||||||
|
STARTUPINFOW startup_info = {};
|
||||||
|
startup_info.cb = sizeof(startup_info);
|
||||||
|
PROCESS_INFORMATION process_info = {};
|
||||||
|
|
||||||
|
// CreateProcess requires a writable command line buffer.
|
||||||
|
std::wstring command_line = L"\"" + path_wide + L"\"";
|
||||||
|
const BOOL created = CreateProcessW(
|
||||||
|
path_wide.c_str(),
|
||||||
|
command_line.data(),
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
FALSE,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
dir_wide.c_str(),
|
||||||
|
&startup_info,
|
||||||
|
&process_info);
|
||||||
|
if (!created) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(process_info.hThread);
|
||||||
|
CloseHandle(process_info.hProcess);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nebula::platform
|
||||||
@@ -10,10 +10,20 @@
|
|||||||
namespace nebula::platform {
|
namespace nebula::platform {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr wchar_t kMainInstanceMutexName[] = L"Local\\NebulaBrowserMainInstance";
|
|
||||||
constexpr wchar_t kWindowClassName[] = L"NebulaBrowserWindow";
|
|
||||||
constexpr ULONG_PTR kOpenTargetCopyDataId = 0x4E42554CUL;
|
constexpr ULONG_PTR kOpenTargetCopyDataId = 0x4E42554CUL;
|
||||||
|
|
||||||
|
const wchar_t* MainInstanceMutexName() {
|
||||||
|
return nebula::ui::GetAppProfile() == "bigpicture"
|
||||||
|
? L"Local\\NebulaBigPictureMainInstance"
|
||||||
|
: L"Local\\NebulaBrowserMainInstance";
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* WindowClassName() {
|
||||||
|
return nebula::ui::GetAppProfile() == "bigpicture"
|
||||||
|
? L"NebulaBigPictureWindow"
|
||||||
|
: L"NebulaBrowserWindow";
|
||||||
|
}
|
||||||
|
|
||||||
class ScopedHandle {
|
class ScopedHandle {
|
||||||
public:
|
public:
|
||||||
explicit ScopedHandle(HANDLE handle) : handle_(handle) {}
|
explicit ScopedHandle(HANDLE handle) : handle_(handle) {}
|
||||||
@@ -54,7 +64,7 @@ void ForwardLaunchTargetToExistingWindow(const std::string& launch_target) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HWND existing_window = FindWindowW(kWindowClassName, nullptr);
|
HWND existing_window = FindWindowW(WindowClassName(), nullptr);
|
||||||
if (!existing_window) {
|
if (!existing_window) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -81,7 +91,7 @@ void PrepareApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool TryAcquireSingleInstance(const std::string& launch_target) {
|
bool TryAcquireSingleInstance(const std::string& launch_target) {
|
||||||
static ScopedHandle mutex(CreateMutexW(nullptr, TRUE, kMainInstanceMutexName));
|
static ScopedHandle mutex(CreateMutexW(nullptr, TRUE, MainInstanceMutexName()));
|
||||||
const bool already_running = mutex.valid() && GetLastError() == ERROR_ALREADY_EXISTS;
|
const bool already_running = mutex.valid() && GetLastError() == ERROR_ALREADY_EXISTS;
|
||||||
if (already_running) {
|
if (already_running) {
|
||||||
ForwardLaunchTargetToExistingWindow(launch_target);
|
ForwardLaunchTargetToExistingWindow(launch_target);
|
||||||
|
|||||||
@@ -109,8 +109,18 @@ const InternalPage* FindInternalPageByFileUrl(const std::string& url) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string g_app_profile;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void SetAppProfile(std::string_view profile) {
|
||||||
|
g_app_profile = std::string(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& GetAppProfile() {
|
||||||
|
return g_app_profile;
|
||||||
|
}
|
||||||
|
|
||||||
std::filesystem::path GetExecutableDirectory() {
|
std::filesystem::path GetExecutableDirectory() {
|
||||||
return nebula::platform::ExecutableDirectory();
|
return nebula::platform::ExecutableDirectory();
|
||||||
}
|
}
|
||||||
@@ -125,6 +135,9 @@ std::filesystem::path GetUserDataDirectory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path user_data = root / "Nebula" / "User Data";
|
std::filesystem::path user_data = root / "Nebula" / "User Data";
|
||||||
|
if (!g_app_profile.empty() && g_app_profile != "desktop") {
|
||||||
|
user_data = root / "Nebula" / ("User Data " + g_app_profile);
|
||||||
|
}
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
std::filesystem::create_directories(user_data, ec);
|
std::filesystem::create_directories(user_data, ec);
|
||||||
return user_data;
|
return user_data;
|
||||||
|
|||||||
@@ -2,9 +2,15 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace nebula::ui {
|
namespace nebula::ui {
|
||||||
|
|
||||||
|
// Process-wide profile used to isolate desktop vs Big Picture CEF data.
|
||||||
|
// Call before GetUserDataDirectory() / CEF initialize. Empty/"desktop" = default.
|
||||||
|
void SetAppProfile(std::string_view profile);
|
||||||
|
const std::string& GetAppProfile();
|
||||||
|
|
||||||
std::filesystem::path GetExecutableDirectory();
|
std::filesystem::path GetExecutableDirectory();
|
||||||
std::filesystem::path GetUserDataDirectory();
|
std::filesystem::path GetUserDataDirectory();
|
||||||
std::filesystem::path GetCacheDirectory();
|
std::filesystem::path GetCacheDirectory();
|
||||||
|
|||||||
+3
-3
@@ -382,11 +382,11 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bigPictureBtn) {
|
if (bigPictureBtn) {
|
||||||
bigPictureBtn.addEventListener('click', async () => {
|
bigPictureBtn.addEventListener('click', () => {
|
||||||
try {
|
try {
|
||||||
if (window.bigPictureAPI && typeof window.bigPictureAPI.launch === 'function') {
|
if (hasNebulaNativeBridge()) {
|
||||||
showStatus('Launching Big Picture Mode...');
|
showStatus('Launching Big Picture Mode...');
|
||||||
await window.bigPictureAPI.launch();
|
window.nebulaNative.postMessage('big-picture');
|
||||||
} else {
|
} else {
|
||||||
showStatus('Big Picture Mode not available');
|
showStatus('Big Picture Mode not available');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user