Skip to content

Commit

Permalink
Refactor shell, introduce backends
Browse files Browse the repository at this point in the history
A backend is a combination of a renderer and a platform. The shell is now only used for common functions specific to the included samples, most of the old shell has been replaced by the new backends. The backend used for the samples can be selected during CMake configuration, allowing all the samples to run on any backend.

All samples and tests have been updated to work with the backends interface, which is a very light abstraction over all the different backends. The old SFML and SDL samples are removed, instead, they can be selected as backends that work on all the samples.

A new GLFW backend has been added, along with the SFML and SDL platforms ported from the old samples. The old macOS shell has been removed as it used a legacy API that is no longer working on modern Apple devices. Now the samples build again on macOS using one of the windowing libraries such as GLFW or SDL.

See the Backends section in 'readme.md' for more details.

Development commits:
- Start reworking shell.
- Refactoring shell, replaces old shell
- Update Windows include
- Update visual tests, unit tests, and benchmarks for new shell interface
- Update demo sample for new shell interface
- Minor include fix for Win32-GL2 backend
- Fix win32 platform backend
- Add backend for X11
- Fix tests
- Rename include headers for windows and xlib
- Update Win32/GL2 backend
- Rename shell function names
- PlatformExtensions default file extension
- Update basic samples and tutorials for new shell
- Update invader sample for the new shell
- Update LuaInvader sample for the new shell
- Add SDL2 backend
- Add SDL backend with native SDL renderer
- Add SFML backend with OpenGL renderer. Update SDL backend and GL2 renderer.
- Refactor shell.
- Remove old shell code.
- Remove SDL2, SDL2renderer, and SFML samples. These are replaced by backends that work with all the samples.
- Move Backends to project root directory.
- Clean up CMake code for the shell and backends.
- Fix SVG and Lottie samples.
- Fix include, update CI
- Additional include fixes, update CI
- Minor fix
- Add macOS library to CMake
- Minor CMake fixes
- Add GLFW backend
- Some cleanup to shell and backends
- Remove GL2 stencil command
- Add backends readme and update samples readme
- Refactor backends, remove global state, move the main loop into the samples, call directly into the backend interface instead of through the shell.
  • Loading branch information
mikke89 committed May 31, 2022
1 parent 5af6006 commit 2edd6f9
Show file tree
Hide file tree
Showing 107 changed files with 6,540 additions and 8,884 deletions.
4 changes: 2 additions & 2 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ install:
cmake --build . --target rlottie --config Release -- "/clp:ErrorsOnly"
cd ../../
git clone --depth 1 --branch v2.3.0 https://github.com/sammycage/lunasvg.git
git clone --depth 1 --branch v2.3.1 https://github.com/sammycage/lunasvg.git
cd lunasvg
mkdir build
cd build
Expand Down Expand Up @@ -105,7 +105,7 @@ after_build:
cp Include/RmlUi/Core/Containers/LICENSE.txt LICENSE.Core.ThirdParty.txt
cp Source/Debugger/LICENSE.txt LICENSE.Debugger.ThirdParty.txt
7z a RmlUi-%VS_SHORTNAME%-%PLATFORM_NAME%.zip Bin/ Include/ Samples/ Build.txt readme.md changelog.md LICENSE* Dependencies/freetype-%FREETYPE_VER%/ Dependencies/rlottie/COPYING Dependencies/rlottie/MPL_SOURCE.txt Dependencies/rlottie/licenses/ Dependencies/lunasvg/LICENSE
7z a RmlUi-%VS_SHORTNAME%-%PLATFORM_NAME%.zip Backends/ Bin/ Include/ Samples/ Build.txt readme.md changelog.md LICENSE* Dependencies/freetype-%FREETYPE_VER%/ Dependencies/rlottie/COPYING Dependencies/rlottie/MPL_SOURCE.txt Dependencies/rlottie/licenses/ Dependencies/lunasvg/LICENSE
mkdir Samples\Dependencies\freetype-%FREETYPE_VER%, Samples\Dependencies\rlottie, Samples\Dependencies\rlottie\licenses, Samples\Dependencies\lunasvg
cp LICENSE* Samples
Expand Down
25 changes: 17 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ jobs:
include:
- cc: clang
cxx: clang++
cmake_options: -DENABLE_PRECOMPILED_HEADERS=OFF
cmake_options: -DENABLE_PRECOMPILED_HEADERS=OFF -DSAMPLES_BACKEND=GLFW_GL2
- cmake_options: -DBUILD_TESTING=ON -DENABLE_PRECOMPILED_HEADERS=OFF
enable_testing: true
- cmake_options: -DNO_FONT_INTERFACE_DEFAULT=ON -DENABLE_LOTTIE_PLUGIN=ON
- cmake_options: -DDISABLE_RTTI_AND_EXCEPTIONS=ON
- cmake_options: -DNO_THIRDPARTY_CONTAINERS=ON
- cmake_options: -DNO_FONT_INTERFACE_DEFAULT=ON -DENABLE_LOTTIE_PLUGIN=ON -DSAMPLES_BACKEND=X11_GL2
- cmake_options: -DDISABLE_RTTI_AND_EXCEPTIONS=ON -DSAMPLES_BACKEND=SDL_GL2
- cmake_options: -DNO_THIRDPARTY_CONTAINERS=ON -DSAMPLES_BACKEND=SFML_GL2

steps:
- uses: actions/checkout@v2

- name: Install Dependencies
run: |-
sudo apt-get update
sudo apt-get install cmake ninja-build libsdl2-dev libsdl2-image-dev libfreetype6-dev libglew-dev liblua5.2-dev libsfml-dev librlottie-dev
sudo apt-get install cmake ninja-build libsdl2-dev libsdl2-image-dev libfreetype6-dev libglew-dev liblua5.2-dev libsfml-dev librlottie-dev libglfw3-dev
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/Build
Expand Down Expand Up @@ -59,20 +59,29 @@ jobs:

env:
BUILD_TYPE: Release


strategy:
fail-fast: false
matrix:
include:
- cmake_options: -DSAMPLES_BACKEND=auto
- cmake_options: -DSAMPLES_BACKEND=GLFW_GL2

steps:
- uses: actions/checkout@v2

- name: Install Dependencies
run: brew install lua
run: brew install lua sdl2 sdl2_image glfw

- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/Build

- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/Build
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_LUA_BINDINGS=ON -DBUILD_SAMPLES=OFF -DWARNINGS_AS_ERRORS=ON
run: >-
cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_LUA_BINDINGS=ON -DBUILD_SAMPLES=ON -DWARNINGS_AS_ERRORS=ON
${{ matrix.cmake_options }}
- name: Build
working-directory: ${{github.workspace}}/Build
Expand Down
72 changes: 72 additions & 0 deletions Backends/RmlUi_Backend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* This source file is part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019 The RmlUi Team, and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

#ifndef RMLUI_BACKENDS_BACKEND_H
#define RMLUI_BACKENDS_BACKEND_H

#include <RmlUi/Core/Input.h>
#include <RmlUi/Core/RenderInterface.h>
#include <RmlUi/Core/SystemInterface.h>
#include <RmlUi/Core/Types.h>

using KeyDownCallback = bool (*)(Rml::Context* context, Rml::Input::KeyIdentifier key, int key_modifier, float native_dp_ratio, bool priority);

/**
This interface serves as a basic abstraction over the various backends included with RmlUi. It is mainly intended as an example to get something
simple up and running, and provides just enough functionality for the included samples.
This interface may be used directly for simple applications and testing. However, for anything more advanced we recommend to use the backend as a
starting point and copy relevant parts into the main loop of your application. On the other hand, the underlying platform and renderer used by the
backend are intended to be re-usable as is.
*/
namespace Backend {

// Initializes the backend, including the custom system and render interfaces, and opens a window for rendering the RmlUi context.
bool Initialize(const char* window_name, int width, int height, bool allow_resize);
// Closes the window and release all resources owned by the backend, including the system and render interfaces.
void Shutdown();

// Returns a pointer to the custom system interface which should be provided to RmlUi.
Rml::SystemInterface* GetSystemInterface();
// Returns a pointer to the custom render interface which should be provided to RmlUi.
Rml::RenderInterface* GetRenderInterface();

// Polls and processes events from the current platform, and applies any relevant events to the provided RmlUi context and the key down callback.
// @return False to indicate that the application should be closed.
bool ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback = nullptr);
// Request application closure during the next event processing call.
void RequestExit();

// Prepares the render state to accept rendering commands from RmlUi, call before rendering the RmlUi context.
void BeginFrame();
// Presents the rendered frame to the screen, call after rendering the RmlUi context.
void PresentFrame();

} // namespace Backend

#endif
246 changes: 246 additions & 0 deletions Backends/RmlUi_Backend_GLFW_GL2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
/*
* This source file is part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019 The RmlUi Team, and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

#include "RmlUi_Backend.h"
#include "RmlUi_Platform_GLFW.h"
#include "RmlUi_Renderer_GL2.h"
#include <RmlUi/Core/Context.h>
#include <RmlUi/Core/Input.h>
#include <RmlUi/Core/Profiling.h>
#include <GLFW/glfw3.h>

static void SetupCallbacks(GLFWwindow* window);

static void LogErrorFromGLFW(int error, const char* description)
{
Rml::Log::Message(Rml::Log::LT_ERROR, "GLFW error (0x%x): %s", error, description);
}

/**
Global data used by this backend.
Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown().
*/
struct BackendData {
SystemInterface_GLFW system_interface;
RenderInterface_GL2 render_interface;
GLFWwindow* window = nullptr;
int glfw_active_modifiers = 0;
bool context_dimensions_dirty = true;

// Arguments set during event processing and nulled otherwise.
Rml::Context* context = nullptr;
KeyDownCallback key_down_callback = nullptr;
};
static Rml::UniquePtr<BackendData> data;

bool Backend::Initialize(const char* name, int width, int height, bool allow_resize)
{
RMLUI_ASSERT(!data);

glfwSetErrorCallback(LogErrorFromGLFW);

if (!glfwInit())
return false;

// Set window hints for OpenGL 2 context creation.
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);

// Request stencil buffer of at least 8-bit size to supporting clipping on transformed elements.
glfwWindowHint(GLFW_STENCIL_BITS, 8);

// Enable MSAA for better-looking visuals, especially when transforms are applied.
glfwWindowHint(GLFW_SAMPLES, 2);

// Apply window properties and create it.
glfwWindowHint(GLFW_RESIZABLE, allow_resize ? GLFW_TRUE : GLFW_FALSE);
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);

GLFWwindow* window = glfwCreateWindow(width, height, name, nullptr, nullptr);
if (!window)
return false;

glfwMakeContextCurrent(window);
glfwSwapInterval(1);

data = Rml::MakeUnique<BackendData>();
data->window = window;
data->system_interface.SetWindow(window);

// The window size may have been scaled by DPI settings, get the actual pixel size.
glfwGetFramebufferSize(window, &width, &height);
data->render_interface.SetViewport(width, height);

// Setup the input and window event callback functions.
SetupCallbacks(window);

return true;
}

void Backend::Shutdown()
{
RMLUI_ASSERT(data);
glfwDestroyWindow(data->window);
data.reset();
glfwTerminate();
}

Rml::SystemInterface* Backend::GetSystemInterface()
{
RMLUI_ASSERT(data);
return &data->system_interface;
}

Rml::RenderInterface* Backend::GetRenderInterface()
{
RMLUI_ASSERT(data);
return &data->render_interface;
}

bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback)
{
RMLUI_ASSERT(data && context);

// The initial window size may have been affected by system DPI settings, apply the actual pixel size and dp-ratio to the context.
if (data->context_dimensions_dirty)
{
data->context_dimensions_dirty = false;

Rml::Vector2i window_size;
float dp_ratio = 1.f;
glfwGetFramebufferSize(data->window, &window_size.x, &window_size.y);
glfwGetWindowContentScale(data->window, &dp_ratio, nullptr);

context->SetDimensions(window_size);
context->SetDensityIndependentPixelRatio(dp_ratio);
}

data->context = context;
data->key_down_callback = key_down_callback;

glfwPollEvents();

data->context = nullptr;
data->key_down_callback = nullptr;

return !glfwWindowShouldClose(data->window);
}

void Backend::RequestExit()
{
RMLUI_ASSERT(data);
glfwSetWindowShouldClose(data->window, GLFW_TRUE);
}

void Backend::BeginFrame()
{
RMLUI_ASSERT(data);
data->render_interface.BeginFrame();
data->render_interface.Clear();
}

void Backend::PresentFrame()
{
RMLUI_ASSERT(data);
data->render_interface.EndFrame();
glfwSwapBuffers(data->window);

// Optional, used to mark frames during performance profiling.
RMLUI_FrameMark;
}

static void SetupCallbacks(GLFWwindow* window)
{
RMLUI_ASSERT(data);

// Key input
glfwSetKeyCallback(window, [](GLFWwindow* /*window*/, int glfw_key, int /*scancode*/, int glfw_action, int glfw_mods) {
if (!data->context)
return;

// Store the active modifiers for later because GLFW doesn't provide them in the callbacks to the mouse input events.
data->glfw_active_modifiers = glfw_mods;

// Override the default key event callback to add global shortcuts for the samples.
Rml::Context* context = data->context;
KeyDownCallback key_down_callback = data->key_down_callback;

switch (glfw_action)
{
case GLFW_PRESS:
case GLFW_REPEAT:
{
const Rml::Input::KeyIdentifier key = RmlGLFW::ConvertKey(glfw_key);
const int key_modifier = RmlGLFW::ConvertKeyModifiers(glfw_mods);
float dp_ratio = 1.f;
glfwGetWindowContentScale(data->window, &dp_ratio, nullptr);

// See if we have any global shortcuts that take priority over the context.
if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, true))
break;
// Otherwise, hand the event over to the context by calling the input handler as normal.
if (!RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods))
break;
// The key was not consumed by the context either, try keyboard shortcuts of lower priority.
if (key_down_callback && !key_down_callback(context, key, key_modifier, dp_ratio, false))
break;
}
break;
case GLFW_RELEASE:
RmlGLFW::ProcessKeyCallback(context, glfw_key, glfw_action, glfw_mods);
break;
}
});

glfwSetCharCallback(window, [](GLFWwindow* /*window*/, unsigned int codepoint) { RmlGLFW::ProcessCharCallback(data->context, codepoint); });

// Mouse input
glfwSetCursorPosCallback(window, [](GLFWwindow* /*window*/, double xpos, double ypos) {
RmlGLFW::ProcessCursorPosCallback(data->context, xpos, ypos, data->glfw_active_modifiers);
});

glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) {
data->glfw_active_modifiers = mods;
RmlGLFW::ProcessMouseButtonCallback(data->context, button, action, mods);
});

glfwSetScrollCallback(window, [](GLFWwindow* /*window*/, double /*xoffset*/, double yoffset) {
RmlGLFW::ProcessScrollCallback(data->context, yoffset, data->glfw_active_modifiers);
});

// Window events
glfwSetFramebufferSizeCallback(window, [](GLFWwindow* /*window*/, int width, int height) {
data->render_interface.SetViewport(width, height);
RmlGLFW::ProcessFramebufferSizeCallback(data->context, width, height);
});

glfwSetWindowContentScaleCallback(window,
[](GLFWwindow* /*window*/, float xscale, float /*yscale*/) { RmlGLFW::ProcessContentScaleCallback(data->context, xscale); });
}
Loading

0 comments on commit 2edd6f9

Please sign in to comment.