├── .gitignore ├── BLUI.uplugin ├── CREDITS.md ├── Content ├── BluiWidget.uasset ├── BluiWorldWidgetActorExample.uasset └── InteractableBluiWidgetActor.uasset ├── LICENSE ├── README.md ├── Resources └── Icon128.png ├── Source ├── Blu │ ├── Blu.Build.cs │ ├── Private │ │ ├── Blu.cpp │ │ ├── BluBluprintFunctionLibrary.cpp │ │ ├── BluEye.cpp │ │ ├── BluJsonObj.cpp │ │ ├── BluManager.cpp │ │ └── RenderHandler.cpp │ └── Public │ │ ├── BluBlueprintFunctionLibrary.h │ │ ├── BluEye.h │ │ ├── BluJsonObj.h │ │ ├── BluManager.h │ │ ├── BluTypes.h │ │ ├── CEFInclude.h │ │ ├── IBlu.h │ │ └── RenderHandler.h └── BluLoader │ ├── BluLoader.Build.cs │ ├── Private │ └── BluLoader.cpp │ └── Public │ └── IBluLoader.h ├── ThirdParty └── cef │ └── .gitkeep └── builder ├── README.md └── archived_builder.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source files for the engine to use 2 | Intermediate/* 3 | Binaries/* 4 | ThirdParty/cef/Win/* -------------------------------------------------------------------------------- /BLUI.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "FriendlyName": "BLUI", 4 | "Version": 3, 5 | "VersionName": "4.10.0", 6 | "Description": "Chromium Embedded Framework (CEF) powered HTML UI and HUD for Unreal Engine 4", 7 | "Category": "UI", 8 | "CreatedBy": "Aaron M. Shea, Getnamo, & Contributors", 9 | "CreatedByURL": "github.com/getnamo", 10 | "DocsURL": "https://github.com/getnamo/BLUI", 11 | "EnabledByDefault" : true, 12 | "CanContainContent": true, 13 | "Modules": 14 | [ 15 | { 16 | "Name" : "BluLoader", 17 | "Type" : "Runtime", 18 | "LoadingPhase" : "PreDefault", 19 | "BlacklistPlatforms" : ["Android", "IOS"] 20 | }, 21 | { 22 | "Name": "Blu", 23 | "Type": "Runtime", 24 | "LoadingPhase": "PreDefault", 25 | "BlacklistPlatforms": ["Android", "IOS" ] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | # BluI third-party Licenses 2 | 3 | BluI makes use of various third-party code and libraries. Below are the names and their appropriate licenses: 4 | 5 | ## VaQuoleUI 6 | 7 | ``` 8 | The MIT License (MIT) 9 | 10 | Copyright (c) 2014 Vladimir Alyamkin 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in all 20 | copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | SOFTWARE. 29 | ``` 30 | 31 | ## Chromium Embedded Framework (CEF) 32 | 33 | ``` 34 | Copyright (c) 2008-2014 Marshall A. Greenblatt. Portions Copyright (c) 35 | 2006-2009 Google Inc. All rights reserved. 36 | 37 | Redistribution and use in source and binary forms, with or without 38 | modification, are permitted provided that the following conditions are 39 | met: 40 | 41 | * Redistributions of source code must retain the above copyright 42 | notice, this list of conditions and the following disclaimer. 43 | * Redistributions in binary form must reproduce the above 44 | copyright notice, this list of conditions and the following disclaimer 45 | in the documentation and/or other materials provided with the 46 | distribution. 47 | * Neither the name of Google Inc. nor the name Chromium Embedded 48 | Framework nor the names of its contributors may be used to endorse 49 | or promote products derived from this software without specific prior 50 | written permission. 51 | 52 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 53 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 54 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 55 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 56 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 57 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 58 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 59 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 60 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 61 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 62 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63 | ``` 64 | -------------------------------------------------------------------------------- /Content/BluiWidget.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getnamo/BLUI-Unreal/0b3d661ff52f2897c835210b1bff9a19c35032e9/Content/BluiWidget.uasset -------------------------------------------------------------------------------- /Content/BluiWorldWidgetActorExample.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getnamo/BLUI-Unreal/0b3d661ff52f2897c835210b1bff9a19c35032e9/Content/BluiWorldWidgetActorExample.uasset -------------------------------------------------------------------------------- /Content/InteractableBluiWidgetActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getnamo/BLUI-Unreal/0b3d661ff52f2897c835210b1bff9a19c35032e9/Content/InteractableBluiWidgetActor.uasset -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Aaron M. Shea (AaronShea) 4 | Copyright (c) 2016-present, Jan Kaniewski (Getnamo) & Contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![release](https://img.shields.io/github/release/getnamo/BLUI.svg?style=flat-square)](https://github.com/getnamo/BLUI-Unreal/releases) 2 | ![BLUI-logo](https://cloud.githubusercontent.com/assets/1334174/5969395/201a1202-a7f1-11e4-98a4-12bc6793f830.png) 3 | [![Github All Releases](https://img.shields.io/github/downloads/getnamo/BLUI-Unreal/total.svg)](https://github.com/getnamo/BLUI-Unreal/releases) 4 | 5 | ## Getnamo Fork Notes 6 | 7 | A fork of BLUI that is kept relatively up to date. 8 | 9 | Latest release is updated to [CEF 90.0.4430.212](https://bitbucket.org/chromiumembedded/cef/src/4430/). See https://github.com/getnamo/BLUbrowser for repo for cef process build. 10 | 11 | To install check out the latest releases https://github.com/getnamo/BLUI-Unreal/releases and drag and drop *Plugins* folder into your project root folder 12 | 13 | Unreal thread: https://forums.unrealengine.com/community/released-projects/29036-blui-open-source-html5-js-css-hud-ui 14 | 15 | [Discord Server](https://discord.gg/qfJUyxaW4s) 16 | 17 | ### Convenience Blueprints 18 | 19 | The native plugin didn't contain any self contained drag and drop examples so I've added some. 20 | 21 | ![examples](https://i.imgur.com/UOCEHM8.png) 22 | 23 | ### BluiWidget 24 | 25 | A user widget (UMG) blueprint which embeds a BLU texture as an image brush. Contains various utility functions to allow loading and parsing urls easily. Call ```InitBluEye``` with starting url and browser window size to start this widget. See *BluiWorldWidgetActorExample* for an example of how it's used in practice. 26 | 27 | 28 | ### BluiWorldWidgetActorExample 29 | 30 | Encapsulated *BluiWidget* user widget in an actor. Drag and drop this actor into your scene and it will auto-spawn the required *BluTickActor* to make everything work. 31 | 32 | ![example output](https://i.imgur.com/bso2ah6.png) 33 | 34 | *3 BluiWorldWIdgetActorExample actors with ```youtube.com```, ```blui ue4``` and ```local://test.html``` specified as their URL respectively* 35 | 36 | 37 | #### Url 38 | 39 | By default the actor will check the url for ```local://``` protocol and load local content first if detected. This should be placed inside the following directory: ```{project root}/Content/html```. NB: You can still use the vanilla ```blui://``` protocol which will load content relative to your project root. 40 | 41 | ![local url](https://i.imgur.com/30hk67Z.png) 42 | 43 | *e.g. having a test.html file inside your Content/html folder* 44 | 45 | 46 | Basic URL validity is also tested, but you can safely ignore http:// etc. E.g. just specifying youtube.com will resolve correctly 47 | 48 | ![](https://i.imgur.com/R6we4jO.png) 49 | 50 | 51 | If your URL isn't valid however, it will redirect the string as a search term e.g. typing a sentance or a search term. 52 | 53 | ![auto-search](https://i.imgur.com/iDoXyFy.png) 54 | 55 | 56 | You can untick *Should Auto Search Invalid Url* to disable this behavior. 57 | 58 | #### Resize 59 | By default the actor has a BLUI resolution of 1000x1000, you can change this by just changing the user widget draw size. 60 | 61 | ![resize](https://i.imgur.com/kB8X4I5.png) 62 | 63 | 64 | 65 | ### BluTickActor 66 | 67 | Since 4.0.0 - This actor is no longer needed. Ticking happens internally. 68 | 69 | Older verions: 70 | Instead of ticking in your level bp, I prefer to use a simple actor to do the ticking. Other convenience blueprints may spawn this as necessary so if you use those, you don't ever need to use this directly. 71 | 72 | ## Demo Project 73 | 74 | Thanks to @oivio we have the Demo project updated to the latest release. See https://github.com/oivio/BLUI-Demo 75 | 76 | ### Video of Demo project 77 | 78 | [![Demo Project](https://img.youtube.com/vi/R0xylXhBm-0/0.jpg)](https://youtu.be/R0xylXhBm-0) 79 | 80 | Click on image to take you to video. 81 | 82 | 83 | What is it? 84 | --------------------------------------- 85 | BLUI is an Unreal Engine 4 plugin that allows easy interaction with the Chromium Embedded Framework. It provides a simple Material Instance and input functions to help streamline the rendering of rich HTML interfaces. 86 | 87 | BLUI tries to stay out of the way as much as possible. All rendering of the DOM and processing of the JavaScript happens in a separate process, just like Chromium. BLUI only updates the texture inside the material instance when Chromium requests a redraw, not every tick, saving a bit more processing along the way. 88 | 89 | Features 90 | --------------------------------------- 91 | + Chromium Powered (same thing that powers Google Chrome!) 92 | + Fully compatible with every web technology that Chrome/Chromium works with. (HTML5, WebAudio, WebSockets etc.) 93 | + No specific ties to ***any*** in game class, simple use Blueprints (or C++) to create a new "BluEye" object and grab its material instance, then you can paint it on anything! 94 | + Execute JavaScript in the "browser" from your game to pass data to the web page 95 | + Using `blu_event` JS native function you can pass data from the page's JavaScript back into UE4! 96 | + C++ or Blueprints, works with both! 97 | 98 | Setting up the editor and project 99 | --------------------------------------- 100 | Then copy the `BLUI` folder into the "Plugins" folder within your **project** directory, and enable the plugin. 101 | 102 | Re-generate your project's Visual Studio file and load up the editor. Then check the plugin list to ensure it has been loaded! 103 | 104 | 105 | Loading Local Files 106 | --------------------------------------- 107 | Set your default URL or use the "Load URL" node/method to load a URL that starts with `local://` this will point to the Content/html directory root of the project or the game (if packaged). So if you wanted to load an HTML file from `YourProject/Content/html/UI/file.html`, set the URL to `local://UI/file.html` 108 | 109 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getnamo/BLUI-Unreal/0b3d661ff52f2897c835210b1bff9a19c35032e9/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/Blu/Blu.Build.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnrealBuildTool; 3 | using System.IO; 4 | using System; 5 | 6 | public class Blu : ModuleRules 7 | { 8 | 9 | private string ThirdPartyPath 10 | { 11 | get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../../ThirdParty/")); } 12 | } 13 | 14 | private void stageFiles(String[] FilesToStage) 15 | { 16 | foreach (var File in FilesToStage) 17 | { 18 | RuntimeDependencies.Add(File); 19 | } 20 | } 21 | 22 | public Blu(ReadOnlyTargetRules Target) : base(Target) 23 | { 24 | PublicDependencyModuleNames.AddRange( 25 | new string[] { 26 | "Core", 27 | "CoreUObject", 28 | "Engine", 29 | "Projects", 30 | "InputCore", 31 | "RenderCore", 32 | "RHI", 33 | "Slate", 34 | "SlateCore", 35 | "UMG", 36 | "Json", 37 | "RenderCore" 38 | }); 39 | 40 | PrivateIncludePaths.AddRange( 41 | new string[] { 42 | Path.Combine(ModuleDirectory, "Private"), 43 | }); 44 | 45 | if(Target.Platform == UnrealTargetPlatform.Win64) 46 | { 47 | 48 | PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "cef/Win/lib", "libcef.lib")); 49 | PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "cef/Win/lib", "libcef_dll_wrapper.lib")); 50 | 51 | PublicDelayLoadDLLs.Add("d3dcompiler_43.dll"); 52 | PublicDelayLoadDLLs.Add("d3dcompiler_47.dll"); 53 | PublicDelayLoadDLLs.Add("ffmpegsumo.dll"); 54 | PublicDelayLoadDLLs.Add("libcef.dll"); 55 | PublicDelayLoadDLLs.Add("libEGL.dll"); 56 | PublicDelayLoadDLLs.Add("libGLESv2.dll"); 57 | 58 | PublicIncludePaths.AddRange( 59 | new string[] { 60 | Path.Combine(ThirdPartyPath, "cef/Win") 61 | }); 62 | 63 | // Add our runtime dependencies 64 | var FilesToStage = Directory.GetFiles(Path.Combine(ThirdPartyPath, "cef/Win/shipping"), "*", SearchOption.AllDirectories); 65 | stageFiles(FilesToStage); 66 | 67 | } else if(Target.Platform == UnrealTargetPlatform.Linux) 68 | { 69 | 70 | PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "cef/Linux/lib", "libcef.so")); 71 | PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "cef/Linux/lib", "libcef_dll_wrapper.a")); 72 | 73 | PublicIncludePaths.AddRange( 74 | new string[] { 75 | Path.Combine(ThirdPartyPath, "cef/Linux") 76 | }); 77 | 78 | } else if(Target.Platform == UnrealTargetPlatform.Mac) 79 | { 80 | 81 | var FrameworkPath = Path.Combine(ThirdPartyPath, "cef/Mac/lib", "Chromium Embedded Framework.framework"); 82 | 83 | PublicFrameworks.Add(FrameworkPath); 84 | PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "cef/Mac/lib", "libcef_dll_wrapper.a")); 85 | 86 | PublicIncludePaths.AddRange( 87 | new string[] { 88 | Path.Combine(ThirdPartyPath, "cef", "Mac") 89 | }); 90 | 91 | var FilesToStage = Directory.GetFiles(Path.Combine(ThirdPartyPath, "cef/Mac/shipping"), "*", SearchOption.AllDirectories); 92 | stageFiles(FilesToStage); 93 | 94 | FilesToStage = Directory.GetFiles(Path.Combine(ThirdPartyPath, "cef/Mac/lib"), "*", SearchOption.AllDirectories); 95 | stageFiles(FilesToStage); 96 | 97 | if(!Target.bBuildEditor) 98 | { 99 | AdditionalBundleResources.Add(new BundleResource(Path.Combine(FrameworkPath, "Chromium Embedded Framework"), "MacOS", false)); 100 | } 101 | } 102 | else 103 | { 104 | throw new BuildException("BLUI: Platform not supported"); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Source/Blu/Private/Blu.cpp: -------------------------------------------------------------------------------- 1 | #include "IBlu.h" 2 | #include "Interfaces/IPluginManager.h" 3 | #include "BluManager.h" 4 | 5 | class FBlu : public IBlu 6 | { 7 | 8 | /** IModuleInterface implementation */ 9 | virtual void StartupModule() override 10 | { 11 | CefString GameDirCef = *FPaths::ConvertRelativePathToFull(FPaths::ProjectDir() + "BluCache"); 12 | FString ExecutablePath = FPaths::ConvertRelativePathToFull(IPluginManager::Get().FindPlugin("BLUI")->GetBaseDir() + "/ThirdParty/cef/"); 13 | 14 | // Setup the default settings for BluManager 15 | BluManager::Settings.windowless_rendering_enabled = true; 16 | BluManager::Settings.no_sandbox = true; 17 | BluManager::Settings.remote_debugging_port = 7777; 18 | BluManager::Settings.uncaught_exception_stack_size = 5; 19 | 20 | #if PLATFORM_LINUX 21 | ExecutablePath = "./blu_ue4_process"; 22 | #endif 23 | #if PLATFORM_MAC 24 | ExecutablePath += "Mac/shipping/blu_ue4_process.app/Contents/MacOS/blu_ue4_process"; 25 | #endif 26 | #if PLATFORM_WINDOWS 27 | ExecutablePath += "Win/shipping/blu_ue4_process.exe"; 28 | #endif 29 | 30 | CefString realExePath = *ExecutablePath; 31 | 32 | // Set the sub-process path 33 | CefString(&BluManager::Settings.browser_subprocess_path).FromString(realExePath); 34 | 35 | // Set the cache path 36 | CefString(&BluManager::Settings.cache_path).FromString(GameDirCef); 37 | 38 | // Make a new manager instance 39 | CefRefPtr BluApp = new BluManager(); 40 | 41 | //CefExecuteProcess(BluManager::main_args, BluApp, NULL); 42 | CefInitialize(BluManager::MainArgs, BluManager::Settings, BluApp, NULL); 43 | 44 | UE_LOG(LogBlu, Log, TEXT(" STATUS: Loaded")); 45 | } 46 | 47 | virtual void ShutdownModule() override 48 | { 49 | UE_LOG(LogBlu, Log, TEXT(" STATUS: Shutdown")); 50 | //CefShutdown(); 51 | } 52 | 53 | }; 54 | 55 | 56 | IMPLEMENT_MODULE( FBlu, Blu ) 57 | DEFINE_LOG_CATEGORY(LogBlu); -------------------------------------------------------------------------------- /Source/Blu/Private/BluBluprintFunctionLibrary.cpp: -------------------------------------------------------------------------------- 1 | #include "BluBlueprintFunctionLibrary.h" 2 | #include "BluJsonObj.h" 3 | 4 | 5 | UBluBlueprintFunctionLibrary::UBluBlueprintFunctionLibrary(const class FObjectInitializer& PCIP) 6 | : Super(PCIP) 7 | { 8 | 9 | } 10 | 11 | UBluEye* UBluBlueprintFunctionLibrary::NewBluEye(UObject* WorldContextObject) 12 | { 13 | 14 | UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull); 15 | UBluEye* Eye = NewObject(WorldContextObject); 16 | 17 | return Eye; 18 | 19 | } 20 | 21 | UBluJsonObj* UBluBlueprintFunctionLibrary::NewBluJSONObj(UObject* WorldContextObject) 22 | { 23 | 24 | UBluJsonObj* JsonObj = NewObject(GetTransientPackage(), UBluJsonObj::StaticClass()); 25 | JsonObj->Init("{}"); 26 | 27 | return JsonObj; 28 | 29 | } 30 | 31 | void UBluBlueprintFunctionLibrary::RunBluEventLoop() 32 | { 33 | BluManager::DoBluMessageLoop(); 34 | } 35 | 36 | UBluJsonObj* UBluBlueprintFunctionLibrary::ParseJSON(const FString& JSONString) 37 | { 38 | 39 | UBluJsonObj* JsonObj = NewObject(GetTransientPackage(), UBluJsonObj::StaticClass()); 40 | JsonObj->Init(JSONString); 41 | 42 | return JsonObj; 43 | 44 | } 45 | 46 | FString UBluBlueprintFunctionLibrary::JSONToString(UBluJsonObj *ObjectToParse) 47 | { 48 | 49 | // Create the JSON reader 50 | FString ReturnString; 51 | TSharedRef> writer = TJsonWriterFactory::Create(&ReturnString); 52 | 53 | // Convert the JSON object to an FString 54 | FJsonSerializer::Serialize(ObjectToParse->GetJsonObj().ToSharedRef(), writer); 55 | 56 | return ReturnString; 57 | 58 | } 59 | 60 | FCharacterEvent UBluBlueprintFunctionLibrary::ToKeyEvent(FKey Key) 61 | { 62 | FModifierKeysState KeyState; 63 | 64 | FCharacterEvent CharEvent = FCharacterEvent(Key.GetFName().ToString().ToUpper().GetCharArray()[0], KeyState, 0, 0); 65 | 66 | return CharEvent; 67 | } 68 | 69 | 70 | 71 | FString UBluBlueprintFunctionLibrary::GameRootDirectory() 72 | { 73 | return FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()); 74 | } 75 | 76 | 77 | bool UBluBlueprintFunctionLibrary::HasSubstring(const FString& SearchIn, const FString& Substring, ESearchCase::Type SearchCase /*= ESearchCase::IgnoreCase*/, ESearchDir::Type SearchDir /*= ESearchDir::FromStart*/) 78 | { 79 | return SearchIn.Contains(Substring, SearchCase, SearchDir); 80 | } -------------------------------------------------------------------------------- /Source/Blu/Private/BluEye.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "BluEye.h" 3 | #include "IBlu.h" 4 | #include "RenderHandler.h" 5 | 6 | FTickEventLoopData UBluEye::EventLoopData = FTickEventLoopData(); 7 | 8 | FBluEyeSettings::FBluEyeSettings() 9 | { 10 | FrameRate = 60.f; 11 | 12 | ViewSize.X = 1280; 13 | ViewSize.Y = 720; 14 | 15 | bIsTransparent = false; 16 | bEnableWebGL = true; 17 | bAudioMuted = false; 18 | bAutoPlayEnabled = true; 19 | bDebugLogTick = false; 20 | } 21 | 22 | UBluEye::UBluEye(const class FObjectInitializer& PCIP) 23 | : Super(PCIP) 24 | { 25 | Texture = nullptr; 26 | bValidTexture = false; 27 | } 28 | 29 | void UBluEye::Init() 30 | { 31 | 32 | /** 33 | * We don't want this running in editor unless it's PIE 34 | * If we don't check this, CEF will spawn infinite processes with widget components 35 | **/ 36 | 37 | if (GEngine) 38 | { 39 | if (GEngine->IsEditor() && !GWorld->IsPlayInEditor()) 40 | { 41 | UE_LOG(LogBlu, Log, TEXT("Notice: not playing - Component Will Not Initialize")); 42 | return; 43 | } 44 | } 45 | 46 | if (Settings.ViewSize.X <= 0 || Settings.ViewSize.Y <= 0) 47 | { 48 | UE_LOG(LogBlu, Log, TEXT("Can't initialize when Width or Height are <= 0")); 49 | return; 50 | } 51 | 52 | BrowserSettings.universal_access_from_file_urls = STATE_ENABLED; 53 | BrowserSettings.file_access_from_file_urls = STATE_ENABLED; 54 | 55 | //BrowserSettings.web_security = STATE_DISABLED; 56 | //BrowserSettings.fullscreen_enabled = true; 57 | 58 | Info.width = (int32)Settings.ViewSize.X; 59 | Info.height = (int32)Settings.ViewSize.Y; 60 | 61 | // Set transparant option 62 | Info.SetAsWindowless(0); //bIsTransparent 63 | 64 | // Figure out if we want to turn on WebGL support 65 | if (Settings.bEnableWebGL) 66 | { 67 | if (BluManager::CPURenderSettings) 68 | { 69 | UE_LOG(LogBlu, Error, TEXT("You have enabled WebGL for this browser, but CPU Saver is enabled in BluManager.cpp - WebGL will not work!")); 70 | } 71 | BrowserSettings.webgl = STATE_ENABLED; 72 | } 73 | 74 | //NB: this setting will change it globally for all new instances 75 | BluManager::AutoPlay = Settings.bAutoPlayEnabled; 76 | 77 | Renderer = new RenderHandler(Settings.ViewSize.X, Settings.ViewSize.Y, this); 78 | ClientHandler = new BrowserClient(Renderer); 79 | Browser = CefBrowserHost::CreateBrowserSync( 80 | Info, 81 | ClientHandler.get(), 82 | "about:blank", 83 | BrowserSettings, 84 | NULL, 85 | NULL); 86 | 87 | 88 | Browser->GetHost()->SetWindowlessFrameRate(Settings.FrameRate); 89 | Browser->GetHost()->SetAudioMuted(Settings.bAudioMuted); 90 | 91 | // Setup JS event emitter 92 | ClientHandler->SetEventEmitter(&ScriptEventEmitter); 93 | ClientHandler->SetLogEmitter(&LogEventEmitter); 94 | 95 | UE_LOG(LogBlu, Log, TEXT("Component Initialized")); 96 | UE_LOG(LogBlu, Log, TEXT("Loading URL: %s"), *DefaultURL); 97 | 98 | // Load the default URL 99 | LoadURL(DefaultURL); 100 | ResetTexture(); 101 | 102 | //Instead of manually ticking, we now tick whenever one blu eye is created 103 | SpawnTickEventLoopIfNeeded(); 104 | } 105 | 106 | void UBluEye::ResetTexture() 107 | { 108 | 109 | // Here we init the texture to its initial state 110 | DestroyTexture(); 111 | 112 | bValidTexture = false; 113 | Texture = nullptr; 114 | 115 | 116 | // init the new Texture2D 117 | Texture = UTexture2D::CreateTransient(Settings.ViewSize.X, Settings.ViewSize.Y, PF_B8G8R8A8); 118 | Texture->AddToRoot(); 119 | Texture->UpdateResource(); 120 | 121 | //RenderParams.Texture2DResource = (FTexture2DResource*)Texture->GetResource(); 122 | 123 | ResetMatInstance(); 124 | 125 | bValidTexture = true; 126 | } 127 | 128 | void UBluEye::DestroyTexture() 129 | { 130 | // Here we destroy the texture and its resource 131 | if (Texture) 132 | { 133 | Texture->RemoveFromRoot(); 134 | 135 | FTextureResource* Resource = Texture->GetResource(); 136 | 137 | if (Resource) 138 | { 139 | BeginReleaseResource(Resource); 140 | Texture->UpdateResource(); 141 | 142 | //NB: these lines are the problem for 5.4 causes deadlock on game thread on exit 143 | //StartBatchedRelease(); 144 | //BeginReleaseResource(Texture->GetResource()); // (FRenderCommandPipe*) &UE::RenderCommandPipe::GetPipes()[0] 145 | //EndBatchedRelease(); 146 | //FlushRenderingCommands(); 147 | 148 | /* This is what that command does... 149 | ENQUEUE_RENDER_COMMAND(UpdateBLUICommand)( 150 | [Resource](FRHICommandList& CommandList) 151 | { 152 | Resource->ReleaseResource(); 153 | });*/ 154 | } 155 | Resource = nullptr; 156 | 157 | Texture->MarkAsGarbage(); 158 | Texture = nullptr; 159 | } 160 | bValidTexture = false; 161 | } 162 | 163 | void UBluEye::TextureUpdate(const void *Buffer, FUpdateTextureRegion2D *UpdateRegions, uint32 RegionCount) 164 | { 165 | if (!Browser || !bEnabled) 166 | { 167 | UE_LOG(LogBlu, Warning, TEXT("No Browser access or BluEye not Enabled")) 168 | return; 169 | } 170 | 171 | if (bValidTexture && Texture->IsValidLowLevelFast()) 172 | { 173 | if (Buffer == nullptr) 174 | { 175 | UE_LOG(LogBlu, Warning, TEXT("No Texture Data Buffer")) 176 | return; 177 | } 178 | 179 | FUpdateTextureRegionsData* RegionData = new FUpdateTextureRegionsData; 180 | RegionData->Texture2DResource = (FTextureResource*)Texture->GetResource(); 181 | RegionData->NumRegions = RegionCount; 182 | RegionData->SrcBpp = 4; 183 | RegionData->SrcPitch = int32(Settings.ViewSize.X) * 4; 184 | RegionData->Regions = UpdateRegions; 185 | 186 | //We need to copy this memory or it might get uninitialized 187 | RegionData->SrcData.SetNumUninitialized(RegionData->SrcPitch * int32(Settings.ViewSize.Y)); 188 | FPlatformMemory::Memcpy(RegionData->SrcData.GetData(), Buffer, RegionData->SrcData.Num()); 189 | 190 | ENQUEUE_RENDER_COMMAND(UpdateBLUICommand)( 191 | [RegionData](FRHICommandList& CommandList) 192 | { 193 | for (uint32 RegionIndex = 0; RegionIndex < RegionData->NumRegions; RegionIndex++) 194 | { 195 | RHIUpdateTexture2D(RegionData->Texture2DResource->TextureRHI->GetTexture2D(), 0, RegionData->Regions[RegionIndex], RegionData->SrcPitch, RegionData->SrcData.GetData()); 196 | } 197 | 198 | FMemory::Free(RegionData->Regions); 199 | delete RegionData; 200 | }); 201 | } 202 | else 203 | { 204 | UE_LOG(LogBlu, Warning, TEXT("No Texture or Texture->GetResource()")) 205 | } 206 | } 207 | 208 | void UBluEye::ExecuteJS(const FString& Code) 209 | { 210 | CefString CodeStr = *Code; 211 | Browser->GetMainFrame()->ExecuteJavaScript(CodeStr, "", 0); 212 | } 213 | 214 | void UBluEye::ExecuteJSMethodWithParams(const FString& methodName, const TArray params) 215 | { 216 | 217 | // Empty param string 218 | FString ParamString = "("; 219 | 220 | // Build the param string 221 | for (FString param : params) 222 | { 223 | ParamString += param; 224 | ParamString += ","; 225 | } 226 | 227 | // Remove the last , it's not needed 228 | ParamString.RemoveFromEnd(","); 229 | ParamString += ");"; 230 | 231 | // time to call the function 232 | ExecuteJS(methodName + ParamString); 233 | } 234 | 235 | void UBluEye::LoadURL(const FString& newURL) 236 | { 237 | FString FinalUrl = newURL; 238 | 239 | //Detect chrome-devtools, and re-target them to regular devtools 240 | if (newURL.Contains(TEXT("chrome-devtools://devtools"))) 241 | { 242 | //devtools://devtools/inspector.html?v8only=true&ws=localhost:9229 243 | //browser->GetHost()->ShowDevTools(info, g_handler, browserSettings, CefPoint()); 244 | FinalUrl = FinalUrl.Replace(TEXT("chrome-devtools://devtools/bundled/inspector.html"), TEXT("devtools://devtools/inspector.html")); 245 | } 246 | 247 | // Check if we want to load a local file 248 | if (newURL.Contains(TEXT("blui://"), ESearchCase::IgnoreCase, ESearchDir::FromStart)) 249 | { 250 | 251 | // Get the current working directory 252 | FString GameDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()); 253 | 254 | // We're loading a local file, so replace the proto with our game directory path 255 | FString LocalFile = newURL.Replace(TEXT("blui://"), *GameDir, ESearchCase::IgnoreCase); 256 | 257 | // Now we use the file proto 258 | LocalFile = FString(TEXT("file:///")) + LocalFile; 259 | 260 | UE_LOG(LogBlu, Log, TEXT("Load Local File: %s"), *LocalFile) 261 | 262 | // Load it up 263 | Browser->GetMainFrame()->LoadURL(*LocalFile); 264 | 265 | return; 266 | 267 | } 268 | 269 | // Load as usual 270 | Browser->GetMainFrame()->LoadURL(*FinalUrl); 271 | 272 | } 273 | 274 | FString UBluEye::GetCurrentURL() 275 | { 276 | return FString(Browser->GetMainFrame()->GetURL().c_str()); 277 | } 278 | 279 | void UBluEye::SetZoom(const float Scale /*= 1*/) 280 | { 281 | Browser->GetHost()->SetZoomLevel(Scale); 282 | } 283 | 284 | float UBluEye::GetZoom() 285 | { 286 | return Browser->GetHost()->GetZoomLevel(); 287 | } 288 | 289 | void UBluEye::DownloadFile(const FString& FileUrl) 290 | { 291 | Browser->GetHost()->StartDownload(*FileUrl); 292 | //Todo: ensure downloading works in some way, shape or form? 293 | } 294 | 295 | bool UBluEye::IsBrowserLoading() 296 | { 297 | return Browser->IsLoading(); 298 | } 299 | 300 | void UBluEye::ReloadBrowser(bool IgnoreCache) 301 | { 302 | 303 | if (IgnoreCache) 304 | { 305 | return Browser->ReloadIgnoreCache(); 306 | } 307 | 308 | Browser->Reload(); 309 | 310 | } 311 | 312 | void UBluEye::NavBack() 313 | { 314 | 315 | if (Browser->CanGoBack()) 316 | { 317 | Browser->GoBack(); 318 | } 319 | 320 | } 321 | 322 | void UBluEye::NavForward() 323 | { 324 | 325 | if (Browser->CanGoForward()) 326 | { 327 | Browser->GoForward(); 328 | } 329 | 330 | } 331 | 332 | UTexture2D* UBluEye::ResizeBrowser(const int32 NewWidth, const int32 NewHeight) 333 | { 334 | 335 | if (NewWidth <= 0 || NewHeight <= 0) 336 | { 337 | // We can't do this, just do nothing. 338 | UE_LOG(LogBlu, Log, TEXT("Can't resize when one or both of the sizes are <= 0!")); 339 | return Texture; 340 | } 341 | 342 | // Disable the web view while we resize 343 | bEnabled = false; 344 | 345 | // Set our new Width and Height 346 | Settings.ViewSize.X = NewWidth; 347 | Settings.ViewSize.Y = NewHeight; 348 | 349 | // Update our render handler 350 | Renderer->Width = NewWidth; 351 | Renderer->Height = NewHeight; 352 | 353 | bValidTexture = false; 354 | 355 | Texture = UTexture2D::CreateTransient(Settings.ViewSize.X, Settings.ViewSize.Y, PF_B8G8R8A8); 356 | Texture->AddToRoot(); 357 | Texture->UpdateResource(); 358 | 359 | bValidTexture = true; 360 | 361 | // Let the browser's host know we resized it 362 | Browser->GetHost()->WasResized(); 363 | 364 | // Now we can keep going 365 | bEnabled = true; 366 | 367 | UE_LOG(LogBlu, Log, TEXT("BluEye was resized!")) 368 | 369 | return Texture; 370 | 371 | } 372 | 373 | UTexture2D* UBluEye::CropWindow(const int32 Y, const int32 X, const int32 NewWidth, const int32 NewHeight) 374 | { 375 | // Disable the web view while we resize 376 | bEnabled = false; 377 | 378 | 379 | // Set our new Width and Height 380 | Settings.ViewSize.X = NewWidth; 381 | Settings.ViewSize.Y = NewHeight; 382 | 383 | // Update our render handler 384 | Renderer->Width = NewWidth; 385 | Renderer->Height = NewHeight; 386 | 387 | bValidTexture = false; 388 | 389 | Texture = UTexture2D::CreateTransient(Settings.ViewSize.X, Settings.ViewSize.Y, PF_B8G8R8A8); 390 | Texture->AddToRoot(); 391 | Texture->UpdateResource(); 392 | 393 | bValidTexture = true; 394 | 395 | // Now we can keep going 396 | bEnabled = true; 397 | 398 | UE_LOG(LogBlu, Log, TEXT("BluEye was cropped!")) 399 | 400 | return Texture; 401 | } 402 | 403 | UBluEye* UBluEye::SetProperties(const int32 SetWidth, 404 | const int32 SetHeight, 405 | const bool SetIsTransparent, 406 | const bool SetEnabled, 407 | const bool SetWebGL, 408 | const FString& SetDefaultURL, 409 | const FName& SetTextureParameterName, 410 | UMaterialInterface* SetBaseMaterial) 411 | { 412 | Settings.ViewSize.X = SetWidth; 413 | Settings.ViewSize.Y = SetHeight; 414 | 415 | bEnabled = SetEnabled; 416 | 417 | Settings.bIsTransparent = SetIsTransparent; 418 | Settings.bEnableWebGL = SetWebGL; 419 | BaseMaterial = SetBaseMaterial; 420 | 421 | DefaultURL = SetDefaultURL; 422 | TextureParameterName = SetTextureParameterName; 423 | 424 | return this; 425 | } 426 | 427 | void UBluEye::TriggerMouseMove(const FVector2D& Pos, const float Scale) 428 | { 429 | 430 | MouseEvent.x = Pos.X / Scale; 431 | MouseEvent.y = Pos.Y / Scale; 432 | 433 | Browser->GetHost()->SendFocusEvent(true); 434 | Browser->GetHost()->SendMouseMoveEvent(MouseEvent, false); 435 | 436 | } 437 | 438 | void UBluEye::TriggerLeftClick(const FVector2D& Pos, const float Scale) 439 | { 440 | TriggerLeftMouseDown(Pos, Scale); 441 | TriggerLeftMouseUp(Pos, Scale); 442 | } 443 | 444 | void UBluEye::TriggerRightClick(const FVector2D& Pos, const float Scale) 445 | { 446 | TriggerRightMouseDown(Pos, Scale); 447 | TriggerRightMouseUp(Pos, Scale); 448 | } 449 | 450 | void UBluEye::TriggerLeftMouseDown(const FVector2D& Pos, const float Scale) 451 | { 452 | MouseEvent.x = Pos.X / Scale; 453 | MouseEvent.y = Pos.Y / Scale; 454 | 455 | Browser->GetHost()->SendMouseClickEvent(MouseEvent, MBT_LEFT, false, 1); 456 | } 457 | 458 | void UBluEye::TriggerRightMouseDown(const FVector2D& Pos, const float Scale) 459 | { 460 | MouseEvent.x = Pos.X / Scale; 461 | MouseEvent.y = Pos.Y / Scale; 462 | 463 | Browser->GetHost()->SendMouseClickEvent(MouseEvent, MBT_RIGHT, false, 1); 464 | } 465 | 466 | void UBluEye::TriggerLeftMouseUp(const FVector2D& Pos, const float Scale) 467 | { 468 | MouseEvent.x = Pos.X / Scale; 469 | MouseEvent.y = Pos.Y / Scale; 470 | 471 | Browser->GetHost()->SendMouseClickEvent(MouseEvent, MBT_LEFT, true, 1); 472 | } 473 | 474 | void UBluEye::TriggerRightMouseUp(const FVector2D& Pos, const float Scale) 475 | { 476 | MouseEvent.x = Pos.X / Scale; 477 | MouseEvent.y = Pos.Y / Scale; 478 | 479 | Browser->GetHost()->SendMouseClickEvent(MouseEvent, MBT_RIGHT, true, 1); 480 | } 481 | 482 | void UBluEye::TriggerMouseWheel(const float MouseWheelDelta, const FVector2D& Pos, const float Scale) 483 | { 484 | MouseEvent.x = Pos.X / Scale; 485 | MouseEvent.y = Pos.Y / Scale; 486 | 487 | Browser->GetHost()->SendMouseWheelEvent(MouseEvent, MouseWheelDelta * 10, MouseWheelDelta * 10); 488 | } 489 | 490 | void UBluEye::KeyDown(FKeyEvent InKey) 491 | { 492 | 493 | ProcessKeyMods(InKey); 494 | ProcessKeyCode(InKey); 495 | 496 | KeyEvent.type = KEYEVENT_KEYDOWN; 497 | Browser->GetHost()->SendKeyEvent(KeyEvent); 498 | 499 | } 500 | 501 | void UBluEye::KeyUp(FKeyEvent InKey) 502 | { 503 | 504 | ProcessKeyMods(InKey); 505 | ProcessKeyCode(InKey); 506 | 507 | KeyEvent.type = KEYEVENT_KEYUP; 508 | Browser->GetHost()->SendKeyEvent(KeyEvent); 509 | 510 | } 511 | 512 | void UBluEye::KeyPress(FKeyEvent InKey) 513 | { 514 | 515 | // Simply trigger down, then up key events 516 | KeyDown(InKey); 517 | KeyUp(InKey); 518 | 519 | } 520 | 521 | void UBluEye::ProcessKeyCode(FKeyEvent InKey) 522 | { 523 | KeyEvent.native_key_code = InKey.GetKeyCode(); 524 | KeyEvent.windows_key_code = InKey.GetKeyCode(); 525 | } 526 | 527 | void UBluEye::CharKeyInput(FCharacterEvent CharEvent) 528 | { 529 | 530 | // Process keymods like usual 531 | ProcessKeyMods(CharEvent); 532 | 533 | // Below char input needs some special treatment, se we can't use the normal key down/up methods 534 | 535 | #if PLATFORM_MAC 536 | KeyEvent.character = CharEvent.GetCharacter(); 537 | #else 538 | KeyEvent.windows_key_code = CharEvent.GetCharacter(); 539 | KeyEvent.native_key_code = CharEvent.GetCharacter(); 540 | #endif 541 | KeyEvent.type = KEYEVENT_CHAR; 542 | Browser->GetHost()->SendKeyEvent(KeyEvent); 543 | } 544 | 545 | void UBluEye::CharKeyDownUp(FCharacterEvent CharEvent) 546 | { 547 | // Process keymods like usual 548 | ProcessKeyMods(CharEvent); 549 | 550 | // Below char input needs some special treatment, se we can't use the normal key down/up methods 551 | 552 | #if PLATFORM_MAC 553 | KeyEvent.character = CharEvent.GetCharacter(); 554 | #else 555 | KeyEvent.windows_key_code = CharEvent.GetCharacter(); 556 | KeyEvent.native_key_code = CharEvent.GetCharacter(); 557 | #endif 558 | KeyEvent.type = KEYEVENT_KEYDOWN; 559 | Browser->GetHost()->SendKeyEvent(KeyEvent); 560 | 561 | KeyEvent.type = KEYEVENT_KEYUP; 562 | Browser->GetHost()->SendKeyEvent(KeyEvent); 563 | } 564 | 565 | void UBluEye::RawCharKeyPress(const FString CharToPress, bool isRepeat, 566 | bool LeftShiftDown, 567 | bool RightShiftDown, 568 | bool LeftControlDown, 569 | bool RightControlDown, 570 | bool LeftAltDown, 571 | bool RightAltDown, 572 | bool LeftCommandDown, 573 | bool RightCommandDown, 574 | bool CapsLocksOn) 575 | { 576 | 577 | FModifierKeysState* KeyState = new FModifierKeysState(LeftShiftDown, RightShiftDown, LeftControlDown, 578 | RightControlDown, LeftAltDown, RightAltDown, LeftCommandDown, RightCommandDown, CapsLocksOn); 579 | 580 | FCharacterEvent* CharEvent = new FCharacterEvent(CharToPress.GetCharArray()[0], *KeyState, 0, isRepeat); 581 | 582 | CharKeyInput(*CharEvent); 583 | 584 | } 585 | 586 | void UBluEye::SpecialKeyPress(EBluSpecialKeys Key, bool LeftShiftDown, 587 | bool RightShiftDown, 588 | bool LeftControlDown, 589 | bool RightControlDown, 590 | bool LeftAltDown, 591 | bool RightAltDown, 592 | bool LeftCommandDown, 593 | bool RightCommandDown, 594 | bool CapsLocksOn) 595 | { 596 | 597 | int32 KeyValue = Key; 598 | 599 | KeyEvent.windows_key_code = KeyValue; 600 | KeyEvent.native_key_code = KeyValue; 601 | KeyEvent.type = KEYEVENT_KEYDOWN; 602 | Browser->GetHost()->SendKeyEvent(KeyEvent); 603 | 604 | KeyEvent.windows_key_code = KeyValue; 605 | KeyEvent.native_key_code = KeyValue; 606 | // bits 30 and 31 should be always 1 for WM_KEYUP 607 | KeyEvent.type = KEYEVENT_KEYUP; 608 | Browser->GetHost()->SendKeyEvent(KeyEvent); 609 | 610 | } 611 | 612 | void UBluEye::ProcessKeyMods(FInputEvent InKey) 613 | { 614 | 615 | int Mods = 0; 616 | 617 | // Test alt 618 | if (InKey.IsAltDown()) 619 | { 620 | Mods |= cef_event_flags_t::EVENTFLAG_ALT_DOWN; 621 | } 622 | else 623 | // Test control 624 | if (InKey.IsControlDown()) 625 | { 626 | Mods |= cef_event_flags_t::EVENTFLAG_CONTROL_DOWN; 627 | } 628 | else 629 | // Test shift 630 | if (InKey.IsShiftDown()) 631 | { 632 | Mods |= cef_event_flags_t::EVENTFLAG_SHIFT_DOWN; 633 | } 634 | 635 | KeyEvent.modifiers = Mods; 636 | 637 | } 638 | 639 | void UBluEye::SpawnTickEventLoopIfNeeded() 640 | { 641 | if (!EventLoopData.DelegateHandle.IsValid()) 642 | { 643 | EventLoopData.DelegateHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([&](float DeltaTime) 644 | { 645 | if (EventLoopData.bShouldTickEventLoop) 646 | { 647 | if (Settings.bDebugLogTick) 648 | { 649 | UE_LOG(LogTemp, Log, TEXT("Delta: %1.2f"), DeltaTime); 650 | } 651 | 652 | //NB: this wrapper doesn't crash, but will fail to render 653 | //Async(EAsyncExecution::ThreadPool, [this] 654 | //{ 655 | BluManager::DoBluMessageLoop(); 656 | //}); 657 | 658 | } 659 | 660 | return true; 661 | })); 662 | } 663 | 664 | EventLoopData.EyeCount++; 665 | } 666 | 667 | UTexture2D* UBluEye::GetTexture() const 668 | { 669 | if (!Texture) 670 | { 671 | return UTexture2D::CreateTransient(Settings.ViewSize.X, Settings.ViewSize.Y, PF_B8G8R8A8); 672 | } 673 | 674 | return Texture; 675 | } 676 | 677 | void UBluEye::ResetMatInstance() 678 | { 679 | if (!Texture || !BaseMaterial || TextureParameterName.IsNone()) 680 | { 681 | return; 682 | } 683 | 684 | // Create material instance 685 | if (!MaterialInstance) 686 | { 687 | MaterialInstance = UMaterialInstanceDynamic::Create(BaseMaterial, NULL); 688 | if (!MaterialInstance) 689 | { 690 | UE_LOG(LogBlu, Warning, TEXT("UI Material instance can't be created")); 691 | return; 692 | } 693 | } 694 | 695 | // Check again, we must have material instance 696 | if (!MaterialInstance) 697 | { 698 | UE_LOG(LogBlu, Error, TEXT("UI Material instance wasn't created")); 699 | return; 700 | } 701 | 702 | // Check we have desired parameter 703 | UTexture* Tex = nullptr; 704 | if (!MaterialInstance->GetTextureParameterValue(TextureParameterName, Tex)) 705 | { 706 | UE_LOG(LogBlu, Warning, TEXT("UI Material instance Texture parameter not found")); 707 | return; 708 | } 709 | 710 | MaterialInstance->SetTextureParameterValue(TextureParameterName, Texture); 711 | } 712 | 713 | void UBluEye::CloseBrowser() 714 | { 715 | BeginDestroy(); 716 | 717 | /*if (Browser) 718 | { 719 | // Close up the browser 720 | Browser->GetHost()->SetAudioMuted(true); 721 | Browser->GetMainFrame()->LoadURL("about:blank"); 722 | //browser->GetMainFrame()->Delete(); 723 | Browser->GetHost()->CloseDevTools(); 724 | Browser->GetHost()->CloseBrowser(true); 725 | Browser = nullptr; 726 | 727 | UE_LOG(LogBlu, Warning, TEXT("Browser Closing")); 728 | } 729 | 730 | DestroyTexture(); 731 | 732 | //Remove our auto-ticking setup 733 | EventLoopData.EyeCount--; 734 | if (EventLoopData.EyeCount <= 0) 735 | { 736 | FTSTicker::GetCoreTicker().RemoveTicker(EventLoopData.DelegateHandle); 737 | EventLoopData.DelegateHandle = FTSTicker::FDelegateHandle(); 738 | }*/ 739 | } 740 | 741 | void UBluEye::BeginDestroy() 742 | { 743 | if (Browser) 744 | { 745 | // Close up the browser 746 | Browser->GetHost()->SetAudioMuted(true); 747 | Browser->GetMainFrame()->LoadURL("about:blank"); 748 | //Browser->GetMainFrame()->Delete(); 749 | Browser->GetHost()->CloseDevTools(); 750 | Browser->GetHost()->CloseBrowser(true); 751 | Browser = nullptr; 752 | 753 | UE_LOG(LogBlu, Warning, TEXT("Browser Closing")); 754 | } 755 | 756 | DestroyTexture(); 757 | SetFlags(RF_BeginDestroyed); 758 | 759 | //Remove our auto-ticking setup 760 | EventLoopData.EyeCount--; 761 | if (EventLoopData.EyeCount <= 0) 762 | { 763 | FTSTicker::GetCoreTicker().RemoveTicker(EventLoopData.DelegateHandle); 764 | EventLoopData.DelegateHandle = FTSTicker::FDelegateHandle(); 765 | } 766 | Super::BeginDestroy(); 767 | } 768 | 769 | void UBluEye::SetShouldTickEventLoop(bool ShouldTick /*= true*/) 770 | { 771 | EventLoopData.bShouldTickEventLoop = ShouldTick; 772 | } 773 | -------------------------------------------------------------------------------- /Source/Blu/Private/BluJsonObj.cpp: -------------------------------------------------------------------------------- 1 | #include "BluJsonObj.h" 2 | #include "IBlu.h" 3 | #include "Json.h" 4 | 5 | UBluJsonObj::UBluJsonObj(const class FObjectInitializer& PCIP) 6 | : Super(PCIP) 7 | { 8 | 9 | } 10 | 11 | void UBluJsonObj::Init(const FString &StringData) 12 | { 13 | StrData = *StringData; 14 | 15 | TSharedRef> JsonReader = TJsonReaderFactory::Create(StringData); 16 | DoParseJson(JsonReader); 17 | } 18 | 19 | FString UBluJsonObj::GetStringValue(const FString& Index) 20 | { 21 | return JsonParsed->GetStringField(Index); 22 | } 23 | 24 | bool UBluJsonObj::GetBooleanValue(const FString &Index) 25 | { 26 | return JsonParsed->GetBoolField(Index); 27 | } 28 | 29 | float UBluJsonObj::GetNumValue(const FString &Index) 30 | { 31 | return JsonParsed->GetNumberField(Index); 32 | } 33 | 34 | UBluJsonObj* UBluJsonObj::GetNestedObject(const FString &Index) 35 | { 36 | TSharedPtr NewJson = JsonParsed->GetObjectField(Index); 37 | 38 | if (!NewJson.IsValid()) 39 | { 40 | return nullptr; 41 | } 42 | 43 | // Make our new Temp obj 44 | UBluJsonObj* TempObj = NewObject(GetTransientPackage(), UBluJsonObj::StaticClass()); 45 | TempObj->SetJsonObj(NewJson); 46 | 47 | // return it 48 | return TempObj; 49 | } 50 | 51 | TArray UBluJsonObj::GetNumArray(const FString &Index) 52 | { 53 | TArray Temp; 54 | 55 | for (TSharedPtr Val : JsonParsed->GetArrayField(Index)) 56 | { 57 | 58 | Temp.Add(Val->AsNumber()); 59 | 60 | } 61 | 62 | return Temp; 63 | } 64 | 65 | TArray UBluJsonObj::GetBooleanArray(const FString &Index) 66 | { 67 | TArray Temp; 68 | 69 | for (TSharedPtr Val : JsonParsed->GetArrayField(Index)) 70 | { 71 | 72 | Temp.Add(Val->AsBool()); 73 | 74 | } 75 | 76 | return Temp; 77 | } 78 | 79 | TArray UBluJsonObj::GetStringArray(const FString &Index) 80 | { 81 | TArray Temp; 82 | 83 | for (TSharedPtr Val : JsonParsed->GetArrayField(Index)) 84 | { 85 | 86 | Temp.Add(Val->AsString()); 87 | 88 | } 89 | 90 | return Temp; 91 | } 92 | 93 | 94 | void UBluJsonObj::SetStringValue(const FString &Value, const FString &Index) 95 | { 96 | JsonParsed->SetStringField(Index, Value); 97 | } 98 | 99 | void UBluJsonObj::SetNumValue(const float Value, const FString &Index) 100 | { 101 | JsonParsed->SetNumberField(Index, Value); 102 | } 103 | 104 | void UBluJsonObj::SetBooleanValue(const bool Value, const FString &Index) 105 | { 106 | JsonParsed->SetBoolField(Index, Value); 107 | } 108 | 109 | void UBluJsonObj::SetNestedObject(UBluJsonObj *Value, const FString &Index) 110 | { 111 | JsonParsed->SetObjectField(Index, Value->GetJsonObj()); 112 | } 113 | 114 | void UBluJsonObj::SetJsonObj(TSharedPtr NewJson) 115 | { 116 | // Set our new stored JSON object 117 | JsonParsed = NewJson; 118 | } 119 | 120 | TSharedPtr UBluJsonObj::GetJsonObj() 121 | { 122 | return JsonParsed; 123 | } 124 | 125 | void UBluJsonObj::DoParseJson(TSharedRef> JsonReader) 126 | { 127 | if (!FJsonSerializer::Deserialize(JsonReader, JsonParsed)) 128 | { 129 | UE_LOG(LogBlu, Warning, TEXT("JSON STRING FAILED TO PARSE! WILL DEFAULT TO EMPTY OBJECT {}")); 130 | 131 | // Make an empty json object to prevent crashing 132 | DoParseJson(TJsonReaderFactory::Create("{}")); 133 | } 134 | } 135 | 136 | // CUSTOM ADDED START 137 | void UBluJsonObj::SetStringArray(const TArray &Value, const FString &Index) 138 | { 139 | TArray> ValueArray; 140 | 141 | for (FString Val : Value) 142 | { 143 | ValueArray.Add(MakeShareable(new FJsonValueString(Val))); 144 | } 145 | 146 | JsonParsed->SetArrayField(Index, ValueArray); 147 | } 148 | 149 | void UBluJsonObj::SetBooleanArray(const TArray &Value, const FString &Index) 150 | { 151 | TArray> ValueArray; 152 | 153 | for (bool Val : Value) 154 | { 155 | ValueArray.Add(MakeShareable(new FJsonValueBoolean(Val))); 156 | } 157 | 158 | JsonParsed->SetArrayField(Index, ValueArray); 159 | } 160 | 161 | void UBluJsonObj::SetNumArray(const TArray &Value, const FString &Index) 162 | { 163 | TArray> ValueArray; 164 | 165 | for (float Val : Value) 166 | { 167 | ValueArray.Add(MakeShareable(new FJsonValueNumber(Val))); 168 | } 169 | 170 | JsonParsed->SetArrayField(Index, ValueArray); 171 | } 172 | 173 | void UBluJsonObj::SetObjectArray(const TArray &Value, const FString &Index) 174 | { 175 | TArray> ValueArray; 176 | 177 | for (UBluJsonObj* Val : Value) 178 | { 179 | ValueArray.Add(MakeShareable(new FJsonValueObject(Val->GetJsonObj()))); 180 | } 181 | 182 | JsonParsed->SetArrayField(Index, ValueArray); 183 | } 184 | // CUSTOM ADDED END 185 | -------------------------------------------------------------------------------- /Source/Blu/Private/BluManager.cpp: -------------------------------------------------------------------------------- 1 | #include "BluManager.h" 2 | 3 | BluManager::BluManager() 4 | { 5 | } 6 | 7 | void BluManager::OnBeforeCommandLineProcessing(const CefString& process_type, 8 | CefRefPtr< CefCommandLine > CommandLine) 9 | { 10 | 11 | ///////////////// 12 | /** 13 | * Used to pick command line switches 14 | * If set to "true": CEF will use less CPU, but rendering performance will be lower. CSS3 and WebGL are not be usable 15 | * If set to "false": CEF will use more CPU, but rendering will be better, CSS3 and WebGL will also be usable 16 | */ 17 | BluManager::CPURenderSettings = false; 18 | ///////////////// 19 | 20 | CommandLine->AppendSwitch("off-screen-rendering-enabled"); 21 | CommandLine->AppendSwitchWithValue("off-screen-frame-rate", "60"); 22 | CommandLine->AppendSwitch("enable-font-antialiasing"); 23 | CommandLine->AppendSwitch("enable-media-stream"); 24 | 25 | // Should we use the render settings that use less CPU? 26 | if (CPURenderSettings) 27 | { 28 | CommandLine->AppendSwitch("disable-gpu"); 29 | CommandLine->AppendSwitch("disable-gpu-compositing"); 30 | CommandLine->AppendSwitch("enable-begin-frame-scheduling"); 31 | } 32 | else 33 | { 34 | // Enables things like CSS3 and WebGL 35 | CommandLine->AppendSwitch("enable-gpu-rasterization"); 36 | CommandLine->AppendSwitch("enable-webgl"); 37 | CommandLine->AppendSwitch("disable-web-security"); 38 | } 39 | 40 | CommandLine->AppendSwitchWithValue("enable-blink-features", "HTMLImports"); 41 | 42 | if (AutoPlay) 43 | { 44 | CommandLine->AppendSwitchWithValue("autoplay-policy", "no-user-gesture-required"); 45 | } 46 | 47 | // Append more command line options here if you want 48 | // Visit Peter Beverloo's site: http://peter.sh/experiments/chromium-command-line-switches/ for more info on the switches 49 | 50 | } 51 | 52 | void BluManager::DoBluMessageLoop() 53 | { 54 | CefDoMessageLoopWork(); 55 | } 56 | 57 | CefSettings BluManager::Settings; 58 | CefMainArgs BluManager::MainArgs; 59 | bool BluManager::CPURenderSettings = false; 60 | bool BluManager::AutoPlay = true; -------------------------------------------------------------------------------- /Source/Blu/Private/RenderHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "RenderHandler.h" 2 | #include "Interfaces/IPluginManager.h" 3 | #include "BluEye.h" 4 | 5 | RenderHandler::RenderHandler(int32 Width, int32 Height, UBluEye* UI) 6 | { 7 | this->Width = Width; 8 | this->Height = Height; 9 | this->ParentUI = UI; 10 | } 11 | 12 | void RenderHandler::GetViewRect(CefRefPtr Browser, CefRect &Rect) 13 | { 14 | Rect = CefRect(0, 0, Width, Height); 15 | } 16 | 17 | void RenderHandler::OnPaint(CefRefPtr Browser, PaintElementType Type, const RectList &DirtyRects, const void *Buffer, int InWidth, int InHeight) 18 | { 19 | FUpdateTextureRegion2D *UpdateRegions = static_cast(FMemory::Malloc(sizeof(FUpdateTextureRegion2D) * DirtyRects.size())); 20 | 21 | int RegionIndex = 0; 22 | for (auto DirtyRect : DirtyRects) 23 | { 24 | UpdateRegions[RegionIndex].DestX = UpdateRegions[RegionIndex].SrcX = DirtyRect.x; 25 | UpdateRegions[RegionIndex].DestY = UpdateRegions[RegionIndex].SrcY = DirtyRect.y; 26 | UpdateRegions[RegionIndex].Height = DirtyRect.height; 27 | UpdateRegions[RegionIndex].Width = DirtyRect.width; 28 | 29 | RegionIndex++; 30 | } 31 | 32 | // Trigger our parent UIs Texture to update 33 | ParentUI->TextureUpdate(Buffer, UpdateRegions, DirtyRects.size()); 34 | } 35 | 36 | void BrowserClient::OnAfterCreated(CefRefPtr Browser) 37 | { 38 | //CEF_REQUIRE_UI_THREAD(); 39 | if (!BrowserRef.get()) 40 | { 41 | // Keep a reference to the main browser. 42 | BrowserRef = Browser; 43 | BrowserId = Browser->GetIdentifier(); 44 | } 45 | } 46 | 47 | void BrowserClient::OnBeforeClose(CefRefPtr Browser) 48 | { 49 | //CEF_REQUIRE_UI_THREAD(); 50 | if (BrowserId == Browser->GetIdentifier()) 51 | { 52 | BrowserRef = NULL; 53 | } 54 | } 55 | 56 | bool BrowserClient::OnConsoleMessage(CefRefPtr Browser, cef_log_severity_t Level, const CefString& Message, const CefString& source, int line) 57 | { 58 | FString LogMessage = FString(Message.c_str()); 59 | LogEmitter->Broadcast(LogMessage); 60 | return true; 61 | } 62 | 63 | void BrowserClient::OnFullscreenModeChange(CefRefPtr< CefBrowser > Browser, bool Fullscreen) 64 | { 65 | UE_LOG(LogTemp, Log, TEXT("Changed to Fullscreen: %d"), Fullscreen); 66 | } 67 | 68 | void BrowserClient::OnTitleChange(CefRefPtr< CefBrowser > Browser, const CefString& Title) 69 | { 70 | FString TitleMessage = FString(Title.c_str()); 71 | LogEmitter->Broadcast(TitleMessage); 72 | } 73 | 74 | CefRefPtr BrowserClient::GetCEFBrowser() 75 | { 76 | return BrowserRef; 77 | } 78 | 79 | bool BrowserClient::OnProcessMessageReceived(CefRefPtr Browser, CefRefPtr Frame, CefProcessId SourceProcess, CefRefPtr Message) 80 | { 81 | FString Data; 82 | FString Name = FString(UTF8_TO_TCHAR(Message->GetArgumentList()->GetString(0).ToString().c_str())); 83 | FString Type = FString(UTF8_TO_TCHAR(Message->GetArgumentList()->GetString(2).ToString().c_str())); 84 | FString DataType = FString(UTF8_TO_TCHAR(Message->GetArgumentList()->GetString(3).ToString().c_str())); 85 | 86 | if (Type == "js_event") 87 | { 88 | 89 | // Check the datatype 90 | 91 | if (DataType == "bool") 92 | Data = Message->GetArgumentList()->GetBool(1) ? TEXT("true") : TEXT("false"); 93 | else if (DataType == "int") 94 | Data = FString::FromInt(Message->GetArgumentList()->GetInt(1)); 95 | else if (DataType == "string") 96 | Data = FString(UTF8_TO_TCHAR(Message->GetArgumentList()->GetString(1).ToString().c_str())); 97 | else if (DataType == "double") 98 | Data = FString::SanitizeFloat(Message->GetArgumentList()->GetDouble(1)); 99 | 100 | EventEmitter->Broadcast(Name, Data); 101 | } 102 | 103 | return true; 104 | } 105 | 106 | void BrowserClient::OnUncaughtException(CefRefPtr Browser, CefRefPtr Frame, CefRefPtr Context, CefRefPtr Exception, CefRefPtr StackTrace) 107 | { 108 | FString ErrorMessage = FString(Exception->GetMessage().c_str()); 109 | UE_LOG(LogClass, Warning, TEXT("%s"), *ErrorMessage); 110 | } 111 | 112 | //The path slashes have to be reversed to work with CEF 113 | FString ReversePathSlashes(FString ForwardPath) 114 | { 115 | return ForwardPath.Replace(TEXT("/"), TEXT("\\")); 116 | } 117 | FString UtilityBLUIDownloadsFolder() 118 | { 119 | return ReversePathSlashes(FPaths::ConvertRelativePathToFull(IPluginManager::Get().FindPlugin("BLUI")->GetBaseDir() + "/Downloads/")); 120 | } 121 | 122 | 123 | void BrowserClient::SetEventEmitter(FScriptEvent* Emitter) 124 | { 125 | this->EventEmitter = Emitter; 126 | } 127 | 128 | void BrowserClient::SetLogEmitter(FLogEvent* Emitter) 129 | { 130 | this->LogEmitter = Emitter; 131 | } 132 | 133 | void BrowserClient::OnBeforeDownload( 134 | CefRefPtr Browser, 135 | CefRefPtr DownloadItem, 136 | const CefString & SuggestedName, 137 | CefRefPtr Callback) 138 | { 139 | UNREFERENCED_PARAMETER(Browser); 140 | UNREFERENCED_PARAMETER(DownloadItem); 141 | 142 | //We use this concatenation method to mix c_str with regular FString and then convert the result back to c_str 143 | FString DownloadPath = UtilityBLUIDownloadsFolder() + FString(SuggestedName.c_str()); 144 | 145 | Callback->Continue(*DownloadPath, false); //don't show the download dialog, just go for it 146 | 147 | UE_LOG(LogClass, Log, TEXT("Downloading file for path %s"), *DownloadPath); 148 | } 149 | 150 | void BrowserClient::OnDownloadUpdated( 151 | CefRefPtr ForBrowser, 152 | CefRefPtr DownloadItem, 153 | CefRefPtr Callback) 154 | { 155 | int Percentage = DownloadItem->GetPercentComplete(); 156 | FString Url = FString(DownloadItem->GetFullPath().c_str()); 157 | 158 | UE_LOG(LogClass, Log, TEXT("Download %s Updated: %d"), *Url , Percentage); 159 | 160 | RenderHandlerRef->ParentUI->DownloadUpdated.Broadcast(Url, Percentage); 161 | 162 | if (Percentage == 100 && DownloadItem->IsComplete()) { 163 | UE_LOG(LogClass, Log, TEXT("Download %s Complete"), *Url); 164 | RenderHandlerRef->ParentUI->DownloadComplete.Broadcast(Url); 165 | } 166 | 167 | //Example download cancel/pause etc, we just have to hijack this 168 | //callback->Cancel(); 169 | } 170 | 171 | -------------------------------------------------------------------------------- /Source/Blu/Public/BluBlueprintFunctionLibrary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Kismet/BlueprintFunctionLibrary.h" 4 | #include "Input/Events.h" 5 | #include "BluEye.h" 6 | 7 | #include "BluBlueprintFunctionLibrary.generated.h" 8 | 9 | UCLASS(ClassGroup = Blu, Blueprintable) 10 | class BLU_API UBluBlueprintFunctionLibrary : public UBlueprintFunctionLibrary 11 | { 12 | 13 | GENERATED_UCLASS_BODY() 14 | 15 | UFUNCTION(BlueprintPure, meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject", DisplayName = "Create BluEye", CompactNodeTitle = "BluEye", Keywords = "new create blu eye blui"), Category = Blu) 16 | static UBluEye* NewBluEye(UObject* WorldContextObject); 17 | 18 | UFUNCTION(BlueprintPure, meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject", DisplayName = "Create BluJSON Obj", CompactNodeTitle = "JSON", Keywords = "new create blu eye blui json"), Category = Blu) 19 | static UBluJsonObj* NewBluJSONObj(UObject* WorldContextObject); 20 | 21 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Run BLUI Tick", Keywords = "blui blu eye blui tick"), Category = Blu) 22 | static void RunBluEventLoop(); 23 | 24 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Parse JSON String", Keywords = "blui blu eye json parse"), Category = Blu) 25 | static UBluJsonObj* ParseJSON(const FString& JSONString); 26 | 27 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "JSON To String", Keywords = "blui blu eye json parse string"), Category = Blu) 28 | static FString JSONToString(UBluJsonObj *ObjectToParse); 29 | 30 | /** convert regular key events into char event which you can use char press*/ 31 | UFUNCTION(BlueprintPure, meta = (DisplayName = "To CharacterEvent (Key)", BlueprintAutocast), Category = Blu) 32 | static FCharacterEvent ToKeyEvent(FKey Key); 33 | 34 | //Utility functions taken from Victory Plugin 35 | UFUNCTION(BlueprintPure, Category = "Blu Utility") 36 | static FString GameRootDirectory(); 37 | 38 | /** 39 | * Returns whether or not the SearchIn string contains the supplied Substring. 40 | * Ex: "cat" is a contained within "concatenation" as a substring. 41 | * @param SearchIn The string to search within 42 | * @param Substring The string to look for in the SearchIn string 43 | * @param bUseCase Whether or not to be case-sensitive 44 | * @param bSearchFromEnd Whether or not to start the search from the end of the string instead of the beginning 45 | */ 46 | UFUNCTION(BlueprintPure, Category = "Blu Utility") 47 | static bool HasSubstring(const FString& SearchIn, const FString& Substring, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase, ESearchDir::Type SearchDir = ESearchDir::FromStart); 48 | 49 | }; -------------------------------------------------------------------------------- /Source/Blu/Public/BluEye.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RenderHandler.h" 4 | #include "BluTypes.h" 5 | #include "BluManager.h" 6 | #include "UObject/Object.h" 7 | #include "BluEye.generated.h" 8 | 9 | UCLASS(ClassGroup = Blu, Blueprintable) 10 | class BLU_API UBluEye : public UObject 11 | { 12 | GENERATED_BODY() 13 | 14 | UBluEye(const class FObjectInitializer& PCIP); 15 | 16 | public: 17 | 18 | //Event delegates 19 | UPROPERTY(BlueprintAssignable, Category = "Blu Browser Events") 20 | FDownloadCompleteSignature DownloadComplete; 21 | 22 | UPROPERTY(BlueprintAssignable, Category = "Blu Browser Events") 23 | FDownloadUpdatedSignature DownloadUpdated; 24 | 25 | //GENERATED_UCLASS_BODY() 26 | 27 | /** Initialize function, should be called after properties are set */ 28 | UFUNCTION(BlueprintCallable, Category = "Blu") 29 | void Init(); 30 | 31 | /** The default URL this UI component will load */ 32 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 33 | FString DefaultURL; 34 | 35 | /** Is this UI component current active? */ 36 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 37 | bool bEnabled; 38 | 39 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 40 | FBluEyeSettings Settings; 41 | 42 | /** Material that will be instanced to load UI texture into it */ 43 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 44 | UMaterialInterface* BaseMaterial; 45 | 46 | /** Name of parameter to load UI texture into material */ 47 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Blu") 48 | FName TextureParameterName = "BluTexture"; 49 | 50 | UFUNCTION(BlueprintCallable, Category = "Blu") 51 | UBluEye* SetProperties(const int32 SetWidth, 52 | const int32 SetHeight, 53 | const bool SetIsTransparent, 54 | const bool SetEnabled, 55 | const bool SetWebGL, 56 | const FString& SetDefaultURL, 57 | const FName& SetTextureParameterName, 58 | UMaterialInterface* SetBaseMaterial); 59 | 60 | /** Get the texture data from our UI component */ 61 | UFUNCTION(BlueprintCallable, Category = "Blu") 62 | UTexture2D* GetTexture() const; 63 | 64 | /** Execute JS code inside the browser */ 65 | UFUNCTION(BlueprintCallable, Category = "Blu") 66 | void ExecuteJS(const FString& code); 67 | 68 | /** 69 | * Execute a JS function/method by name with FString Array as params. 70 | * Each element in the array will be passed into the function in order and separated by a , 71 | * If you want to pass a JSON string as an object, simply don't put quotes around the outside braces {"foo" : "bar"} 72 | * If you want to pass a number, do similar: 10.5 73 | * To pass as a string, place quotes around the param when adding to the array: "10.5" and "hello" are strings 74 | */ 75 | UFUNCTION(BlueprintCallable, Category = "Blu", meta = (DisplayName = "Execute Javascript With Params", Keywords = "js javascript parameters")) 76 | void ExecuteJSMethodWithParams(const FString& methodName, const TArray params); 77 | 78 | /** Load a new URL into the browser */ 79 | UFUNCTION(BlueprintCallable, Category = "Blu") 80 | void LoadURL(const FString& newURL); 81 | 82 | /** Get the currently loaded URL */ 83 | UFUNCTION(BlueprintPure, Category = "Blu") 84 | FString GetCurrentURL(); 85 | 86 | /** Trigger Zoom */ 87 | UFUNCTION(BlueprintCallable, Category = "Blu") 88 | void SetZoom(const float scale = 1); 89 | 90 | /** Get our zoom level */ 91 | UFUNCTION(BlueprintPure, Category = "Blu") 92 | float GetZoom(); 93 | 94 | /** Download a file */ 95 | UFUNCTION(BlueprintCallable, Category = "Blu") 96 | void DownloadFile(const FString& fileUrl); 97 | 98 | /** Trigger a LEFT click in the browser via a Vector2D */ 99 | UFUNCTION(BlueprintCallable, Category = "Blu") 100 | void TriggerLeftClick(const FVector2D& pos, const float scale = 1); 101 | 102 | /** Trigger a RIGHT click in the browser via a Vector2D */ 103 | UFUNCTION(BlueprintCallable, Category = "Blu") 104 | void TriggerRightClick(const FVector2D& pos, const float scale = 1); 105 | 106 | /** Trigger a LEFT MOUSE DOWN in the browser via a Vector2D */ 107 | UFUNCTION(BlueprintCallable, Category = "Blu") 108 | void TriggerLeftMouseDown(const FVector2D& pos, const float scale = 1); 109 | 110 | /** Trigger a RIGHT MOUSE DOWN in the browser via a Vector2D */ 111 | UFUNCTION(BlueprintCallable, Category = "Blu") 112 | void TriggerRightMouseDown(const FVector2D& pos, const float scale = 1); 113 | 114 | /** Trigger a LEFT MOUSE UP in the browser via a Vector2D */ 115 | UFUNCTION(BlueprintCallable, Category = "Blu") 116 | void TriggerLeftMouseUp(const FVector2D& pos, const float scale = 1); 117 | 118 | /* Trigger a RIGHT MOUSE UP in the browser via a Vector2D */ 119 | UFUNCTION(BlueprintCallable, Category = "Blu") 120 | void TriggerRightMouseUp(const FVector2D& pos, const float scale = 1); 121 | 122 | /** Move the mouse in the browser */ 123 | UFUNCTION(BlueprintCallable, Category = "Blu") 124 | void TriggerMouseMove(const FVector2D& pos, const float scale = 1); 125 | 126 | /** Move the mouse in the browser */ 127 | UFUNCTION(BlueprintCallable, Category = "Blu") 128 | void TriggerMouseWheel(const float MouseWheelDelta, const FVector2D& pos, const float scale = 1); 129 | 130 | /** Javascript event emitter */ 131 | UPROPERTY(BlueprintAssignable) 132 | FScriptEvent ScriptEventEmitter; 133 | 134 | UPROPERTY(BlueprintAssignable) 135 | FLogEvent LogEventEmitter; 136 | 137 | /** Trigger a key down event */ 138 | UFUNCTION(BlueprintCallable, Category = "Blu") 139 | void KeyDown(FKeyEvent InKey); 140 | 141 | /** Trigger a key up event */ 142 | UFUNCTION(BlueprintCallable, Category = "Blu") 143 | void KeyUp(FKeyEvent InKey); 144 | 145 | /** Trigger a key press event */ 146 | UFUNCTION(BlueprintCallable, Category = "Blu") 147 | void KeyPress(FKeyEvent InKey); 148 | 149 | /** Trigger a character key event as if typing input */ 150 | UFUNCTION(BlueprintCallable, Category = "Blu") 151 | void CharKeyInput(FCharacterEvent CharEvent); 152 | 153 | /** Trigger a character key event as if pressing like a keyboard shortcut */ 154 | UFUNCTION(BlueprintCallable, Category = "Blu") 155 | void CharKeyDownUp(FCharacterEvent CharEvent); 156 | 157 | /** Trigger a raw keypress via a character */ 158 | UFUNCTION(BlueprintCallable, Category = "Blu", meta = (AdvancedDisplay = "2")) 159 | void RawCharKeyPress(const FString charToPress, bool isRepeat, 160 | bool LeftShiftDown, 161 | bool RightShiftDown, 162 | bool LeftControlDown, 163 | bool RightControlDown, 164 | bool LeftAltDown, 165 | bool RightAltDown, 166 | bool LeftCommandDown, 167 | bool RightCommandDown, 168 | bool CapsLocksOn); 169 | 170 | UFUNCTION(BlueprintCallable, Category = "Blu", meta = (AdvancedDisplay = "2")) 171 | void SpecialKeyPress(EBluSpecialKeys key, 172 | bool LeftShiftDown, 173 | bool RightShiftDown, 174 | bool LeftControlDown, 175 | bool RightControlDown, 176 | bool LeftAltDown, 177 | bool RightAltDown, 178 | bool LeftCommandDown, 179 | bool RightCommandDown, 180 | bool CapsLocksOn); 181 | 182 | /** Close the browser */ 183 | UFUNCTION(BlueprintCallable, Category = "Blu") 184 | void CloseBrowser(); 185 | 186 | /** Check if the browser is still loading */ 187 | UFUNCTION(BlueprintCallable, Category = "Blu") 188 | bool IsBrowserLoading(); 189 | 190 | /** Reloads the browser's current page */ 191 | UFUNCTION(BlueprintCallable, Category = "Blu") 192 | void ReloadBrowser(bool IgnoreCache); 193 | 194 | /** Navigate back in this web view's history */ 195 | UFUNCTION(BlueprintCallable, Category = "Blu") 196 | void NavBack(); 197 | 198 | /** Navigate forward in this web view's history */ 199 | UFUNCTION(BlueprintCallable, Category = "Blu") 200 | void NavForward(); 201 | 202 | /** Resize the browser's viewport */ 203 | UFUNCTION(BlueprintCallable, Category = "Blu") 204 | UTexture2D* ResizeBrowser(const int32 NewWidth, const int32 NewHeight); 205 | 206 | //This cropping function doesn't work atm 207 | //UFUNCTION(BlueprintCallable, Category = "Blu") 208 | UTexture2D* CropWindow(const int32 Y, const int32 X, const int32 NewWidth, const int32 NewHeight); 209 | 210 | void TextureUpdate(const void* Buffer, FUpdateTextureRegion2D * UpdateRegions, uint32 RegionCount); 211 | 212 | void BeginDestroy() override; 213 | 214 | /** Use this to pause the tick loop in the new system */ 215 | UFUNCTION(BlueprintCallable, Category = "Blu") 216 | static void SetShouldTickEventLoop(bool ShouldTick = true); 217 | 218 | protected: 219 | 220 | CefWindowInfo Info; 221 | CefRefPtr ClientHandler; 222 | CefBrowserSettings BrowserSettings; 223 | RenderHandler* Renderer; 224 | CefRefPtr Browser; 225 | 226 | CefMouseEvent MouseEvent; 227 | CefKeyEvent KeyEvent; 228 | 229 | void ResetTexture(); 230 | void DestroyTexture(); 231 | void ResetMatInstance(); 232 | 233 | // Parse UE4 key events, helper 234 | void ProcessKeyCode(FKeyEvent InKey); 235 | 236 | // Helper for processing key modifiers 237 | void ProcessKeyMods(FInputEvent InKey); 238 | 239 | void SpawnTickEventLoopIfNeeded(); 240 | 241 | static FTickEventLoopData EventLoopData; 242 | 243 | // Store UI state in this UTexture2D 244 | UPROPERTY() 245 | UTexture2D* Texture; 246 | 247 | UMaterialInstanceDynamic* MaterialInstance; 248 | 249 | private: 250 | 251 | FBluTextureParams RenderParams; 252 | FThreadSafeBool bValidTexture; 253 | }; 254 | -------------------------------------------------------------------------------- /Source/Blu/Public/BluJsonObj.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BluJsonObj.generated.h" 4 | 5 | UCLASS(ClassGroup = Blu, Blueprintable) 6 | class BLU_API UBluJsonObj : public UObject 7 | { 8 | 9 | GENERATED_UCLASS_BODY() 10 | 11 | public: 12 | 13 | //// Get Values //// 14 | 15 | /* Gets a String Value for the key given */ 16 | UFUNCTION(BlueprintCallable, Category = "Blu") 17 | FString GetStringValue(const FString &Index); 18 | 19 | /* Gets a Numerical Value for the key given */ 20 | UFUNCTION(BlueprintCallable, Category = "Blu") 21 | float GetNumValue(const FString &Index); 22 | 23 | /* Gets a Boolean Value for the key given */ 24 | UFUNCTION(BlueprintCallable, Category = "Blu") 25 | bool GetBooleanValue(const FString &Index); 26 | 27 | /* Gets a Nested JSON Object Value for the key given */ 28 | UFUNCTION(BlueprintCallable, Category = "Blu") 29 | UBluJsonObj* GetNestedObject(const FString &Index); 30 | 31 | //// Get Array Values //// 32 | 33 | /* Gets an Array of floats or numbers for the key given */ 34 | UFUNCTION(BlueprintCallable, Category = "Blu") 35 | TArray GetNumArray(const FString &Index); 36 | 37 | /* Gets an Array of booleans for the key given */ 38 | UFUNCTION(BlueprintCallable, Category = "Blu") 39 | TArray GetBooleanArray(const FString &Index); 40 | 41 | /* Gets an Array of strings for the key given */ 42 | UFUNCTION(BlueprintCallable, Category = "Blu") 43 | TArray GetStringArray(const FString &Index); 44 | 45 | //// Set Values //// 46 | 47 | /* Sets or Adds a String Value to this JSON object */ 48 | UFUNCTION(BlueprintCallable, Category = "Blu") 49 | void SetStringValue(const FString &Value, const FString &Index); 50 | 51 | /* Sets or Adds a Numerical Value to this JSON object */ 52 | UFUNCTION(BlueprintCallable, Category = "Blu") 53 | void SetNumValue(const float Value, const FString &Index); 54 | 55 | /* Sets or Adds a Boolean Value to this JSON object */ 56 | UFUNCTION(BlueprintCallable, Category = "Blu") 57 | void SetBooleanValue(const bool Value, const FString &Index); 58 | 59 | /* Sets or Adds a Nested JSON Object Value to this JSON object */ 60 | UFUNCTION(BlueprintCallable, Category = "Blu") 61 | void SetNestedObject(UBluJsonObj *Value, const FString &Index); 62 | 63 | void Init(const FString &dataString); 64 | void SetJsonObj(TSharedPtr NewJson); 65 | 66 | TSharedPtr GetJsonObj(); 67 | 68 | // CUSTOM ADDED START 69 | UFUNCTION(BlueprintCallable, Category = "Blu") 70 | void SetStringArray(const TArray &Value, const FString &Index); 71 | 72 | UFUNCTION(BlueprintCallable, Category = "Blu") 73 | void SetBooleanArray(const TArray &Value, const FString &Index); 74 | 75 | UFUNCTION(BlueprintCallable, Category = "Blu") 76 | void SetNumArray(const TArray &Value, const FString &Index); 77 | 78 | UFUNCTION(BlueprintCallable, Category = "Blu") 79 | void SetObjectArray(const TArray &Value, const FString &Index); 80 | // CUSTOM ADDED END 81 | 82 | private: 83 | 84 | FString StrData; 85 | TSharedPtr JsonParsed; 86 | 87 | void DoParseJson(TSharedRef> JsonReader); 88 | }; -------------------------------------------------------------------------------- /Source/Blu/Public/BluManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CEFInclude.h" 4 | 5 | class BLU_API BluManager : public CefApp 6 | { 7 | public: 8 | 9 | BluManager(); 10 | 11 | static void DoBluMessageLoop(); 12 | static CefSettings Settings; 13 | static CefMainArgs MainArgs; 14 | static bool CPURenderSettings; 15 | static bool AutoPlay; 16 | 17 | virtual void OnBeforeCommandLineProcessing(const CefString& ProcessType, 18 | CefRefPtr< CefCommandLine > CommandLine) override; 19 | 20 | IMPLEMENT_REFCOUNTING(BluManager); 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /Source/Blu/Public/BluTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "BluTypes.generated.h" 5 | 6 | struct FTickEventLoopData 7 | { 8 | FTSTicker::FDelegateHandle DelegateHandle; 9 | int32 EyeCount; 10 | bool bShouldTickEventLoop; 11 | 12 | FTickEventLoopData() 13 | { 14 | DelegateHandle = FTSTicker::FDelegateHandle(); 15 | EyeCount = 0; 16 | bShouldTickEventLoop = true; 17 | } 18 | }; 19 | 20 | struct FBluTextureParams 21 | { 22 | // Pointer to our Texture's resource 23 | FTexture2DResource* Texture2DResource; 24 | }; 25 | 26 | struct FUpdateTextureRegionsData 27 | { 28 | FTextureResource* Texture2DResource; 29 | uint32 NumRegions; 30 | FUpdateTextureRegion2D* Regions; 31 | uint32 SrcPitch; 32 | uint32 SrcBpp; 33 | TArray SrcData; 34 | }; 35 | 36 | UENUM(BlueprintType) 37 | enum EBluSpecialKeys 38 | { 39 | backspacekey = 8 UMETA(DisplayName = "Backspace"), 40 | tabkey = 9 UMETA(DisplayName = "Tab"), 41 | enterkey = 13 UMETA(DisplayName = "Enter"), 42 | pausekey = 19 UMETA(DisplayName = "Pause"), 43 | escapekey = 27 UMETA(DisplayName = "Escape"), 44 | pageupkey = 33 UMETA(DisplayName = "Page Up"), 45 | pagedownkey = 34 UMETA(DisplayName = "Page Down"), 46 | endkey = 35 UMETA(DisplayName = "End"), 47 | homekey = 36 UMETA(DisplayName = "Home"), 48 | leftarrowkey = 37 UMETA(DisplayName = "Left Arrow"), 49 | rightarrowkey = 39 UMETA(DisplayName = "Right Arrow"), 50 | downarrowkey = 40 UMETA(DisplayName = "Down Arrow"), 51 | uparrowkey = 38 UMETA(DisplayName = "Up Arrow"), 52 | insertkey = 45 UMETA(DisplayName = "Insert"), 53 | deletekey = 46 UMETA(DisplayName = "Delete"), 54 | numlockkey = 144 UMETA(DisplayName = "Num Lock"), 55 | scrolllockkey = 145 UMETA(DisplayName = "Scroll Lock"), 56 | unknownkey = 0, 57 | }; 58 | 59 | 60 | USTRUCT(BlueprintType) 61 | struct FBluEyeSettings 62 | { 63 | GENERATED_USTRUCT_BODY() 64 | 65 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BluSettings") 66 | float FrameRate; 67 | 68 | /** Should this be rendered in game to be transparent? */ 69 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 70 | bool bIsTransparent; 71 | 72 | /** Width(X) and Height(Y) of the view resolution */ 73 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 74 | FVector2D ViewSize; 75 | 76 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 77 | bool bEnableWebGL; 78 | 79 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 80 | bool bAudioMuted; 81 | 82 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 83 | bool bAutoPlayEnabled; 84 | 85 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blu") 86 | bool bDebugLogTick; 87 | 88 | FBluEyeSettings(); 89 | }; 90 | 91 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FScriptEvent, const FString&, EventName, const FString&, EventMessage); 92 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLogEvent, const FString&, LogText); 93 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDownloadCompleteSignature, FString, url); 94 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDownloadUpdatedSignature, FString, url, float, percentage); 95 | //DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDownloadComplete); -------------------------------------------------------------------------------- /Source/Blu/Public/CEFInclude.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if PLATFORM_WINDOWS 4 | #include "Windows/WindowsHWrapper.h" 5 | #include "Windows/AllowWindowsPlatformTypes.h" 6 | #include "Windows/AllowWindowsPlatformAtomics.h" 7 | #endif 8 | //#pragma push_macro("OVERRIDE") 9 | //#undef OVERRIDE // cef headers provide their own OVERRIDE macro 10 | THIRD_PARTY_INCLUDES_START 11 | #include "include/cef_client.h" 12 | #include "include/cef_browser.h" 13 | #include "include/cef_app.h" 14 | THIRD_PARTY_INCLUDES_END 15 | //#pragma pop_macro("OVERRIDE") 16 | #if PLATFORM_WINDOWS 17 | #include "Windows/HideWindowsPlatformAtomics.h" 18 | #include "Windows/HideWindowsPlatformTypes.h" 19 | #endif -------------------------------------------------------------------------------- /Source/Blu/Public/IBlu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (c) Aaron M. Shea 2014 3 | */ 4 | #pragma once 5 | #include "Modules/ModuleManager.h" 6 | 7 | DECLARE_LOG_CATEGORY_EXTERN(LogBlu, Log, All); 8 | 9 | class IBlu : public IModuleInterface 10 | { 11 | public: 12 | 13 | /** 14 | * Singleton-like access to this module's interface. This is just for convenience! 15 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. 16 | * 17 | * @return Returns singleton instance, loading the module on demand if needed 18 | */ 19 | static inline IBlu& Get() 20 | { 21 | return FModuleManager::LoadModuleChecked("Blu"); 22 | } 23 | 24 | /** 25 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. 26 | * 27 | * @return True if the module is loaded and ready to use 28 | */ 29 | static inline bool IsAvailable() 30 | { 31 | return FModuleManager::Get().IsModuleLoaded("Blu"); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /Source/Blu/Public/RenderHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CEFInclude.h" 4 | #include "BluTypes.h" 5 | 6 | class UBluEye; 7 | 8 | 9 | class RenderHandler : public CefRenderHandler 10 | { 11 | public: 12 | UBluEye* ParentUI; 13 | 14 | int32 Width; 15 | int32 Height; 16 | 17 | // CefRenderHandler interface 18 | virtual void GetViewRect(CefRefPtr Browser, CefRect &Rect) override; 19 | 20 | void OnPaint(CefRefPtr Browser, PaintElementType Type, const RectList &DirtyRects, const void *Buffer, int Width, int Height) override; 21 | 22 | RenderHandler(int32 Width, int32 Height, UBluEye* UI); 23 | 24 | // CefBase interface 25 | // NOTE: Must be at bottom 26 | public: 27 | IMPLEMENT_REFCOUNTING(RenderHandler); 28 | }; 29 | 30 | // for manual render handler 31 | class BrowserClient : public CefClient, public CefLifeSpanHandler, public CefDownloadHandler, public CefDisplayHandler 32 | { 33 | 34 | private: 35 | FScriptEvent* EventEmitter; 36 | FLogEvent* LogEmitter; 37 | CefRefPtr RenderHandlerRef; 38 | 39 | // For lifespan 40 | CefRefPtr BrowserRef; 41 | int BrowserId; 42 | bool bIsClosing; 43 | 44 | public: 45 | BrowserClient(RenderHandler* InRenderHandler) : RenderHandlerRef(InRenderHandler) 46 | { 47 | 48 | }; 49 | 50 | virtual CefRefPtr GetRenderHandler() 51 | { 52 | return RenderHandlerRef; 53 | }; 54 | 55 | // Getter for renderer 56 | virtual CefRefPtr GetRenderHandlerCustom() 57 | { 58 | return RenderHandlerRef; 59 | }; 60 | 61 | // Getter for lifespan 62 | virtual CefRefPtr GetLifeSpanHandler() override 63 | { 64 | return this; 65 | } 66 | //required or pdf download won't work 67 | virtual CefRefPtr GetDownloadHandler() override 68 | { 69 | return this; 70 | } 71 | 72 | virtual bool OnProcessMessageReceived(CefRefPtr Browser, 73 | CefRefPtr Frame, 74 | CefProcessId SourceProcess, 75 | CefRefPtr Message) override; 76 | 77 | virtual void OnUncaughtException(CefRefPtr Browser, 78 | CefRefPtr Frame, 79 | CefRefPtr Context, 80 | CefRefPtr Exception, 81 | CefRefPtr StackTrace); 82 | 83 | void SetEventEmitter(FScriptEvent* Emitter); 84 | void SetLogEmitter(FLogEvent* Emitter); 85 | 86 | //CefDownloadHandler 87 | virtual void OnBeforeDownload( 88 | CefRefPtr Browser, 89 | CefRefPtr DownloadItem, 90 | const CefString& SuggestedName, 91 | CefRefPtr Callback) override; 92 | 93 | virtual void OnDownloadUpdated( 94 | CefRefPtr Browser, 95 | CefRefPtr DownloadItem, 96 | CefRefPtr Callback) override; 97 | 98 | //CefLifeSpanHandler 99 | virtual bool OnBeforePopup(CefRefPtr Browser, 100 | CefRefPtr Frame, 101 | const CefString& TargetUrl, 102 | const CefString& TargetFrameName, 103 | WindowOpenDisposition TargetDisposition, 104 | bool UserGesture, 105 | const CefPopupFeatures& PopupFeatures, 106 | CefWindowInfo& WindowInfo, 107 | CefRefPtr& Client, 108 | CefBrowserSettings& Settings, 109 | CefRefPtr& ExtraInfo, 110 | bool* NoJavascriptAccess) { 111 | return false; 112 | } 113 | 114 | // Lifespan methods 115 | void OnAfterCreated(CefRefPtr Browser) override; 116 | void OnBeforeClose(CefRefPtr Browser) override; 117 | 118 | virtual bool OnConsoleMessage(CefRefPtr Browser, 119 | cef_log_severity_t Level, 120 | const CefString& Message, 121 | const CefString& Source, 122 | int Line) override; 123 | 124 | virtual void OnFullscreenModeChange(CefRefPtr< CefBrowser > Browser, bool Fullscreen) override; 125 | 126 | virtual void OnTitleChange(CefRefPtr< CefBrowser > Browser, const CefString& Title); 127 | 128 | CefRefPtr GetCEFBrowser(); 129 | 130 | // NOTE: Must be at bottom 131 | public: 132 | IMPLEMENT_REFCOUNTING(BrowserClient); 133 | }; 134 | 135 | 136 | -------------------------------------------------------------------------------- /Source/BluLoader/BluLoader.Build.cs: -------------------------------------------------------------------------------- 1 | using UnrealBuildTool; 2 | using System.IO; 3 | using System; 4 | 5 | public class BluLoader : ModuleRules 6 | { 7 | public BluLoader(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PublicDependencyModuleNames.AddRange( 10 | new string[] 11 | { 12 | "Core", 13 | "CoreUObject", 14 | "Engine", 15 | "Projects" 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/BluLoader/Private/BluLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "IBluLoader.h" 2 | #include "Interfaces/IPluginManager.h" 3 | #include "CoreMinimal.h" 4 | #include "Misc/Paths.h" 5 | #include 6 | 7 | #if PLATFORM_WINDOWS 8 | #include "Windows/WindowsPlatformProcess.h" 9 | #endif 10 | 11 | class FBluLoader : public IBluLoader 12 | { 13 | 14 | /** IModuleInterface implementation */ 15 | virtual void StartupModule() override 16 | { 17 | FString LibPath = FPaths::ConvertRelativePathToFull(IPluginManager::Get().FindPlugin("BLUI")->GetBaseDir() + "/ThirdParty/cef/"); 18 | 19 | // If we're on Windows we need to load DLLs from our custom path 20 | #if PLATFORM_WINDOWS 21 | LibPath += "Win/shipping/"; 22 | FPlatformProcess::PushDllDirectory(*LibPath); 23 | UE_LOG(LogBluLoader, Log, TEXT("patched dll directory paths")); 24 | #endif 25 | 26 | #if PLATFORM_MAC 27 | // We need to load OUR CEF3 framework bundle here. It uses this identifier: org.chromium.ContentShell.BLUI.framework 28 | LibPath += "Mac/lib/Chromium Embedded Framework.framework/Chromium Embedded Framework"; 29 | void* framework_hdl = dlopen(TCHAR_TO_ANSI(*LibPath), RTLD_NOW); 30 | UE_LOG(LogBluLoader, Log, TEXT("dlopen has loaded CEF framework")); 31 | #endif 32 | 33 | UE_LOG(LogBluLoader, Log, TEXT("STATUS: BLUI Ready to Load!")); 34 | } 35 | 36 | virtual void ShutdownModule() override 37 | { 38 | UE_LOG(LogBluLoader, Log, TEXT("STATUS: BLUI Has Shutdown")); 39 | } 40 | 41 | }; 42 | 43 | IMPLEMENT_MODULE( FBluLoader, BluLoader ) 44 | DEFINE_LOG_CATEGORY(LogBluLoader); -------------------------------------------------------------------------------- /Source/BluLoader/Public/IBluLoader.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (c) Aaron M. Shea 2014 3 | */ 4 | #pragma once 5 | #include "Modules/ModuleManager.h" 6 | 7 | DECLARE_LOG_CATEGORY_EXTERN(LogBluLoader, Log, All); 8 | 9 | class IBluLoader : public IModuleInterface 10 | { 11 | public: 12 | 13 | /** 14 | * Singleton-like access to this module's interface. This is just for convenience! 15 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. 16 | * 17 | * @return Returns singleton instance, loading the module on demand if needed 18 | */ 19 | static inline IBluLoader& Get() 20 | { 21 | return FModuleManager::LoadModuleChecked("BluLoader"); 22 | } 23 | 24 | /** 25 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. 26 | * 27 | * @return True if the module is loaded and ready to use 28 | */ 29 | static inline bool IsAvailable() 30 | { 31 | return FModuleManager::Get().IsModuleLoaded("BluLoader"); 32 | } 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /ThirdParty/cef/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getnamo/BLUI-Unreal/0b3d661ff52f2897c835210b1bff9a19c35032e9/ThirdParty/cef/.gitkeep -------------------------------------------------------------------------------- /builder/README.md: -------------------------------------------------------------------------------- 1 | ### BLUI CEF Chromium Build Script 2 | 3 | Current CEF building instructions: https://github.com/getnamo/blubrowser 4 | 5 | #### Archived - Windows Steps 6 | 7 | Requirements: 8 | ``` 9 | Python 2.7 (latest) 10 | Visual Studio 2015 11 | CMake 12 | Patch 13 | Git 14 | ``` 15 | 16 | * Ensure that `git, python, msbuild, patch, cmake` are all added to your PATH and can be run from the command line. 17 | * Copy `builder.py` to some other directory. 18 | * Use command prompt to navigate to the builder.py location. 19 | * Run `python builder.py` and provide the Visual Studio version (2015 is recommended) 20 | * Wait. A very long time. Really. 21 | * If no errors. Folder `blui` will contain all source and binary files needed for the `Plugins` folder. 22 | 23 | > Other platforms for this script are still a WIP... -------------------------------------------------------------------------------- /builder/archived_builder.py: -------------------------------------------------------------------------------- 1 | from distutils import spawn 2 | from subprocess import call 3 | import platform, urllib2, os, shutil, sys, argparse, glob, fileinput 4 | 5 | CEF_VERSION = "2623" 6 | CEF_BUILD_SCRIPT_URL = "https://bitbucket.org/chromiumembedded/cef/raw/master/tools/automate/automate-git.py" 7 | BLUI_PATCH = [ 8 | ["./cefbuild/cef/libcef/common/main_delegate.cc", "./browser/cef3_macosx_framework_pathBLUI.patch"], 9 | ["./cefbuild/cef/libcef/resources/framework-Info.plist", "./browser/framework-id-osx.patch"], 10 | ["./cefbuild/cef/include/cef_base.h", "./browser/cef_base.patch"], 11 | ["./cefbuild/cef/include/base/cef_thread_collision_warner.h", "./browser/cef_thread_collision_warner.patch"] 12 | ] 13 | 14 | parser = argparse.ArgumentParser() 15 | parser.add_argument('-m', '--msvc') 16 | args = parser.parse_args() 17 | 18 | class BLUIBuilder(): 19 | def __init__(self): 20 | self.pre_reqs = [] 21 | # Non platform specific things 22 | self.git_commands = ["git clone https://github.com/AaronShea/BLUI.git blui", 23 | "git clone https://github.com/AaronShea/BluBrowser.git browser"] 24 | 25 | def init(self): 26 | if self.get_os() == "Windows": 27 | self.pre_reqs = ["git", "msbuild", "cmake", "python", "patch"] 28 | 29 | def check_pre_reqs(self): 30 | print "Checking for: " + str(self.pre_reqs) 31 | for cmd in self.pre_reqs: 32 | print "Check for " + cmd 33 | path = spawn.find_executable(cmd) 34 | if path == None: 35 | raise Exception("Prereqs were not found! \nCould not find command: %s" % cmd) 36 | print "All good!" 37 | 38 | def clone_blu(self): 39 | for gitcmd in self.git_commands: 40 | call(gitcmd) 41 | 42 | def download_cef_script(self): 43 | response = urllib2.urlopen(CEF_BUILD_SCRIPT_URL) 44 | script = response.read() 45 | f = open("cefbuild.py",'w') 46 | f.write(script) 47 | f.close() 48 | 49 | def fetchCEFBuild(self): 50 | print "We're about to fetch and build Chromium and CEF!" 51 | print "This is going to take a while, please don't kill" 52 | print "the process until it's done." 53 | 54 | if self.get_os() == "Windows": 55 | if args.msvc is not None: 56 | os.environ["GYP_MSVS_VERSION"] = args.msvc 57 | else: 58 | vs_version = str(input("Please input your Visual Studio Version (2012, 2013 etc): ")) 59 | os.environ["GYP_MSVS_VERSION"] = vs_version 60 | os.environ["DEPOT_TOOLS_WIN_TOOLCHAIN"] = "0" 61 | call("python cefbuild.py --download-dir=./cefbuild --branch=%s --no-build --force-update" % CEF_VERSION) 62 | 63 | def patch_files(self): 64 | print "Applying patches..." 65 | for patchpair in BLUI_PATCH: 66 | cmd = "patch --verbose %s < %s" % (patchpair[0], patchpair[1]) 67 | os.system(cmd) 68 | 69 | def buildCEF(self): 70 | print "-- Building CEF and Chromium. This will take a while! --" 71 | os.environ["GYP_DEFINES"] = "target_arch=x64" 72 | call("python cefbuild.py --download-dir=./cefbuild --branch=%s --no-update --force-build --x64-build" % CEF_VERSION) 73 | 74 | def build_browser(self): 75 | if self.get_os() == "Windows": 76 | gen = "Visual Studio 14 2015 Win64" 77 | # Copy in new cefsimple from blubrowser 78 | rmpath = glob.glob('./cefbuild/chromium/src/cef/binary_distrib/cef_binary_*64/cefsimple')[0] 79 | shutil.rmtree(rmpath) 80 | shutil.copytree("./browser/BluBrowser", rmpath) 81 | call("cmake " + rmpath + "/.. " + "-G\"" + gen + "\"") 82 | 83 | ######## Start Windows Build Commands ######## 84 | if self.get_os() == "Windows": 85 | # Build step 1 - Debug and release 86 | 87 | call("msbuild ALL_BUILD.vcxproj /p:Configuration=Debug") 88 | call("msbuild ALL_BUILD.vcxproj /p:Configuration=Release") 89 | 90 | # Modify the libcef wrapper project file 91 | for line in fileinput.input("libcef_dll/libcef_dll_wrapper.vcxproj", inplace=True): 92 | if line.strip() == "MultiThreaded": 93 | line = "MultiThreadedDLL" 94 | if line.strip() == "MultiThreadedDebug": 95 | line = "MultiThreadedDebugDLL" 96 | print "%s" % line, 97 | 98 | # We can now rebuild the wrapper lib as dynamic (see above changes) 99 | call("msbuild libcef_dll/libcef_dll_wrapper.vcxproj /p:Configuration=Debug") 100 | call("msbuild libcef_dll/libcef_dll_wrapper.vcxproj /p:Configuration=Release") 101 | 102 | # Copy debug and release libs over 103 | debugPath = glob.glob("./cefbuild/chromium/src/cef/binary_distrib/cef_binary_*64/Debug/*.lib") 104 | releasePath = glob.glob("./cefbuild/chromium/src/cef/binary_distrib/cef_binary_*64/Release/*.lib") 105 | for lib in debugPath: 106 | shutil.copy(lib, "./libcef_dll/Debug") 107 | for lib in releasePath: 108 | shutil.copy(lib, "./libcef_dll/Release") 109 | self.packWindows() 110 | 111 | def packWindows(self): 112 | print "Packing up the plugin..." 113 | packLibReleasePath = "./libcef_dll/Release/" 114 | packLibDebugPath = "./libcef_dll/Debug/" 115 | packShipping = "./cefsimple/Release/" 116 | packInclude = "./cefbuild/cef/include/" 117 | 118 | # Copy libs 119 | shutil.copytree(packLibReleasePath, "./blui/ThirdParty/cef/Win/lib") 120 | shutil.copytree(packLibDebugPath, "./blui/ThirdParty/cef/Win/lib/Debug") 121 | 122 | # Copy shipping files 123 | shutil.copytree(packShipping, "./blui/ThirdParty/cef/Win/shipping") 124 | 125 | # Copy include files 126 | shutil.copytree(packInclude, "./blui/ThirdParty/cef/Win/include") 127 | 128 | print "=== ALL DONE! BLUI has been packaged for Windows! [in ./blui]" 129 | ######## End Windows Build Commands ######## 130 | 131 | 132 | def get_os(self): 133 | return platform.system() 134 | 135 | builder = BLUIBuilder() 136 | 137 | builder.init() 138 | builder.check_pre_reqs() 139 | builder.clone_blu() 140 | builder.download_cef_script() 141 | builder.fetchCEFBuild() 142 | builder.patch_files() 143 | builder.buildCEF() 144 | builder.build_browser() 145 | --------------------------------------------------------------------------------