{"id":16214819,"url":"https://github.com/lecrapouille/offscreencef","last_synced_at":"2025-03-19T09:31:23.210Z","repository":{"id":44367037,"uuid":"431220605","full_name":"Lecrapouille/OffScreenCEF","owner":"Lecrapouille","description":"[Application][Prototype] Chromium Embedded Framework with OpenGL Core or SDL2","archived":false,"fork":false,"pushed_at":"2024-11-02T17:46:09.000Z","size":426,"stargazers_count":46,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-28T17:50:38.637Z","etag":null,"topics":["cef","chromium","chromium-embedded-framework","glew","glfw3","opengl","sdl","sdl2"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Lecrapouille.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-23T19:00:58.000Z","updated_at":"2025-01-22T09:25:43.000Z","dependencies_parsed_at":"2024-10-27T20:29:59.117Z","dependency_job_id":"e90ff9dc-432d-44ad-a8ad-87c44dbda9cb","html_url":"https://github.com/Lecrapouille/OffScreenCEF","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lecrapouille%2FOffScreenCEF","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lecrapouille%2FOffScreenCEF/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lecrapouille%2FOffScreenCEF/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lecrapouille%2FOffScreenCEF/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lecrapouille","download_url":"https://codeload.github.com/Lecrapouille/OffScreenCEF/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243982140,"owners_count":20378605,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cef","chromium","chromium-embedded-framework","glew","glfw3","opengl","sdl","sdl2"],"created_at":"2024-10-10T11:13:06.778Z","updated_at":"2025-03-19T09:31:23.203Z","avatar_url":"https://github.com/Lecrapouille.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Chromium Embedded Framework's cefsimple Off-Screen Rendering using SDL2 or Core OpenGL\n\nCEF = Chromium Embedded Framework\n\n**This project shall be considered as proof of concept for this final [project](https://github.com/Lecrapouille/gdcef) the code is not proper, will not be maintained and not all mouse/keyboard callbacks are plugged!**\n\nFor the GitHub project named [Stigmee](https://github.com/stigmee) using [Godot Engine](https://github.com/godotengine/godot), and particularly for its [CEF native module](https://github.com/stigmee/gdnative-cef) (under GPL license, but also forked as MIT license https://github.com/Lecrapouille/gdcef), I needed to make my hand on something simpler and therefore prototype (proof of concept) and modify the C++ CEF example named [cefsimple](https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial) to replace the X11 display by either SDL2 or OpenGL Core (glew, glfw3). I tried the following GitHub repos but without success:\n- SDL2: https://github.com/gotnospirit/cef3-sdl2\n- SDL2: https://github.com/jamethy/sdl2-cef\n- OpenGL Core: https://github.com/if1live/cef-gl-example\n- OpenGL Core: https://github.com/andmcgregor/cefgui\n\nThey are outdated (more than \u003e 4 years), the CEF API changed, and when I compiled they crashed by forking indefinitely the application until my Linux fell down. So they are not safe to use! When starting, CEF will create several forks and needs to access the command line (`main(int argc, char* argv[])`) to give commands to the child process. From memory, issues of these repos come from the fact that the CEF API has evolved, and the command line access has been broken and as a consequence, CEF is forking forever. In this repo is the updated version of these repos. They are inside the following folders:\n- cefsimple_sdl: using SDL2.\n- cefsimple_opengl: using OpenGL Core (\u003e= 3.3).\n- cefsimple_separate: with two separate processes. Two processes are needed when CEF cannot directly access the `main(int argc, char* argv[])` function of your application. You have to know that CEF modifies the content of your `argv` and this may mess up your application if it also parses the command line (you can back it up, meaning using a `std::vector` to back up `argv` and after CEF init to restore values in `argv`).\n\nWhat is \"two separated processes\" exactly? Just an extra fork: the main process forks itself and calls the secondary process, which can fully access its own `main(int argc, char* argv[])`. The main constraint is the path of the secondary process shall be **canonic** (and this is a pain to get the real path). Do we really require two separate processes? Yes for applications that access to `main(int argc, char* argv[])` since CEF also uses it or for applications such as game engines (Godot, Unreal, Unity ...) where you cannot modify their `main(int argc, char* argv[])` code source to add your CEF code. See CEF:\n- for Unreal Engine: https://github.com/oivio/BLUI calling https://github.com/ashea-code/BluBrowser.\n- for Godot: https://github.com/Lecrapouille/gdcef/tree/master/addons/gdcef/gdcef calling https://github.com/Lecrapouille/gdcef/tree/master/addons/gdcef/subprocess\n\n[![OpenGL version](doc/screenshot.png)](https://youtu.be/8xhxiDI4D5o)\n\n*Fig 1 - The OpenGL version. Click on the image to see the YouTube video of the demo.*\n\nIn this screenshot, you can see two browsers: one running YouTube, and the second running a PDF. The projection of the mouse to the PDF plan has not been made. You can find [here](https://github.com/Lecrapouille/gdcef/tree/master/addons/gdcef/demos/3D) how to make it.\n\n**Note 1:** The keyboard/mouse event conversion from SDL2/glfw3 to CEF has to be fixed. **Update:** I have a functional keyboard for Godot/CEF but I have not yet patched this current repo for SDL and glfw3 (request me if you needed it urgently). So you can look at this [code](https://github.com/Lecrapouille/gdcef/blob/master/addons/gdcef/gdcef/src/browser_io.cpp).\n\n**Note 2:** I find the system not very reactive compared to the official cefsimple example.\n\n## Help Wanted\n\nWhile this repo shall be considered as a proof of concept for https://github.com/Lecrapouille/gdcef, pull requests and help are welcome to improve these examples :)\nYou can achieve the API by adding more methods such as previous/next page, reload, and zoom ... See https://github.com/oivio/BLUI\n\n## Tested on\n\nTested with CEF 116.\n\n- Linux: It's working on my Linux 64-bit Debian 11.\n- Windows: version can work quite easily but I did not translate the ./install.sh into Python script, but you can adapt this one on the version of this project for Godot [here](https://github.com/Lecrapouille/gdcef).\n- MacOS X: is more complex and is not working yet: https://github.com/Lecrapouille/OffScreenCEF/issues/4\n\n## Some Differences\n\n- Note: All these codes are quick and dirty since I needed to check how to make CEF run with OpenGL or SDL2 before switching to Godot Engine.\n- `cefsimple_sdl` is a single main file, while cefsimple_opengl has been split into several files.\n- `cefsimple_sdl` is using more handlers such as `CefLifeSpanHandler`. cefsimple_opengl does not.\n- `cefsimple_opengl` is using many viewports. cefsimple_sdl not. Viewport allows you to place the web page where you want on the screen (in the screenshot, you can see two browsers: one running YouTube, the second running a PDF).\n- `CefExecuteProcess` shall be called before OpenGL/SDL2 context and using the command line\nof your application (`int argc, char* argv[]`). If not, your system can freeze. Here is the\nfunction documentation (`caf_app.h` file):\n\n```\n   This function should be called from the application entry point function to\n   execute a secondary process. It can be used to run secondary processes from\n   the browser client executable (default behavior) or from a separate\n   executable specified by the CefSettings.browser_subprocess_path value. If\n   called for the browser process (identified by no \"type\" command-line value)\n   it will return immediately with a value of -1. If called for a recognized\n   secondary process it will block until the process should exit and then return\n   the process exit code. The |application| parameter may be empty. The\n   |windows_sandbox_info| parameter is only used on Windows and may be NULL (see\n   cef_sandbox_win.h for details).\n```\n- In `cefsimple_sdl` and `cefsimple_opengl`, both programs can access the `main(int argc, char* argv[])` function and therefore access the command line. CEF; when forking; modifies the command lines to give behaviors to the forked child. Sometimes, accessing the main function is not possible (or when your application is also using the command line, while possible to save and restore it before and after calling CEF). Therefore, you shall launch a second process that can access its main function and therefore call chromium. As a consequence, `cefsimple_separate` has been added when you cannot modify the main application to access the `int main(int argc, char* argv[])` function.\n\n## How to compile this project?\n\nRun the bash script for Linux:\n```\n./install.sh\n```\n\n**Note:** If you want to change the CEF version. Take the desired version at https://cef-builds.spotifycdn.com/index.html You will see something similar:\n\n```\n09/01/2023 - 116.0.19+gc6a20bc+chromium-116.0.5845.141 / Chromium 116.0.5845.141\n```\n\nNow, in the install.sh script, search and replace the content of this variable: `CEF_VERSION=` with the desired CEF version:\n\n```\nCEF_VERSION=\"116.0.19+gc6a20bc+chromium-116.0.5845.141\"\n```\n\nThe install.sh script will:\n- Download CEF inside the `thirdparty/` folder.\n- Use the CEF CMake to compile libraries and assets,\n- Copy them inside the `build/` folder,\n- Compile my applications into the `build/` folder.\n\nOnce done with success, you have to go inside the `build/` folder and run one of the following applications:\n- `./secondary_process`\n- `./primary_process`\n- `./cefsimple_opengl`\n- `./cefsimple_sdl`\n\n**Note:** A Python version of the script can be used and adapted. This will allow us to use it for Windows.\n[Here](https://github.com/Lecrapouille/gdcef).\n\n## Details on what the installation script is doing for you automatically\n\nTo compile my applications, some libs and packages are needed: we have to compile them from CEF's example named `cefsimple` (OffScreenCEF/thirdparty/cef_binary/tests/cefsimple/ once downloaded). These libs and assets shall be in the same folder as the application binary (some internal configurations can change the path of some of them, but not all of them). We beware, some lib can be \u003e 1 Gigabyte (depending on the operating system).\n\nHere is what the script is doing, but we will explain it manually.\n\nFirstly, let's name some folders. This will shorten the code in this document. Adapt the CEF version to your operating system with the desired version according to https://cef-builds.spotifycdn.com/index.html\n\n```bash\nTMP=/tmp\nCEF_LINK=https://cef-builds.spotifycdn.com/cef_binary_96.0.14%2Bg28ba5c8%2Bchromium-96.0.4664.55_linux64.tar.bz2\nCEF=$TMP/cef_binary_96.0.14+g28ba5c8+chromium-96.0.4664.55_linux64\nCEF_TARBALL=$CEF.tar.bz2\nCEFSIMPLE_SDL=$CEF/tests/cefsimple_sdl\nCEFSIMPLE_GL=$CEF/tests/cefsimple_opengl\n```\n\nDownload and decompress it inside a temporary folder:\n\n```bash\nwget -c $CEF_LINK -O- | tar -xj\n```\n\nGit clone this repo inside the CEF folder:\n\n```bash\ncd $CEF\ngit clone https://github.com/Lecrapouille/OffScreenCEF.git --depth=1\n```\n\nCompile CEF. This takes some minutes (not hours that would be needed if you had to compile CEF from sources):\n\n```bash\nmkdir $CEF/build\ncd $CEF/build\ncmake -DCMAKE_BUILD_TYPE=Debug ..\nmake -j$(nproc)\n```\n\n`$(nproc)` is your number of CPU cores. You can use `-DCMAKE_BUILD_TYPE=Release` instead but just avoid mixing debug and release symbols. Now we have to copy needed elements for compiling `cefsimple_sdl` and `cefsimple_opengl` and making them running:\n\n```bash\ncp -v $CEF/Debug/libcef.so $CEF/build/libcef_dll_wrapper/libcef_dll_wrapper.a $CEFSIMPLE_SDL\n\n(cd $CEF/build/tests/cefsimple/Debug/\n cp -v icudtl.dat resources.pak chrome_100_percent.pak chrome_200_percent.pak v8_context_snapshot.bin $CEFSIMPLE_SDL\n)\n\nmkdir -p $CEFSIMPLE_SDL/locales\ncp -v $CEF/build/tests/cefsimple/Debug/locales/en-US.pak $CEFSIMPLE_SDL/locales\n```\n\nDo the same thing for `$CEFSIMPLE_GL`. See the screenshot for needed assets (note this screenshot is old: in the latest version of\nthis repo all assets and libs are no longer inside the code source but inside the `build/` folder with the binary of applications):\n\n![Needed assets](doc/assets.png)\n\n*Fig 2 - Needed assets from CEF.*\n\nCEF needs all its assets next to it. Some paths can be configured. See OffScreenCEF/thirdparty/cef_binary/include/internal/cef_types.h for more information (once CEF has been downloaded by the install.sh script).\n\n## How CEF works?\n\nThe documentation of CEF is not really beginner-friendly:\nhttps://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md\n\nYou will have access to `int main(int argc, char * argv[])` you will have to deal with two separate processes.\nIf you can access to `int main(int argc, char * argv[])`:\n\n```\nint main(int argc, char * argv[])\n{\n    // Start CEF by forking this current application. Access to argc, argv is mandatory\n    // to allow passing information to forked CEF processes.\n    CefMainArgs args(argc, argv);\n    int result = CefExecuteProcess(args, nullptr, nullptr);\n    if (result \u003e= 0)\n    {\n        // The forked process has ended, so exit this application\n        exit(result);\n    }\n    else if (result == -1)\n    {\n        // If called for the browser process (identified by no \"type\" command-line value)\n        // it will return immediately with a value of -1\n    }\n\n    // Configure CEF see cef_binary/include/internal/cef_types.h for more information\n    CefSettings settings;\n    settings.windowless_rendering_enabled = true;\n    if (!CefInitialize(args, settings, nullptr, nullptr))\n    {\n        return EXIT_FAILURE;\n    }\n\n    // CEF started with success.\n    // Code for OpenGL/SDL2 init and runtime loop comes here after this line.\n    ...\n\n    // End of your application\n    return EXIT_SUCCESS;\n}\n```\nFor rendering the web document, check out the code with:\n\n```\nclass RenderHandler: public CefRenderHandler { ...\n```\n\nTwo important methods to override from `CefRenderHandler`:\n- The `GetViewRect` will get the region of your texture/windows and inform CEF. For a full page:\n\n```\nvirtual void GetViewRect(CefRefPtr\u003cCefBrowser\u003e browser, CefRect\u0026 rect) override\n{\n    rect = CefRect(0, 0, m_width, m_height);\n}\n```\n\n- When CEF has done some content to make you render:\n\n```\nvirtual void OnPaint(CefRefPtr\u003cCefBrowser\u003e browser, PaintElementType type,\n                     const RectList \u0026dirtyRects, const void* buffer,\n                     int w, int h) override\n```\n\nThe `buffer` contains the image of the whole web page in the form BGRA8 (beware this is Blue-Green-Red-Alpha not Red-Green-Blue and 1 byte by chanel!).\nThe buffer size is w, h:\n\n```\nint const COLOR_CHANELS = 4;\nint const SIZEOF_COLOR = COLOR_CHANELS * sizeof(char);\nint const TEXTURE_SIZE = SIZEOF_COLOR * width * height;\n```\n\nColor conversion BGRA8 -\u003e RGBA8: swap B and R channels can be made with:\n\n```\nfor (int i = 0; i \u003c TEXTURE_SIZE; i += COLOR_CHANELS)\n{\n    std::swap(w[i], w[i + 2]);\n}\n```\n\nThe `dirtyRects` contains a list of dirty regions to refresh. This avoids repainting the entire texture.\n\nIn a general way, you can derive some CEF classes to override some of their methods to react to events such as a browser is created, closed, or page loaded ...\n\n```\nclass BrowserClient: public CefRenderHandler,\n                     public CefLoadHandler,\n                     public CefClient\n{\n    public:\n\n        BrowserClient() { ... }\n\n    private: // CefClient::CefBaseRefCounted interfaces\n\n        // ---------------------------------------------------------------------\n        //! \\brief CEF reference counting\n        // ---------------------------------------------------------------------\n        IMPLEMENT_REFCOUNTING(BrowserClient);\n\n    private: // CefClient interfaces\n\n        // ---------------------------------------------------------------------\n        //! \\brief Return the handler for off-screen rendering events.\n        // ---------------------------------------------------------------------\n        virtual CefRefPtr\u003cCefRenderHandler\u003e GetRenderHandler() override\n        {\n            return this;\n        }\n\n        // ---------------------------------------------------------------------\n        //! \\brief Return the handler for browser load status events.\n        // ---------------------------------------------------------------------\n        virtual CefRefPtr\u003cCefLoadHandler\u003e GetLoadHandler() override\n        {\n            return this;\n        }\n\n    private: // CefRenderHandler interfaces\n\n        // ---------------------------------------------------------------------\n        //! \\brief Get the viewport.\n        // ---------------------------------------------------------------------\n        virtual void GetViewRect(CefRefPtr\u003cCefBrowser\u003e browser, CefRect\u0026 rect) override\n        {\n            ...\n        }\n\n        // ---------------------------------------------------------------------\n        //! \\brief Called when an element should be painted. Pixel values passed\n        //! to this method are scaled relative to view coordinates based on the\n        //! value of CefScreenInfo.device_scale_factor returned from\n        //! GetScreenInfo. |type| indicates whether the element is the view or\n        //! the popup widget. |buffer| contains the pixel data for the whole\n        //! image. |dirtyRects| contains the set of rectangles in pixel\n        //! coordinates that need to be repainted. |buffer| will be\n        //! |width|*|height|*4 bytes in size and represents a BGRA image with an\n        //! upper-left origin. This method is only called when\n        //! CefWindowInfo::shared_texture_enabled is set to false.\n        // ---------------------------------------------------------------------\n        virtual void OnPaint(CefRefPtr\u003cCefBrowser\u003e browser,\n                             CefRenderHandler::PaintElementType type,\n                             const CefRenderHandler::RectList\u0026 dirtyRects,\n                             const void* buffer, int width, int height) override\n        {\n            ...\n        }\n\n    private: // CefLoadHandler interfaces\n\n        // ---------------------------------------------------------------------\n        //! \\brief Called when the browser is done loading a frame. The |frame|\n        //! value will never be empty -- call the IsMain() method to check if\n        //! this frame is the main frame. Multiple frames may be loading at the\n        //! same time. Sub-frames may start or continue loading after the main\n        //! frame load has ended. This method will not be called for the same page\n        //! navigations (fragments, history state, etc.) or for navigations that\n        //! fail or are canceled before commit. For notification of overall\n        //! browser load status use OnLoadingStateChange instead.\n        // ---------------------------------------------------------------------\n        virtual void OnLoadEnd(CefRefPtr\u003cCefBrowser\u003e browser,\n                               CefRefPtr\u003cCefFrame\u003e frame,\n                               int httpStatusCode) override\n        {\n            ...\n        }\n    };\n```\n\nThe way is always the same: to override methods from `CefXXXHandler` you have first to\noverride methods from `CefClient`:\n```\nvirtual CefRefPtr\u003cCefXXXHandler\u003e GetXXXHandler() override\n{\n   return this;\n}\n```\n\n(instead of the default method returning `nullptr`) meaning the handler is now used.\nSince `CefClient` derives from `CefBaseRefCounted` you will also have to define\nreference counting interfaces by adding `IMPLEMENT_REFCOUNTING(BrowserClient);`\n\nTo create a browser, the following code is needed:\n\n```\nCefBrowserSettings browserSettings;\nbrowserSettings.windowless_frame_rate = 60; // 30 is default\n\nCefWindowInfo window_info;\nwindow_info.SetAsWindowless(0);\n\nCefRefPtr\u003cCefBrowser\u003e browser;\nbrowser = CefBrowserHost::CreateBrowserSync(\n            window_info,\n            browserClient.get(),\n            \"https://duckduckgo.com/\",\n            browserSettings,\n            nullptr, nullptr);\n```\n\nCEF needs to be fed with mouse position, keyboard pressed, window reshaped ...\nFor example, for the mouse:\n\n```\nCefMouseEvent evt;\nevt.x = from_sdl2_mouse.x;\nevt.y = from_sdl2_mouse.y;\nm_browser-\u003eGetHost()-\u003eSendMouseMoveEvent(evt, mouse_leave);\n```\n\n## Other projects\n\n- CppCon19: Embrace Modern Technology: Using HTML 5 for GUI in C++ - Borislav Stanimirov. GitHub: https://github.com/iboB/html5-gui-demo Youtube video: https://www.youtube.com/watch?v=bbbcZd4cuxg\n- For Godot: https://github.com/stigmee/gdnative-cef\n- For C++/Qt: https://github.com/CefView/CefViewCore\n- For C++/Python: https://github.com/cztomczak/cefpython\n- For C#/Unity: https://github.com/Voltstro-Studios/UnityWebBrowser\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flecrapouille%2Foffscreencef","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flecrapouille%2Foffscreencef","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flecrapouille%2Foffscreencef/lists"}