Skip to content

Commit

Permalink
Linux WebView implementation (#1985)
Browse files Browse the repository at this point in the history
* linux webview start

* Linux UIWebView phase1

* update setup for linux uiwebview

* add depends for arch

* removed unnecessary XReparent call

* Fixed some XWayland issues

* finalizing linux webview implementation

* enabled webview tests on linux

* updated copyright link

* removed some unnecessary comments

* some formatting

* some more formatting

* fixed a test typo

* removed some unnecessary codes

* removed some unnecessary comments
  • Loading branch information
IamSanjid authored Jun 13, 2024
1 parent 3bcc1cf commit 6cb7618
Show file tree
Hide file tree
Showing 14 changed files with 1,302 additions and 168 deletions.
15 changes: 15 additions & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,21 @@ if(ANDROID)
add_subdirectory(${_AX_ROOT}/core/platform/android ${ENGINE_BINARY_PATH}/core/cpp-android)
endif()

if(LINUX)
find_package(X11 REQUIRED)
target_include_directories(${_AX_CORE_LIB} PUBLIC "${X11_X11_INCLUDE_PATH}")
# X11 gets linked by cmake/Modules/AXLinkHelpers.cmake

# including GTK3.0 and WebKit2Gtk4.0
find_package(PkgConfig REQUIRED)
pkg_check_modules(GKT3 REQUIRED gtk+-3.0)
pkg_check_modules(WEBKIT2GTK webkit2gtk-4.0)

target_include_directories(${_AX_CORE_LIB} PUBLIC ${GTK3_INCLUDE_DIRS} ${WEBKIT2GTK_INCLUDE_DIRS})
target_link_directories(${_AX_CORE_LIB} PRIVATE ${GTK3_LIBRARY_DIRS} ${WEBKIT2GTK_LIBRARY_DIRS})
target_link_libraries(${_AX_CORE_LIB} ${GTK3_LIBRARIES} ${WEBKIT2GTK_LIBRARIES})
endif()

#if(XCODE)
# # Later versions of Xcode clang want to compile C++17 with aligned allocation turned on and this is only supported on iOS 11.0+
# # TODO: Only turn this off if ${CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET} < 11.0
Expand Down
9 changes: 9 additions & 0 deletions core/platform/GLView.h
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,15 @@ class AX_DLL GLView : public Object
virtual void* getNSGLContext() = 0; // stevetranby: added
#endif /* (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) */

#if (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)
virtual void* getX11Window() = 0;
virtual void* getX11Display() = 0;
/* TODO: Implement AX_PLATFORM_LINUX_WAYLAND
virtual void* getWaylandWindow() = 0;
virtual void* getWaylandDisplay() = 0;
*/
#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)

/**
* Renders a Scene with a Renderer
* This method is called directly by the Director
Expand Down
34 changes: 32 additions & 2 deletions core/platform/GLViewImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ THE SOFTWARE.
# endif
#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC)

#if (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)
# ifndef GLFW_EXPOSE_NATIVE_X11
# define GLFW_EXPOSE_NATIVE_X11
# endif
# ifndef GLFW_EXPOSE_NATIVE_WAYLAND
# define GLFW_EXPOSE_NATIVE_WAYLAND
# endif
#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)

#if (AX_TARGET_PLATFORM != AX_PLATFORM_WASM)
# include <GLFW/glfw3native.h>
#endif
Expand Down Expand Up @@ -382,6 +391,27 @@ void* GLViewImpl::getNSGLContext()
} // stevetranby: added
#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC)

#if (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)
void* GLViewImpl::getX11Window()
{
return (void*)glfwGetX11Window(_mainWindow);
}
void* GLViewImpl::getX11Display()
{
return (void*)glfwGetX11Display();
}
/* TODO: Implement AX_PLATFORM_LINUX_WAYLAND
void* GLViewImpl::getWaylandWindow()
{
return (void*)glfwGetWaylandWindow(_mainWindow);
}
void* GLViewImpl::getWaylandDisplay()
{
return (void*)glfwGetWaylandDisplay();
}
*/
#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)

GLViewImpl* GLViewImpl::create(std::string_view viewName)
{
return GLViewImpl::create(viewName, false);
Expand Down Expand Up @@ -557,8 +587,8 @@ bool GLViewImpl::initWithRect(std::string_view viewName, const ax::Rect& rect, f
glfwSetCursorPosCallback(_mainWindow, GLFWEventHandler::onGLFWMouseMoveCallBack);
#if defined(__EMSCRIPTEN__)
// clang-format off
_isTouchDevice = !!EM_ASM_INT(return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
_isTouchDevice = !!EM_ASM_INT(return (('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0)) ? 1 : 0;
);
if (_isTouchDevice)
Expand Down
9 changes: 9 additions & 0 deletions core/platform/GLViewImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ class AX_DLL GLViewImpl : public GLView
void* getNSGLContext() override; // stevetranby: added
#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_MAC)

#if (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)
void* getX11Window() override;
void* getX11Display() override;
/* TODO: Implement AX_PLATFORM_LINUX_WAYLAND
void* getWaylandWindow() override;
void* getWaylandDisplay() override;
*/
#endif // #if (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)

protected:
GLViewImpl(bool initglfw = true);
virtual ~GLViewImpl();
Expand Down
4 changes: 4 additions & 0 deletions core/ui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,13 @@ elseif(APPLE)
elseif(LINUX)
set(_AX_UI_SPECIFIC_HEADER
ui/UIEditBox/UIEditBoxImpl-linux.h
ui/UIWebView/UIWebView.h
ui/UIWebView/UIWebViewImpl-linux.h
)
set(_AX_UI_SPECIFIC_SRC
ui/UIEditBox/UIEditBoxImpl-linux.cpp
ui/UIWebView/UIWebViewImpl-linux.cpp
ui/UIWebView/UIWebView.cpp
)
elseif(EMSCRIPTEN)
set(_AX_UI_SPECIFIC_SRC
Expand Down
4 changes: 3 additions & 1 deletion core/ui/UIWebView/UIWebView-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@

/// @cond DO_NOT_SHOW

#if (defined(_WIN32) && defined(AX_ENABLE_MSEDGE_WEBVIEW2)) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS)
#if (defined(_WIN32) && defined(AX_ENABLE_MSEDGE_WEBVIEW2)) || \
(AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS || \
AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)

#include "ui/UIWebView/UIWebView.h"
#include "platform/GLView.h"
Expand Down
7 changes: 6 additions & 1 deletion core/ui/UIWebView/UIWebView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@
# include "ui/UIWebView/UIWebViewImpl-win32.h"
# include "ui/UIWebView/UIWebView-inl.h"

#endif
#elif (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)

# include "ui/UIWebView/UIWebViewImpl-linux.h"
# include "ui/UIWebView/UIWebView-inl.h"

#endif
4 changes: 3 additions & 1 deletion core/ui/UIWebView/UIWebView.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
#include "ui/GUIExport.h"
#include "base/Data.h"

#if (defined(_WIN32) && defined(AX_ENABLE_MSEDGE_WEBVIEW2)) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS)
#if (defined(_WIN32) && defined(AX_ENABLE_MSEDGE_WEBVIEW2)) || \
(AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID || AX_TARGET_PLATFORM == AX_PLATFORM_IOS || \
AX_TARGET_PLATFORM == AX_PLATFORM_LINUX)
/**
* @addtogroup ui
* @{
Expand Down
184 changes: 184 additions & 0 deletions core/ui/UIWebView/UIWebViewCommon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/****************************************************************************
Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md).
https://axmol.dev/
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 __AXMOL__UI__WEBVIEWCOMMON_H_
#define __AXMOL__UI__WEBVIEWCOMMON_H_

#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "base/Utils.h"

namespace webview_common
{
USING_NS_AX;
using namespace rapidjson;

inline std::string htmlFromUri(std::string_view s)
{
if (s.substr(0, 15) == "data:text/html,")
{
return utils::urlDecode(s.substr(15));
}
return "";
}

inline int jsonUnescape(const char* s, size_t n, char* out)
{
int r = 0;
if (*s++ != '"')
{
return -1;
}
while (n > 2)
{
char c = *s;
if (c == '\\')
{
s++;
n--;
switch (*s)
{
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case '\\':
c = '\\';
break;
case '/':
c = '/';
break;
case '\"':
c = '\"';
break;
default: // TODO: support unicode decoding
return -1;
}
}
if (out != NULL)
{
*out++ = c;
}
s++;
n--;
r++;
}
if (*s != '"')
{
return -1;
}
if (out != NULL)
{
*out = '\0';
}
return r;
}

// These are the results that must be returned by this method
// assert(jsonParse(R"({"foo":"bar"})", "foo", -1) == "bar");
// assert(jsonParse(R"({"foo":""})", "foo", -1) == "");
// assert(jsonParse(R"(["foo", "bar", "baz"])", "", 0) == "foo");
// assert(jsonParse(R"(["foo", "bar", "baz"])", "", 2) == "baz");
// The following is a special case, where the exact json string is not returned due
// to how rapidjson re-creates the nested object, original: "{"bar": 1}", parsed result: "{"bar":1}"
// assert(jsonParse(R"({"foo": {"bar": 1}})", "foo", -1) == R"({"bar":1})");
inline std::string jsonParse(std::string_view s, std::string_view key, const int index)
{
const char* value = nullptr;
size_t value_sz{};
StringBuffer sb;
Writer<StringBuffer> writer(sb);
Document d;
d.Parse(s.data());
if (key.empty() && index > -1)
{
if (d.IsArray())
{
auto&& jsonArray = d.GetArray();
if (SizeType(index) < jsonArray.Size())
{
auto&& arrayValue = jsonArray[SizeType(index)];
value = arrayValue.GetString();
value_sz = arrayValue.GetStringLength();
}
}
}
else
{
auto&& fieldItr = d.FindMember(key.data());
if (fieldItr != d.MemberEnd())
{
auto&& jsonValue = fieldItr->value;
if (jsonValue.IsString())
{
value = jsonValue.GetString();
value_sz = jsonValue.GetStringLength();
}
else
{
jsonValue.Accept(writer);
value = sb.GetString();
value_sz = sb.GetLength();
}
}
}

if (value != nullptr)
{
if (value[0] != '"')
{
return std::string(value, value_sz);
}

const auto n = jsonUnescape(value, value_sz, nullptr);
if (n > 0)
{
const auto decoded = std::unique_ptr<char[]>(new char[n + 1]);
jsonUnescape(value, value_sz, decoded.get());
return std::string(decoded.get(), n);
}
}
return "";
}

static std::string getDataURI(const ax::Data& data, std::string_view mime_type)
{
auto encodedData = utils::base64Encode(std::span{data.getBytes(), data.getBytes() + data.getSize()});
return std::string{"data:"}.append(mime_type).append(";base64,").append(utils::urlEncode(encodedData));
}

} // namespace webview_common

#endif // __AXMOL__UI__WEBVIEWCOMMON_H_
Loading

0 comments on commit 6cb7618

Please sign in to comment.