mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Add initial CEF integration to engine.
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -16,3 +16,6 @@
|
||||
[submodule "Linguini"]
|
||||
path = Linguini
|
||||
url = https://github.com/space-wizards/Linguini
|
||||
[submodule "cefglue"]
|
||||
path = cefglue
|
||||
url = https://gitlab.com/xiliumhq/chromiumembedded/cefglue/
|
||||
|
||||
126
Robust.Client.CEF/BitmapBuffer.cs
Normal file
126
Robust.Client.CEF/BitmapBuffer.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright © 2018 The CefSharp Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Log;
|
||||
using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.CEF
|
||||
{
|
||||
/// <summary>
|
||||
/// BitmapBuffer contains a byte[] used to store the Bitmap generated from <see cref="IRenderHandler.OnPaint"/>
|
||||
/// and associated methods for updating that buffer and creating a <see cref="Bitmap"/> from the actaual Buffer
|
||||
/// </summary>
|
||||
internal class BitmapBuffer
|
||||
{
|
||||
private const int BytesPerPixel = 4;
|
||||
private const PixelFormat Format = PixelFormat.Format32bppPArgb;
|
||||
|
||||
private byte[] buffer = Array.Empty<byte>();
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes
|
||||
/// </summary>
|
||||
public int NumberOfBytes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Width
|
||||
/// </summary>
|
||||
public int Width { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Height
|
||||
/// </summary>
|
||||
public int Height { get; private set; }
|
||||
/// <summary>
|
||||
/// Dirty Rect - unified region containing th
|
||||
/// </summary>
|
||||
public CefRectangle DirtyRect { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Locking object used to syncronise access to the underlying buffer
|
||||
/// </summary>
|
||||
public object BitmapLock { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of BitmapBuffer
|
||||
/// </summary>
|
||||
/// <param name="bitmapLock">Reference to the bitmapLock, a shared lock object is expected</param>
|
||||
internal BitmapBuffer(object bitmapLock)
|
||||
{
|
||||
BitmapLock = bitmapLock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the byte[] array that represents the Bitmap
|
||||
/// </summary>
|
||||
public byte[] Buffer
|
||||
{
|
||||
get { return buffer; }
|
||||
}
|
||||
|
||||
//TODO: May need to Pin the buffer in memory using GCHandle.Alloc(this.buffer, GCHandleType.Pinned);
|
||||
private void ResizeBuffer(int width, int height)
|
||||
{
|
||||
if (buffer == null || width != Width || height != Height)
|
||||
{
|
||||
//No of Pixels (width * height) * BytesPerPixel
|
||||
NumberOfBytes = width * height * BytesPerPixel;
|
||||
|
||||
buffer = new byte[NumberOfBytes];
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy data from the unmanaged buffer (IntPtr) into our managed buffer.
|
||||
/// Locks BitmapLock before performing any update
|
||||
/// </summary>
|
||||
/// <param name="width">width</param>
|
||||
/// <param name="height">height</param>
|
||||
/// <param name="buffer">pointer to unmanaged buffer (void*)</param>
|
||||
/// <param name="dirtyRect">rectangle to be updated</param>
|
||||
public void UpdateBuffer(int width, int height, IntPtr buffer, CefRectangle dirtyRect)
|
||||
{
|
||||
lock (BitmapLock)
|
||||
{
|
||||
DirtyRect = dirtyRect;
|
||||
ResizeBuffer(width, height);
|
||||
Marshal.Copy(buffer, this.buffer, 0, NumberOfBytes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Bitmap given with the current Width/Height and <see cref="Format"/>
|
||||
/// then copies the buffer that represents the bitmap.
|
||||
/// Locks <see cref="BitmapLock"/> before creating the <see cref="Bitmap"/>
|
||||
/// </summary>
|
||||
/// <returns>A new bitmap</returns>
|
||||
public Bitmap? CreateBitmap()
|
||||
{
|
||||
lock (BitmapLock)
|
||||
{
|
||||
if (Width == 0 || Height == 0 || buffer.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var bitmap = new Bitmap(Width, Height, Format);
|
||||
|
||||
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, Format);
|
||||
|
||||
Marshal.Copy(Buffer, 0, bitmapData.Scan0, NumberOfBytes);
|
||||
|
||||
bitmap.UnlockBits(bitmapData);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
254
Robust.Client.CEF/BrowserControl.cs
Normal file
254
Robust.Client.CEF/BrowserControl.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.CEF
|
||||
{
|
||||
// Funny browser control to integrate in UI.
|
||||
public class BrowserControl : Control
|
||||
{
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
|
||||
private RobustWebClient _client;
|
||||
private CefBrowser _browser;
|
||||
private ControlRenderHandler _renderer;
|
||||
|
||||
// TODO CEF: I don't know how to UI, are these methods below right?
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
var buffer = _renderer.Buffer;
|
||||
return new Vector2(buffer.Width, buffer.Height);
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureCore(Vector2 availableSize)
|
||||
{
|
||||
var buffer = _renderer.Buffer;
|
||||
return new Vector2(buffer.Width, buffer.Height);
|
||||
}
|
||||
|
||||
public BrowserControl()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
// A funny render handler that will allow us to render to the control.
|
||||
_renderer = new ControlRenderHandler(this);
|
||||
|
||||
// A funny web cef client. This can actually be shared by multiple browsers, but I'm not sure how the
|
||||
// rendering would work in that case? TODO CEF: Investigate a way to share the web client?
|
||||
_client = new RobustWebClient(_renderer);
|
||||
|
||||
var info = CefWindowInfo.Create();
|
||||
|
||||
// FUNFACT: If you DO NOT set these below and set info.Width/info.Height instead, you get an external window
|
||||
// Good to know, huh? Setup is the same, except you can pass a dummy render handler to the CEF client.
|
||||
info.SetAsWindowless(IntPtr.Zero, false); // TODO CEF: Pass parent handle?
|
||||
info.WindowlessRenderingEnabled = true;
|
||||
|
||||
var settings = new CefBrowserSettings()
|
||||
{
|
||||
WindowlessFrameRate = 60,
|
||||
};
|
||||
|
||||
// Create the web browser! And by default, we go to about:blank.
|
||||
_browser = CefBrowserHost.CreateBrowserSync(info, _client, settings, "about:blank");
|
||||
}
|
||||
|
||||
protected override void MouseMove(GUIMouseMoveEventArgs args)
|
||||
{
|
||||
base.MouseMove(args);
|
||||
|
||||
// TODO CEF: Modifiers
|
||||
_browser.GetHost().SendMouseMoveEvent(new CefMouseEvent((int)args.RelativePosition.X, (int)args.RelativePosition.Y, CefEventFlags.None), false);
|
||||
}
|
||||
|
||||
protected override void MouseExited()
|
||||
{
|
||||
base.MouseExited();
|
||||
|
||||
// TODO CEF: Modifiers
|
||||
_browser.GetHost().SendMouseMoveEvent(new CefMouseEvent(0, 0, CefEventFlags.None), true);
|
||||
}
|
||||
|
||||
protected override void MouseWheel(GUIMouseWheelEventArgs args)
|
||||
{
|
||||
base.MouseWheel(args);
|
||||
|
||||
// TODO CEF: Modifiers
|
||||
_browser.GetHost().SendMouseWheelEvent(new CefMouseEvent((int)args.RelativePosition.X, (int)args.RelativePosition.Y, CefEventFlags.None), (int)args.Delta.X*4, (int)args.Delta.Y*4);
|
||||
}
|
||||
|
||||
protected override void TextEntered(GUITextEventArgs args)
|
||||
{
|
||||
base.TextEntered(args);
|
||||
|
||||
// TODO CEF: Yeah the thing below is not how this works.
|
||||
// _browser.GetHost().SendKeyEvent(new CefKeyEvent(){NativeKeyCode = (int) args.CodePoint});
|
||||
}
|
||||
|
||||
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.KeyBindUp(args);
|
||||
|
||||
// TODO CEF Clean up this shitty code. Also add middle click.
|
||||
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
{
|
||||
_browser.GetHost().SendMouseClickEvent(new CefMouseEvent((int)args.RelativePosition.X, (int)args.RelativePosition.Y, CefEventFlags.None), CefMouseButtonType.Left, true, 1);
|
||||
} else if (args.Function == EngineKeyFunctions.UIRightClick)
|
||||
{
|
||||
_browser.GetHost().SendMouseClickEvent(new CefMouseEvent((int)args.RelativePosition.X, (int)args.RelativePosition.Y, CefEventFlags.None), CefMouseButtonType.Middle, true, 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.KeyBindDown(args);
|
||||
|
||||
// TODO CEF Clean up this shitty code. Also add middle click.
|
||||
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
{
|
||||
_browser.GetHost().SendMouseClickEvent(new CefMouseEvent((int)args.RelativePosition.X, (int)args.RelativePosition.Y, CefEventFlags.None), CefMouseButtonType.Left, false, 1);
|
||||
} else if (args.Function == EngineKeyFunctions.UIRightClick)
|
||||
{
|
||||
_browser.GetHost().SendMouseClickEvent(new CefMouseEvent((int)args.RelativePosition.X, (int)args.RelativePosition.Y, CefEventFlags.None), CefMouseButtonType.Middle, false, 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Resized()
|
||||
{
|
||||
base.Resized();
|
||||
|
||||
_browser.GetHost().NotifyMoveOrResizeStarted();
|
||||
_browser.GetHost().WasResized();
|
||||
}
|
||||
|
||||
public void Browse(string url)
|
||||
{
|
||||
_browser.GetMainFrame().LoadUrl(url);
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
{
|
||||
base.Draw(handle);
|
||||
|
||||
var bitmap = _renderer.Buffer.CreateBitmap();
|
||||
|
||||
if (bitmap == null)
|
||||
return;
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
|
||||
// Oof, ow, owie the allocations.
|
||||
bitmap.Save(memoryStream, ImageFormat.Png);
|
||||
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// TODO CEF: There must certainly be a better way of doing this... Right?
|
||||
var texture = _clyde.LoadTextureFromPNGStream(memoryStream);
|
||||
|
||||
handle.DrawTexture(texture, Vector2.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ControlRenderHandler : CefRenderHandler
|
||||
{
|
||||
public BitmapBuffer Buffer { get; }
|
||||
private Control _control;
|
||||
|
||||
internal ControlRenderHandler(Control control)
|
||||
{
|
||||
Buffer = new BitmapBuffer(this);
|
||||
_control = control;
|
||||
}
|
||||
|
||||
protected override CefAccessibilityHandler GetAccessibilityHandler()
|
||||
{
|
||||
if (_control.Disposed)
|
||||
return null!;
|
||||
|
||||
// TODO CEF: Do we need this? Can we return null instead?
|
||||
return new AccessibilityHandler();
|
||||
}
|
||||
|
||||
protected override void GetViewRect(CefBrowser browser, out CefRectangle rect)
|
||||
{
|
||||
if (_control.Disposed)
|
||||
{
|
||||
rect = new CefRectangle();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO CEF: Do we need to pass real screen coords? Cause what we do already works...
|
||||
//var screenCoords = _control.ScreenCoordinates;
|
||||
//rect = new CefRectangle((int) screenCoords.X, (int) screenCoords.Y, (int)Math.Max(_control.Size.X, 1), (int)Math.Max(_control.Size.Y, 1));
|
||||
|
||||
// We do the max between size and 1 because it will LITERALLY CRASH WITHOUT AN ERROR otherwise.
|
||||
rect = new CefRectangle(0, 0, (int)Math.Max(_control.Size.X, 1), (int)Math.Max(_control.Size.Y, 1));
|
||||
}
|
||||
|
||||
protected override bool GetScreenInfo(CefBrowser browser, CefScreenInfo screenInfo)
|
||||
{
|
||||
if (_control.Disposed)
|
||||
return false;
|
||||
|
||||
// TODO CEF: Get actual scale factor?
|
||||
screenInfo.DeviceScaleFactor = 1.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnPopupSize(CefBrowser browser, CefRectangle rect)
|
||||
{
|
||||
if (_control.Disposed)
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void OnPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects, IntPtr buffer, int width, int height)
|
||||
{
|
||||
if (_control.Disposed)
|
||||
return;
|
||||
|
||||
foreach (var dirtyRect in dirtyRects)
|
||||
{
|
||||
Buffer.UpdateBuffer(width, height, buffer, dirtyRect);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAcceleratedPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects, IntPtr sharedHandle)
|
||||
{
|
||||
// Unused, but we're forced to implement it so.. NOOP.
|
||||
}
|
||||
|
||||
protected override void OnScrollOffsetChanged(CefBrowser browser, double x, double y)
|
||||
{
|
||||
if (_control.Disposed)
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void OnImeCompositionRangeChanged(CefBrowser browser, CefRange selectedRange, CefRectangle[] characterBounds)
|
||||
{
|
||||
if (_control.Disposed)
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO CEF: Do we need this?
|
||||
private class AccessibilityHandler : CefAccessibilityHandler
|
||||
{
|
||||
protected override void OnAccessibilityTreeChange(CefValue value)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnAccessibilityLocationChange(CefValue value)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
182
Robust.Client.CEF/CefManager.cs
Normal file
182
Robust.Client.CEF/CefManager.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
// The library we're using right now. TODO CEF: Do we want to use something else? We will need to ship it ourselves if so.
|
||||
using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.CEF
|
||||
{
|
||||
// Register this with IoC.
|
||||
// TODO CEF: think if making this inherit CefApp is a good idea...
|
||||
// TODO CEF: A way to handle external window browsers...
|
||||
[UsedImplicitly]
|
||||
public class CefManager : CefApp, IPostInjectInit
|
||||
{
|
||||
[Dependency] private readonly IConsoleHost _consoleHost = default!;
|
||||
|
||||
private readonly BrowserProcessHandler _browserProcessHandler;
|
||||
private readonly RenderProcessHandler _renderProcessHandler;
|
||||
private bool _initialized = false;
|
||||
|
||||
public CefManager()
|
||||
{
|
||||
// Probably not needed?
|
||||
_renderProcessHandler = new RenderProcessHandler();
|
||||
_browserProcessHandler = new BrowserProcessHandler();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this to initialize CEF.
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
DebugTools.Assert(!_initialized);
|
||||
|
||||
// Register this funny command for easy debugging.
|
||||
// TODO CEF: Actually make this command work. I used to have this in content before because it was easier...
|
||||
_consoleHost.RegisterCommand("browse", "Opens an embedded web browser in an in-game window!", "browse <url>", BrowseCommand);
|
||||
|
||||
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.
|
||||
|
||||
// TODO CEF Unhardcode these paths below somehow, it seems CEF needs paths in the actual disk for these...
|
||||
|
||||
// Multi-process currently doesn't work...
|
||||
BrowserSubprocessPath = "/home/zumo/Projects/space-station-14/bin/Content.Client/Robust.Client.CEF",
|
||||
|
||||
// I don't think this is needed? Research.
|
||||
LocalesDirPath = "/home/zumo/Projects/space-station-14/bin/Content.Client/locales/",
|
||||
|
||||
// I don't think this is needed either? Do research.
|
||||
ResourcesDirPath = "/home/zumo/Projects/space-station-14/bin/Content.Client/",
|
||||
};
|
||||
|
||||
Logger.Info($"CEF Version: {CefRuntime.ChromeVersion}");
|
||||
|
||||
// --------------------------- README --------------------------------------------------
|
||||
// By the way! You're gonna need the CEF binaries in your client's bin folder.
|
||||
// More specifically, version cef_binary_91.1.21+g9dd45fe+chromium-91.0.4472.114
|
||||
// https://cef-builds.spotifycdn.com/cef_binary_91.1.21%2Bg9dd45fe%2Bchromium-91.0.4472.114_windows64_minimal.tar.bz2
|
||||
// https://cef-builds.spotifycdn.com/cef_binary_91.1.21%2Bg9dd45fe%2Bchromium-91.0.4472.114_linux64_minimal.tar.bz2
|
||||
// Here's how to get it to work:
|
||||
// 1. Copy all the contents of "Release" to the bin folder.
|
||||
// 2. Copy all the contents of "Resources" to the bin folder.
|
||||
// Supposedly, you should just need libcef.so in Release and icudtl.dat in Resources...
|
||||
// The rest might be optional.
|
||||
// Maybe. Good luck! If you get odd crashes with no info and a weird exit code, use GDB!
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
// We pass no main arguments...
|
||||
CefRuntime.Initialize(new CefMainArgs(Array.Empty<string>()), settings, this, IntPtr.Zero);
|
||||
|
||||
// TODO CEF: After this point, debugging breaks. No, literally. My client crashes but ONLY with the debugger.
|
||||
// I have tried using the DEBUG and RELEASE versions of libcef.so, stripped or non-stripped...
|
||||
// And nothing seemed to work. Odd.
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void BrowseCommand(IConsoleShell shell, string argstr, string[] args)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
shell.WriteError("CEF is not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
shell.WriteError("Incorrect amount of arguments! Must be a single one.");
|
||||
return;
|
||||
}
|
||||
|
||||
var window = new SS14Window();
|
||||
|
||||
var browser = new BrowserControl();
|
||||
|
||||
if (args.Length < 1)
|
||||
return;
|
||||
|
||||
browser.MouseFilter = Control.MouseFilterMode.Stop;
|
||||
window.MouseFilter = Control.MouseFilterMode.Pass;
|
||||
window.Contents.AddChild(browser);
|
||||
|
||||
browser.Browse(args[0]);
|
||||
|
||||
window.Open();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Needs to be called regularly for CEF to keep working.
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
DebugTools.Assert(_initialized);
|
||||
|
||||
// Calling this makes CEF do its work, without using its own update loop.
|
||||
CefRuntime.DoMessageLoopWork();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call before program shutdown.
|
||||
/// </summary>
|
||||
public void Shutdown()
|
||||
{
|
||||
DebugTools.Assert(_initialized);
|
||||
|
||||
Dispose(true);
|
||||
|
||||
CefRuntime.Shutdown();
|
||||
}
|
||||
|
||||
protected override CefBrowserProcessHandler GetBrowserProcessHandler()
|
||||
{
|
||||
return _browserProcessHandler;
|
||||
}
|
||||
|
||||
protected override CefRenderProcessHandler GetRenderProcessHandler()
|
||||
{
|
||||
return _renderProcessHandler;
|
||||
}
|
||||
|
||||
protected override void OnBeforeCommandLineProcessing(string processType, CefCommandLine commandLine)
|
||||
{
|
||||
// Disable zygote. TODO CEF: Do research on this?
|
||||
commandLine.AppendSwitch("--no-zygote");
|
||||
|
||||
// We use single-process for now as multi-process requires us to ship a native program
|
||||
commandLine.AppendSwitch("--single-process");
|
||||
|
||||
// We do CPU rendering, disable the GPU...
|
||||
commandLine.AppendSwitch("--disable-gpu");
|
||||
commandLine.AppendSwitch("--disable-gpu-compositing");
|
||||
commandLine.AppendSwitch("--in-process-gpu");
|
||||
|
||||
Logger.Debug($"{commandLine}");
|
||||
}
|
||||
|
||||
// TODO CEF: Research - Is this even needed?
|
||||
private class BrowserProcessHandler : CefBrowserProcessHandler
|
||||
{
|
||||
}
|
||||
|
||||
// TODO CEF: Research - Is this even needed?
|
||||
private class RenderProcessHandler : CefRenderProcessHandler
|
||||
{
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Robust.Client.CEF/Program.cs
Normal file
46
Robust.Client.CEF/Program.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.CEF
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
// This was supposed to be the main entry for the subprocess program... It doesn't work.
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
CefRuntime.Load();
|
||||
|
||||
var mainArgs = new CefMainArgs(args);
|
||||
var app = new SubprocessApp();
|
||||
|
||||
// This will block executing IF this is a proper subprocess but it was broken and it returned -1
|
||||
// -1 means this process is the main method which... Wasn't possible.
|
||||
// We probably need a native program?
|
||||
var code = CefRuntime.ExecuteProcess(mainArgs, app, IntPtr.Zero);
|
||||
|
||||
if (code != 0)
|
||||
{
|
||||
System.Console.WriteLine($"CEF Subprocess exited with exit code {code}! Arguments: {string.Join(' ', args)}");
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Not really needed.
|
||||
private class SubprocessApp : CefApp
|
||||
{
|
||||
protected override void OnBeforeCommandLineProcessing(string processType, CefCommandLine commandLine)
|
||||
{
|
||||
base.OnBeforeCommandLineProcessing(processType, commandLine);
|
||||
|
||||
// Just the same stuff as in CefManager.
|
||||
commandLine.AppendSwitch("--no-zygote");
|
||||
commandLine.AppendSwitch("--disable-gpu");
|
||||
commandLine.AppendSwitch("--disable-gpu-compositing");
|
||||
commandLine.AppendSwitch("--in-process-gpu");
|
||||
|
||||
System.Console.WriteLine($"SUBPROCESS COMMAND LINE: {commandLine.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Robust.Client.CEF/Robust.Client.CEF.csproj
Normal file
25
Robust.Client.CEF/Robust.Client.CEF.csproj
Normal file
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<OutputType>WinExe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.DefineConstants.targets" />
|
||||
<Target Name="RobustAfterBuild" AfterTargets="Build" />
|
||||
<Import Project="..\MSBuild\Robust.Engine.targets" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\cefglue\CefGlue\CefGlue.csproj" />
|
||||
<ProjectReference Include="..\Robust.Client\Robust.Client.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
20
Robust.Client.CEF/RobustWebClient.cs
Normal file
20
Robust.Client.CEF/RobustWebClient.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.CEF
|
||||
{
|
||||
// Simple web client.
|
||||
internal class RobustWebClient : CefClient
|
||||
{
|
||||
private readonly CefRenderHandler _renderHandler;
|
||||
|
||||
internal RobustWebClient(CefRenderHandler handler)
|
||||
{
|
||||
_renderHandler = handler;
|
||||
}
|
||||
|
||||
protected override CefRenderHandler GetRenderHandler()
|
||||
{
|
||||
return _renderHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
cefglue
Submodule
1
cefglue
Submodule
Submodule cefglue added at 7810f236c5
Reference in New Issue
Block a user