Initial macOS WebView support

This is a gigantic kerfuffle because Chromium expects a very specific directory & app bundle layout. Have to change a bunch of resource loading code to account for content development being launched from an app bundle, and also had to make automatic MSBuild tooling & a python script to generate such an app bundle
This commit is contained in:
PJB3005
2025-11-09 00:06:16 +01:00
parent feb9e1db69
commit 602d7833a1
18 changed files with 336 additions and 32 deletions

View File

@@ -1,7 +1,9 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Threading;
using Robust.Shared.ContentPack;
using Xilium.CefGlue;
namespace Robust.Client.WebView.Cef
@@ -20,6 +22,20 @@ namespace Robust.Client.WebView.Cef
argv[0] = "-";
}
#if MACOS
NativeLibrary.SetDllImportResolver(typeof(CefSettings).Assembly,
(name, assembly, path) =>
{
if (name == "libcef")
{
var libPath = PathHelpers.ExecutableRelativeFile("../../../../Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework");
return NativeLibrary.Load(libPath, assembly, path);
}
return 0;
});
#endif
var mainArgs = new CefMainArgs(argv);
StartWatchThread();

View File

@@ -17,11 +17,13 @@ namespace Robust.Client.WebView.Cef
{
internal partial class WebViewManagerCef
{
private readonly List<ControlImpl> _activeControls = new();
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
{
var shader = _prototypeManager.Index<ShaderPrototype>("bgra");
var shaderInstance = shader.Instance();
var impl = new ControlImpl(owner, shaderInstance);
var impl = new ControlImpl(this, owner, shaderInstance);
_dependencyCollection.InjectDependencies(impl);
return impl;
}
@@ -133,11 +135,13 @@ namespace Robust.Client.WebView.Cef
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IInputManager _inputMgr = default!;
private readonly WebViewManagerCef _manager;
public readonly WebViewControl Owner;
private readonly ShaderInstance _shaderInstance;
public ControlImpl(WebViewControl owner, ShaderInstance shaderInstance)
public ControlImpl(WebViewManagerCef manager, WebViewControl owner, ShaderInstance shaderInstance)
{
_manager = manager;
Owner = owner;
_shaderInstance = shaderInstance;
}
@@ -194,6 +198,7 @@ namespace Robust.Client.WebView.Cef
var texture = _clyde.CreateBlankTexture<Rgba32>(Vector2i.One);
_data = new LiveData(texture, client, browser, renderer);
_manager._activeControls.Add(this);
}
public void CloseBrowser()
@@ -203,6 +208,8 @@ namespace Robust.Client.WebView.Cef
_data!.Texture.Dispose();
_data.Browser.GetHost().CloseBrowser(true);
_data = null;
_manager._activeControls.Remove(this);
}
public void MouseMove(GUIMouseMoveEventArgs args)
@@ -279,6 +286,7 @@ namespace Robust.Client.WebView.Cef
// Logger.Debug($"{guiRawEvent.Action} {guiRawEvent.Key} {guiRawEvent.ScanCode} {vkKey}");
#if !MACOS
var lParam = 0;
lParam |= (guiRawEvent.ScanCode & 0xFF) << 16;
if (guiRawEvent.Action != RawKeyAction.Down)
@@ -286,7 +294,9 @@ namespace Robust.Client.WebView.Cef
if (guiRawEvent.Action == RawKeyAction.Up)
lParam |= 1 << 31;
#else
var lParam = guiRawEvent.RawCode;
#endif
var modifiers = CalcModifiers(guiRawEvent.Key);
host.SendKeyEvent(new CefKeyEvent
@@ -307,7 +317,7 @@ namespace Robust.Client.WebView.Cef
host.SendKeyEvent(new CefKeyEvent
{
EventType = CefKeyEventType.Char,
WindowsKeyCode = '\r',
WindowsKeyCode = '\b',
NativeKeyCode = lParam,
Modifiers = modifiers
});

View File

@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using Robust.Client.Console;
using Robust.Client.Utility;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
@@ -47,11 +48,12 @@ namespace Robust.Client.WebView.Cef
string subProcessName;
if (OperatingSystem.IsWindows())
subProcessName = "Robust.Client.WebView.exe";
else if (OperatingSystem.IsLinux())
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
subProcessName = "Robust.Client.WebView";
else
throw new NotSupportedException("Unsupported platform for CEF!");
#if !MACOS
var subProcessPath = Path.Combine(BasePath, subProcessName);
var cefResourcesPath = LocateCefResources();
_sawmill.Debug($"Subprocess path: {subProcessPath}, resources: {cefResourcesPath}");
@@ -60,19 +62,36 @@ namespace Robust.Client.WebView.Cef
if (cefResourcesPath == null)
throw new InvalidOperationException("Unable to locate cef_resources directory!");
#endif
var remoteDebugPort = _cfg.GetCVar(WCVars.WebRemoteDebugPort);
var cachePath = FindAndLockCacheDirectory();
#if MACOS
NativeLibrary.SetDllImportResolver(typeof(CefSettings).Assembly,
(name, assembly, path) =>
{
if (name == "libcef")
{
var libPath = PathHelpers.ExecutableRelativeFile("../Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework");
return NativeLibrary.Load(libPath, assembly, path);
}
return 0;
});
#endif
var settings = new CefSettings()
{
WindowlessRenderingEnabled = true, // So we can render to our UI controls.
ExternalMessagePump = false, // Unsure, honestly. TODO CEF: Research this?
NoSandbox = true, // Not disabling the sandbox crashes CEF.
#if !MACOS
BrowserSubprocessPath = subProcessPath,
LocalesDirPath = Path.Combine(cefResourcesPath, "locales"),
ResourcesDirPath = cefResourcesPath,
#endif
RemoteDebuggingPort = remoteDebugPort,
CookieableSchemesList = "usr,res",
CachePath = cachePath,
@@ -113,7 +132,6 @@ namespace Robust.Client.WebView.Cef
if (ProbeDir(BasePath, out var path))
return path;
foreach (var searchDir in NativeDllSearchDirectories())
{
if (ProbeDir(searchDir, out path))
@@ -147,6 +165,16 @@ namespace Robust.Client.WebView.Cef
public void Shutdown()
{
foreach (var control in _activeControls.ToArray())
{
control.CloseBrowser();
}
foreach (var window in _browserWindows.ToArray())
{
window.Dispose();
}
CefRuntime.Shutdown();
}

View File

@@ -8,10 +8,10 @@ internal sealed class TestBrowseWindow : DefaultWindow
{
protected override Vector2 ContentsMinimumSize => new Vector2(640, 480);
public TestBrowseWindow()
public TestBrowseWindow(string url)
{
var wv = new WebViewControl();
wv.Url = "https://spacestation14.io";
wv.Url = url;
Contents.AddChild(wv);
}
@@ -23,6 +23,15 @@ internal sealed class TestBrowseWindowCommand : LocalizedCommands
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
new TestBrowseWindow().Open();
var url = args.Length > 0 ? args[0] : "https://spacestation14.com";
new TestBrowseWindow(url).Open();
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
return CompletionResult.FromHint("<url>");
return CompletionResult.Empty;
}
}