From 2c873db4b6d7e48307ef064f49cae80a18cee8c3 Mon Sep 17 00:00:00 2001 From: "carlosgc@webkit.org" Date: Mon, 11 Jun 2018 06:43:14 +0000 Subject: [PATCH] [WPE] Add a MiniBrowser and use it to run WebDriver tests https://bugs.webkit.org/show_bug.cgi?id=186345 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed by Žan Doberšek. .: Add an option to enable building the MiniBrowser. * Source/cmake/FindWaylandProtocols.cmake: Added. * Source/cmake/OptionsWPE.cmake: Source/WebDriver: Use MiniBrowser instead of dyz as the default WebDriver browser for WPE. * wpe/WebDriverServiceWPE.cpp: (WebDriver::WebDriverService::platformParseCapabilities const): Tools: Most of the code is based on dyz and gtk MiniBrowser. This patch adds a new internal library WPEToolingBackends, including the headless view backend and a new window backend to be used by the MiniBrowser. MiniBrowser can also run in headless mode, by using the headless backend instead of the window one, which will allow us to run the WebDriver tests in the bots. * CMakeLists.txt: * MiniBrowser/wpe/CMakeLists.txt: Added. * MiniBrowser/wpe/main.cpp: Added. (automationStartedCallback): (createViewBackend): (main): * Scripts/run-minibrowser: Remove WPE specific code. * Scripts/run-webdriver-tests: Add headless display-server option. * Scripts/webkitdirs.pm: (launcherName): Remove WPE specific code. * Scripts/webkitpy/webdriver_tests/webdriver_driver_wpe.py: (WebDriverWPE.browser_name): Return MiniBrowser. (WebDriverWPE.browser_path): Return the path to the MiniBrowser in build dir. (WebDriverWPE.browser_args): Add --headless when running in headless mode. (WebDriverWPE.capabilities): Use the full browser path. * Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py: (WebDriverTestRunnerSelenium.collect_tests): Fix early return value. * TestWebKitAPI/PlatformWPE.cmake: Use WPEToolingBackends instead of HeadlessViewBackend. * TestWebKitAPI/PlatformWebView.h: Ditto. * TestWebKitAPI/glib/PlatformWPE.cmake: Ditto * TestWebKitAPI/glib/WebKitGLib/TestMain.h: (Test::createWebViewBackend): Ditto. * TestWebKitAPI/wpe/PlatformWebViewWPE.cpp: (TestWebKitAPI::PlatformWebView::initialize): Ditto. * WebKitTestRunner/PlatformWPE.cmake: Ditto. * WebKitTestRunner/PlatformWebView.h: Ditto. * WebKitTestRunner/wpe/PlatformWebViewWPE.cpp: (WTR::PlatformWebView::PlatformWebView): Ditto. * wpe/HeadlessViewBackend/CMakeLists.txt: Removed. * wpe/backends/CMakeLists.txt: Added. * wpe/backends/HeadlessViewBackend.cpp: Renamed from Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp. (WPEToolingBackends::getEGLDisplay): (WPEToolingBackends::HeadlessViewBackend::HeadlessViewBackend): (WPEToolingBackends::HeadlessViewBackend::~HeadlessViewBackend): (WPEToolingBackends::HeadlessViewBackend::createSnapshot): (WPEToolingBackends::HeadlessViewBackend::performUpdate): (WPEToolingBackends::HeadlessViewBackend::displayBuffer): * wpe/backends/HeadlessViewBackend.h: Renamed from Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.h. * wpe/backends/ViewBackend.cpp: Added. (WPEToolingBackends::ViewBackend::ViewBackend): (WPEToolingBackends::ViewBackend::~ViewBackend): (WPEToolingBackends::ViewBackend::initialize): (WPEToolingBackends::ViewBackend::setInputClient): (WPEToolingBackends::ViewBackend::backend const): (WPEToolingBackends::ViewBackend::dispatchInputPointerEvent): (WPEToolingBackends::ViewBackend::dispatchInputAxisEvent): (WPEToolingBackends::ViewBackend::dispatchInputKeyboardEvent): * wpe/backends/ViewBackend.h: Added. * wpe/backends/WindowViewBackend.cpp: Added. (WPEToolingBackends::WindowViewBackend::WindowViewBackend): (WPEToolingBackends::WindowViewBackend::~WindowViewBackend): (WPEToolingBackends::WindowViewBackend::displayBuffer): (WPEToolingBackends::WindowViewBackend::handleKeyEvent): * wpe/backends/WindowViewBackend.h: Added. * wpe/jhbuild.modules: Remove dyz and add wayland-protocols. git-svn-id: http://svn.webkit.org/repository/webkit/trunk@232670 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- ChangeLog | 12 + Source/WebDriver/ChangeLog | 12 + Source/WebDriver/wpe/WebDriverServiceWPE.cpp | 2 +- Source/cmake/FindWaylandProtocols.cmake | 38 ++ Source/cmake/OptionsWPE.cmake | 1 + Tools/CMakeLists.txt | 9 +- Tools/ChangeLog | 68 ++ Tools/MiniBrowser/wpe/CMakeLists.txt | 36 ++ Tools/MiniBrowser/wpe/main.cpp | 196 ++++++ Tools/Scripts/run-minibrowser | 13 +- Tools/Scripts/run-webdriver-tests | 5 +- Tools/Scripts/webkitdirs.pm | 4 +- .../webdriver_tests/webdriver_driver_wpe.py | 24 +- .../webdriver_test_runner_selenium.py | 2 +- Tools/TestWebKitAPI/PlatformWPE.cmake | 4 +- Tools/TestWebKitAPI/PlatformWebView.h | 4 +- Tools/TestWebKitAPI/glib/PlatformWPE.cmake | 4 +- .../TestWebKitAPI/glib/WebKitGLib/TestMain.h | 4 +- .../TestWebKitAPI/wpe/PlatformWebViewWPE.cpp | 2 +- Tools/WebKitTestRunner/PlatformWPE.cmake | 4 +- Tools/WebKitTestRunner/PlatformWebView.h | 4 +- .../wpe/PlatformWebViewWPE.cpp | 2 +- Tools/wpe/HeadlessViewBackend/CMakeLists.txt | 23 - Tools/wpe/backends/CMakeLists.txt | 53 ++ .../HeadlessViewBackend.cpp | 150 ++--- .../HeadlessViewBackend.h | 41 +- Tools/wpe/backends/ViewBackend.cpp | 137 ++++ Tools/wpe/backends/ViewBackend.h | 78 +++ Tools/wpe/backends/WindowViewBackend.cpp | 611 ++++++++++++++++++ Tools/wpe/backends/WindowViewBackend.h | 121 ++++ Tools/wpe/jhbuild.modules | 11 +- 31 files changed, 1479 insertions(+), 196 deletions(-) create mode 100644 Source/cmake/FindWaylandProtocols.cmake create mode 100644 Tools/MiniBrowser/wpe/CMakeLists.txt create mode 100644 Tools/MiniBrowser/wpe/main.cpp delete mode 100644 Tools/wpe/HeadlessViewBackend/CMakeLists.txt create mode 100644 Tools/wpe/backends/CMakeLists.txt rename Tools/wpe/{HeadlessViewBackend => backends}/HeadlessViewBackend.cpp (57%) rename Tools/wpe/{HeadlessViewBackend => backends}/HeadlessViewBackend.h (61%) create mode 100644 Tools/wpe/backends/ViewBackend.cpp create mode 100644 Tools/wpe/backends/ViewBackend.h create mode 100644 Tools/wpe/backends/WindowViewBackend.cpp create mode 100644 Tools/wpe/backends/WindowViewBackend.h diff --git a/ChangeLog b/ChangeLog index 4c8eae7fa0a50..f4a6e7dec68bc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2018-06-10 Carlos Garcia Campos + + [WPE] Add a MiniBrowser and use it to run WebDriver tests + https://bugs.webkit.org/show_bug.cgi?id=186345 + + Reviewed by Žan Doberšek. + + Add an option to enable building the MiniBrowser. + + * Source/cmake/FindWaylandProtocols.cmake: Added. + * Source/cmake/OptionsWPE.cmake: + 2018-06-06 Dan Bernstein [Xcode] Opt out of the New Build System diff --git a/Source/WebDriver/ChangeLog b/Source/WebDriver/ChangeLog index e0a28bb8fb697..807a5be7d2359 100644 --- a/Source/WebDriver/ChangeLog +++ b/Source/WebDriver/ChangeLog @@ -1,3 +1,15 @@ +2018-06-10 Carlos Garcia Campos + + [WPE] Add a MiniBrowser and use it to run WebDriver tests + https://bugs.webkit.org/show_bug.cgi?id=186345 + + Reviewed by Žan Doberšek. + + Use MiniBrowser instead of dyz as the default WebDriver browser for WPE. + + * wpe/WebDriverServiceWPE.cpp: + (WebDriver::WebDriverService::platformParseCapabilities const): + 2018-06-04 Carlos Garcia Campos [WPE] WebDriver: stop making mandatory to provide a browser path if wpe:browserOptions is present in capabilities diff --git a/Source/WebDriver/wpe/WebDriverServiceWPE.cpp b/Source/WebDriver/wpe/WebDriverServiceWPE.cpp index 0aaac3dffa030..d63f5a4ba23bb 100644 --- a/Source/WebDriver/wpe/WebDriverServiceWPE.cpp +++ b/Source/WebDriver/wpe/WebDriverServiceWPE.cpp @@ -81,7 +81,7 @@ bool WebDriverService::platformMatchCapability(const String&, const RefPtr { ASCIILiteral("--automation") }; RefPtr browserOptions; diff --git a/Source/cmake/FindWaylandProtocols.cmake b/Source/cmake/FindWaylandProtocols.cmake new file mode 100644 index 0000000000000..69bdc7a3be9ae --- /dev/null +++ b/Source/cmake/FindWaylandProtocols.cmake @@ -0,0 +1,38 @@ +# - Try to find WaylandProtocols +# Once done, this will define +# +# WAYLAND_PROTOCOLS_FOUND - system has WaylandProtocols. +# WAYLAND_PROTOCOLS_DATADIR - the WaylandProtocols data directory +# WAYLAND_SCANNER - the path to the wayland-scanner command. +# +# Copyright (C) 2018 Igalia S.L. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +find_package(PkgConfig) +pkg_check_modules(WAYLAND_PROTOCOLS wayland-protocols) + +if (WAYLAND_PROTOCOLS_FOUND) + pkg_get_variable(WAYLAND_PROTOCOLS_DATADIR wayland-protocols pkgdatadir) + find_program(WAYLAND_SCANNER NAMES wayland-scanner) + mark_as_advanced(WAYLAND_PROTOCOLS_DATADIR WAYLAND_SCANNER) +endif () diff --git a/Source/cmake/OptionsWPE.cmake b/Source/cmake/OptionsWPE.cmake index 8b0cd1f819557..10e156c967fc5 100644 --- a/Source/cmake/OptionsWPE.cmake +++ b/Source/cmake/OptionsWPE.cmake @@ -60,6 +60,7 @@ endif () SET_AND_EXPOSE_TO_BUILD(ENABLE_DEVELOPER_MODE ${DEVELOPER_MODE}) if (DEVELOPER_MODE) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_API_TESTS PRIVATE ON) + WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MINIBROWSER PUBLIC ON) endif () include(GStreamerDependencies) diff --git a/Tools/CMakeLists.txt b/Tools/CMakeLists.txt index 0a481184b2e0d..5f4f9e0c963ca 100644 --- a/Tools/CMakeLists.txt +++ b/Tools/CMakeLists.txt @@ -21,14 +21,21 @@ elseif ("${PORT}" STREQUAL "Mac") add_subdirectory(MiniBrowser/mac) elseif ("${PORT}" STREQUAL "JSCOnly") elseif ("${PORT}" STREQUAL "WPE") + if (DEVELOPER_MODE OR ENABLE_MINIBROWSER) + add_subdirectory(wpe/backends) + endif () + if (DEVELOPER_MODE) add_subdirectory(ImageDiff) add_subdirectory(WebKitTestRunner) - add_subdirectory(wpe/HeadlessViewBackend) if (ENABLE_API_TESTS) add_subdirectory(TestWebKitAPI/glib) endif () endif () + + if (ENABLE_MINIBROWSER) + add_subdirectory(MiniBrowser/wpe) + endif () elseif ("${PORT}" STREQUAL "Win") add_subdirectory(DumpRenderTree) add_subdirectory(ImageDiff) diff --git a/Tools/ChangeLog b/Tools/ChangeLog index bd019400a1634..9cc4ffead5e4b 100644 --- a/Tools/ChangeLog +++ b/Tools/ChangeLog @@ -1,3 +1,71 @@ +2018-06-10 Carlos Garcia Campos + + [WPE] Add a MiniBrowser and use it to run WebDriver tests + https://bugs.webkit.org/show_bug.cgi?id=186345 + + Reviewed by Žan Doberšek. + + Most of the code is based on dyz and gtk MiniBrowser. This patch adds a new internal library WPEToolingBackends, + including the headless view backend and a new window backend to be used by the MiniBrowser. MiniBrowser can also + run in headless mode, by using the headless backend instead of the window one, which will allow us to run the + WebDriver tests in the bots. + + * CMakeLists.txt: + * MiniBrowser/wpe/CMakeLists.txt: Added. + * MiniBrowser/wpe/main.cpp: Added. + (automationStartedCallback): + (createViewBackend): + (main): + * Scripts/run-minibrowser: Remove WPE specific code. + * Scripts/run-webdriver-tests: Add headless display-server option. + * Scripts/webkitdirs.pm: + (launcherName): Remove WPE specific code. + * Scripts/webkitpy/webdriver_tests/webdriver_driver_wpe.py: + (WebDriverWPE.browser_name): Return MiniBrowser. + (WebDriverWPE.browser_path): Return the path to the MiniBrowser in build dir. + (WebDriverWPE.browser_args): Add --headless when running in headless mode. + (WebDriverWPE.capabilities): Use the full browser path. + * Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py: + (WebDriverTestRunnerSelenium.collect_tests): Fix early return value. + * TestWebKitAPI/PlatformWPE.cmake: Use WPEToolingBackends instead of HeadlessViewBackend. + * TestWebKitAPI/PlatformWebView.h: Ditto. + * TestWebKitAPI/glib/PlatformWPE.cmake: Ditto + * TestWebKitAPI/glib/WebKitGLib/TestMain.h: + (Test::createWebViewBackend): Ditto. + * TestWebKitAPI/wpe/PlatformWebViewWPE.cpp: + (TestWebKitAPI::PlatformWebView::initialize): Ditto. + * WebKitTestRunner/PlatformWPE.cmake: Ditto. + * WebKitTestRunner/PlatformWebView.h: Ditto. + * WebKitTestRunner/wpe/PlatformWebViewWPE.cpp: + (WTR::PlatformWebView::PlatformWebView): Ditto. + * wpe/HeadlessViewBackend/CMakeLists.txt: Removed. + * wpe/backends/CMakeLists.txt: Added. + * wpe/backends/HeadlessViewBackend.cpp: Renamed from Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp. + (WPEToolingBackends::getEGLDisplay): + (WPEToolingBackends::HeadlessViewBackend::HeadlessViewBackend): + (WPEToolingBackends::HeadlessViewBackend::~HeadlessViewBackend): + (WPEToolingBackends::HeadlessViewBackend::createSnapshot): + (WPEToolingBackends::HeadlessViewBackend::performUpdate): + (WPEToolingBackends::HeadlessViewBackend::displayBuffer): + * wpe/backends/HeadlessViewBackend.h: Renamed from Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.h. + * wpe/backends/ViewBackend.cpp: Added. + (WPEToolingBackends::ViewBackend::ViewBackend): + (WPEToolingBackends::ViewBackend::~ViewBackend): + (WPEToolingBackends::ViewBackend::initialize): + (WPEToolingBackends::ViewBackend::setInputClient): + (WPEToolingBackends::ViewBackend::backend const): + (WPEToolingBackends::ViewBackend::dispatchInputPointerEvent): + (WPEToolingBackends::ViewBackend::dispatchInputAxisEvent): + (WPEToolingBackends::ViewBackend::dispatchInputKeyboardEvent): + * wpe/backends/ViewBackend.h: Added. + * wpe/backends/WindowViewBackend.cpp: Added. + (WPEToolingBackends::WindowViewBackend::WindowViewBackend): + (WPEToolingBackends::WindowViewBackend::~WindowViewBackend): + (WPEToolingBackends::WindowViewBackend::displayBuffer): + (WPEToolingBackends::WindowViewBackend::handleKeyEvent): + * wpe/backends/WindowViewBackend.h: Added. + * wpe/jhbuild.modules: Remove dyz and add wayland-protocols. + 2018-06-10 Fujii Hironori [Win][MiniBrowser] MiniBrowser class should be renamed to WebKitLegacyBrowserWindow diff --git a/Tools/MiniBrowser/wpe/CMakeLists.txt b/Tools/MiniBrowser/wpe/CMakeLists.txt new file mode 100644 index 0000000000000..f261379683b43 --- /dev/null +++ b/Tools/MiniBrowser/wpe/CMakeLists.txt @@ -0,0 +1,36 @@ +set(MINIBROWSER_DIR "${TOOLS_DIR}/MiniBrowser/wpe") + +set(MiniBrowser_SOURCES + ${MINIBROWSER_DIR}/main.cpp +) + +set(MiniBrowser_INCLUDE_DIRECTORIES + ${TOOLS_DIR}/wpe/backends/ + ${DERIVED_SOURCES_WPE_API_DIR} + ${FORWARDING_HEADERS_WPE_DIR} + ${CMAKE_BINARY_DIR}/DerivedSources/WPEToolingBackends +) + +set(MiniBrowser_SYSTEM_INCLUDE_DIRECTORIES + ${WPE_INCLUDE_DIRS} + ${WPEBACKEND_FDO_INCLUDE_DIRS} +) + +set(MiniBrowser_LIBRARIES + ${JavaScriptCore_LIBRARY_NAME} + WPEToolingBackends + WebKit + ${WPE_LIBRARIES} + ${WPEBACKEND_FDO_LIBRARIES} +) + +if (DEVELOPER_MODE) + add_definitions(-DWEBKIT_INJECTED_BUNDLE_PATH="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}") +endif () + +include_directories(${MiniBrowser_INCLUDE_DIRECTORIES}) +include_directories(SYSTEM ${MiniBrowser_SYSTEM_INCLUDE_DIRECTORIES}) +add_executable(MiniBrowser ${MiniBrowser_SOURCES}) +target_link_libraries(MiniBrowser ${MiniBrowser_LIBRARIES}) + +install(TARGETS MiniBrowser DESTINATION "${LIBEXEC_INSTALL_DIR}") diff --git a/Tools/MiniBrowser/wpe/main.cpp b/Tools/MiniBrowser/wpe/main.cpp new file mode 100644 index 0000000000000..26adc30d8b12f --- /dev/null +++ b/Tools/MiniBrowser/wpe/main.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2018 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmakeconfig.h" + +#include "HeadlessViewBackend.h" +#include "WindowViewBackend.h" +#include +#include +#include + +static const char** uriArguments; +static const char** ignoreHosts; +static gboolean headlessMode; +static gboolean privateMode; +static gboolean automationMode; +static const char* cookiesFile; +static const char* cookiesPolicy; +static const char* proxy; + +static const GOptionEntry commandLineOptions[] = +{ + { "headless", 'h', 0, G_OPTION_ARG_NONE, &headlessMode, "Run in headless mode", nullptr }, + { "private", 'p', 0, G_OPTION_ARG_NONE, &privateMode, "Run in private browsing mode", nullptr }, + { "automation", 0, 0, G_OPTION_ARG_NONE, &automationMode, "Run in automation mode", nullptr }, + { "cookies-file", 'c', 0, G_OPTION_ARG_FILENAME, &cookiesFile, "Persistent cookie storage database file", "FILE" }, + { "cookies-policy", 0, 0, G_OPTION_ARG_STRING, &cookiesPolicy, "Cookies accept policy (always, never, no-third-party). Default: no-third-party", "POLICY" }, + { "proxy", 0, 0, G_OPTION_ARG_STRING, &proxy, "Set proxy", "PROXY" }, + { "ignore-host", 0, 0, G_OPTION_ARG_STRING_ARRAY, &ignoreHosts, "Set proxy ignore hosts", "HOSTS" }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, nullptr, "[URL]" }, + { nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr } +}; + +class InputClient final : public WPEToolingBackends::ViewBackend::InputClient { +public: + InputClient(GMainLoop* loop) + : m_loop(loop) + { + } + + bool dispatchKeyboardEvent(struct wpe_input_keyboard_event* event) override + { + if (event->pressed && event->modifiers & wpe_input_keyboard_modifier_control && event->keyCode == 'q') { + g_main_loop_quit(m_loop); + return true; + } + + return false; + } + +private: + GMainLoop* m_loop { nullptr }; +}; + +static WebKitWebView* createWebViewForAutomationCallback(WebKitAutomationSession*, WebKitWebView* view) +{ + return view; +} + +static void automationStartedCallback(WebKitWebContext*, WebKitAutomationSession* session, WebKitWebView* view) +{ + auto* info = webkit_application_info_new(); + // FIXME: add version info when wpe has WebKitVersion.h + // webkit_application_info_set_version(info, WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION); + webkit_automation_session_set_application_info(session, info); + webkit_application_info_unref(info); + + g_signal_connect(session, "create-web-view", G_CALLBACK(createWebViewForAutomationCallback), view); +} + +static std::unique_ptr createViewBackend(uint32_t width, uint32_t height) +{ + if (headlessMode) + return std::make_unique(width, height); + return std::make_unique(width, height); +} + +int main(int argc, char *argv[]) +{ + // MiniBrowser only works with WPEBackend-fdo, so ensure no other backend is used, + // either by passing the WPE_BACKEND_LIBRARY env var or loading the default symlink. + g_setenv("WPE_BACKEND_LIBRARY", "libWPEBackend-fdo-0.1.so", TRUE); +#if ENABLE_DEVELOPER_MODE + g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE); +#endif + + GOptionContext* context = g_option_context_new(nullptr); + g_option_context_add_main_entries(context, commandLineOptions, nullptr); + g_option_context_add_group(context, gst_init_get_option_group()); + + GError* error = nullptr; + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_printerr("Cannot parse arguments: %s\n", error->message); + g_error_free(error); + g_option_context_free(context); + + return 1; + } + g_option_context_free(context); + + auto* loop = g_main_loop_new(nullptr, FALSE); + + auto backend = createViewBackend(1280, 720); + struct wpe_view_backend* wpeBackend = backend->backend(); + if (!wpeBackend) { + g_warning("Failed to create WPE view backend"); + g_main_loop_unref(loop); + return 1; + } + + backend->setInputClient(std::make_unique(loop)); + + auto* viewBackend = webkit_web_view_backend_new(wpeBackend, [](gpointer data) { + delete static_cast(data); + }, backend.release()); + + auto* webContext = (privateMode || automationMode) ? webkit_web_context_new_ephemeral() : webkit_web_context_get_default(); + + if (cookiesPolicy) { + auto* cookieManager = webkit_web_context_get_cookie_manager(webContext); + auto* enumClass = static_cast(g_type_class_ref(WEBKIT_TYPE_COOKIE_ACCEPT_POLICY)); + GEnumValue* enumValue = g_enum_get_value_by_nick(enumClass, cookiesPolicy); + if (enumValue) + webkit_cookie_manager_set_accept_policy(cookieManager, static_cast(enumValue->value)); + g_type_class_unref(enumClass); + } + + if (cookiesFile && !webkit_web_context_is_ephemeral(webContext)) { + auto* cookieManager = webkit_web_context_get_cookie_manager(webContext); + auto storageType = g_str_has_suffix(cookiesFile, ".txt") ? WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT : WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE; + webkit_cookie_manager_set_persistent_storage(cookieManager, cookiesFile, storageType); + } + + if (proxy) { + auto* webkitProxySettings = webkit_network_proxy_settings_new(proxy, ignoreHosts); + webkit_web_context_set_network_proxy_settings(webContext, WEBKIT_NETWORK_PROXY_MODE_CUSTOM, webkitProxySettings); + webkit_network_proxy_settings_free(webkitProxySettings); + } + + const char* singleprocess = g_getenv("MINIBROWSER_SINGLEPROCESS"); + webkit_web_context_set_process_model(webContext, (singleprocess && *singleprocess) ? + WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS : WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES); + + auto* settings = webkit_settings_new_with_settings( + "enable-developer-extras", TRUE, + "enable-webgl", TRUE, + "enable-media-stream", TRUE, + nullptr); + + auto* webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, + "backend", viewBackend, + "web-context", webContext, + "settings", settings, + "is-controlled-by-automation", automationMode, + nullptr)); + g_object_unref(settings); + + webkit_web_context_set_automation_allowed(webContext, automationMode); + g_signal_connect(webContext, "automation-started", G_CALLBACK(automationStartedCallback), webView); + + if (uriArguments) + webkit_web_view_load_uri(webView, uriArguments[0]); + else if (!automationMode) + webkit_web_view_load_uri(webView, "https://wpewebkit.org"); + + g_main_loop_run(loop); + + g_object_unref(webView); + if (privateMode) + g_object_unref(webContext); + g_main_loop_unref(loop); + + return 0; +} diff --git a/Tools/Scripts/run-minibrowser b/Tools/Scripts/run-minibrowser index 86ccca0e33fa7..495440531aecc 100755 --- a/Tools/Scripts/run-minibrowser +++ b/Tools/Scripts/run-minibrowser @@ -44,7 +44,7 @@ my @jhbuildWrapper; prohibitUnknownPort(); setConfiguration(); -if (isGtk()) { +if (isGtk() || isWPE()) { # Check to see that all the frameworks are built. checkFrameworks(); @@ -53,17 +53,6 @@ if (isGtk()) { @jhbuildWrapper = wrapperPrefixIfNeeded(); print "Starting MiniBrowser.\n"; exec @jhbuildWrapper, $launcherPath, @ARGV or die; -} elsif (isWPE()) { - # Check to see that all the frameworks are built. - checkFrameworks(); - - $launcherName = launcherName(); - @jhbuildWrapper = wrapperPrefixIfNeeded(); - $ENV{"WEBKIT_EXEC_PATH"} = catdir(productDir(), "bin"); - $ENV{"WEBKIT_INJECTED_BUNDLE_PATH"} = catdir(productDir(), "lib"); - $ENV{"LD_LIBRARY_PATH"} = catdir(productDir(), "lib") . (defined $ENV{"LD_LIBRARY_PATH"} ? ":" . $ENV{"LD_LIBRARY_PATH"} : "" ); - print "Starting $launcherName \n"; - exec @jhbuildWrapper, $launcherName, @ARGV or die; } elsif (isAppleCocoaWebKit()) { printHelpAndExitForRunAndDebugWebKitAppIfNeeded(); exit exitStatus(runMiniBrowser()); diff --git a/Tools/Scripts/run-webdriver-tests b/Tools/Scripts/run-webdriver-tests index 4051a1a26fb28..10f7fede66f76 100755 --- a/Tools/Scripts/run-webdriver-tests +++ b/Tools/Scripts/run-webdriver-tests @@ -49,9 +49,10 @@ option_parser.add_option('--timeout', action='store', type='int', dest='timeout' help='Time in seconds until a test times out (use 0 to disable)') option_parser.add_option('--json-output', action='store', metavar="FILE", help='Write results to JSON file at the given path') -option_parser.add_option('--display-server', choices=['xvfb', 'xorg', 'weston', 'wayland'], default='xvfb', +option_parser.add_option('--display-server', choices=['xvfb', 'xorg', 'weston', 'wayland', 'headless'], default='xvfb', help='"xvfb": Use a virtualized X11 server. "xorg": Use the current X11 session. ' - '"weston": Use a virtualized Weston server. "wayland": Use the current wayland session.') + '"weston": Use a virtualized Weston server. "wayland": Use the current wayland session.' + '"headless": Headless mode in current session') options, args = option_parser.parse_args() diff --git a/Tools/Scripts/webkitdirs.pm b/Tools/Scripts/webkitdirs.pm index 27716a4e74d13..909218a204435 100755 --- a/Tools/Scripts/webkitdirs.pm +++ b/Tools/Scripts/webkitdirs.pm @@ -1689,14 +1689,12 @@ sub launcherPath() sub launcherName() { - if (isGtk()) { + if (isGtk() || isWPE()) { return "MiniBrowser"; } elsif (isAppleMacWebKit()) { return "Safari"; } elsif (isAppleWinWebKit()) { return "MiniBrowser"; - } elsif (isWPE()) { - return "dyz"; } } diff --git a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_driver_wpe.py b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_driver_wpe.py index f025731b9abf9..3ce48b0b7a5ea 100644 --- a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_driver_wpe.py +++ b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_driver_wpe.py @@ -33,27 +33,21 @@ def binary_path(self): return self._port._build_path('bin', 'WPEWebDriver') def browser_name(self): - return 'dyz' + return 'MiniBrowser' + + def browser_path(self): + return self._port._build_path('bin', 'MiniBrowser') def browser_args(self): - return ['--automation'] + args = ['--automation'] + if self._port._display_server == 'headless': + args.append('--headless') + return args def capabilities(self): return {'wpe:browserOptions': { - 'binary': self.browser_name(), + 'binary': self.browser_path(), 'args': self.browser_args()}} - def browser_env(self): - env = {} - env['WEBKIT_EXEC_PATH'] = self._port._build_path('bin') - try: - ld_library_path = os.environ['LD_LIBRARY_PATH'] - except KeyError: - ld_library_path = None - env['LD_LIBRARY_PATH'] = self._port._build_path('lib') - if ld_library_path: - env['LD_LIBRARY_PATH'] += ':' + ld_library_path - return env - register_driver('wpe', WebDriverWPE) diff --git a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py index 4e58a3d252c37..48f11124a14fa 100644 --- a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py +++ b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py @@ -44,7 +44,7 @@ def __init__(self, port, driver, env, expectations): def collect_tests(self, tests): if self._driver.selenium_name() is None: - return 0 + return [] skipped = [os.path.join(self._tests_dir, test) for test in self._expectations.skipped_tests()] relative_tests_dir = os.path.join('imported', 'selenium', 'py', 'test') diff --git a/Tools/TestWebKitAPI/PlatformWPE.cmake b/Tools/TestWebKitAPI/PlatformWPE.cmake index 366255e8cbc1c..17c9f6f3ee626 100644 --- a/Tools/TestWebKitAPI/PlatformWPE.cmake +++ b/Tools/TestWebKitAPI/PlatformWPE.cmake @@ -17,7 +17,7 @@ include_directories( ${FORWARDING_HEADERS_DIR}/JavaScriptCore ${FORWARDING_HEADERS_DIR}/JavaScriptCore/glib ${DERIVED_SOURCES_JAVASCRIPCOREWPE_DIR} - ${TOOLS_DIR}/wpe/HeadlessViewBackend + ${TOOLS_DIR}/wpe/backends ) include_directories(SYSTEM @@ -79,7 +79,7 @@ set_target_properties(TestWebCore PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TESTWEBK # TestWebKit list(APPEND test_webkit_api_LIBRARIES - WPEHeadlessViewBackend + WPEToolingBackends ) add_executable(TestWebKit ${test_webkit_api_SOURCES}) diff --git a/Tools/TestWebKitAPI/PlatformWebView.h b/Tools/TestWebKitAPI/PlatformWebView.h index 6b39dc202877c..b957c5ad0840f 100644 --- a/Tools/TestWebKitAPI/PlatformWebView.h +++ b/Tools/TestWebKitAPI/PlatformWebView.h @@ -48,10 +48,12 @@ typedef NSWindow *PlatformWindow; typedef WKViewRef PlatformWKView; typedef GtkWidget *PlatformWindow; #elif PLATFORM(WPE) +namespace WPEToolingBackends { class HeadlessViewBackend; +} struct wpe_view_backend; typedef WKViewRef PlatformWKView; -typedef HeadlessViewBackend *PlatformWindow; +typedef WPEToolingBackends::HeadlessViewBackend *PlatformWindow; #elif PLATFORM(WIN) typedef WKViewRef PlatformWKView; typedef HWND PlatformWindow; diff --git a/Tools/TestWebKitAPI/glib/PlatformWPE.cmake b/Tools/TestWebKitAPI/glib/PlatformWPE.cmake index c102b1a5a37ee..f511cd2a684d3 100644 --- a/Tools/TestWebKitAPI/glib/PlatformWPE.cmake +++ b/Tools/TestWebKitAPI/glib/PlatformWPE.cmake @@ -10,7 +10,7 @@ list(APPEND WebKitGLibAPITests_INCLUDE_DIRECTORIES ${FORWARDING_HEADERS_WPE_DIR} ${FORWARDING_HEADERS_WPE_EXTENSION_DIR} ${FORWARDING_HEADERS_WPE_DOM_DIR} - ${TOOLS_DIR}/wpe/HeadlessViewBackend + ${TOOLS_DIR}/wpe/backends ) list(APPEND WebKitGLibAPITests_SYSTEM_INCLUDE_DIRECTORIES @@ -21,5 +21,5 @@ list(APPEND WebKitGLibAPITests_SYSTEM_INCLUDE_DIRECTORIES list(APPEND WebKitGLibAPITest_LIBRARIES ${WPE_LIBRARIES} ${WPEBACKEND_FDO_LIBRARIES} - WPEHeadlessViewBackend + WPEToolingBackends ) diff --git a/Tools/TestWebKitAPI/glib/WebKitGLib/TestMain.h b/Tools/TestWebKitAPI/glib/WebKitGLib/TestMain.h index 1d3da7f245813..13ba28e244df3 100644 --- a/Tools/TestWebKitAPI/glib/WebKitGLib/TestMain.h +++ b/Tools/TestWebKitAPI/glib/WebKitGLib/TestMain.h @@ -142,9 +142,9 @@ class Test { #if PLATFORM(WPE) static WebKitWebViewBackend* createWebViewBackend() { - auto* headlessBackend = new HeadlessViewBackend; + auto* headlessBackend = new WPEToolingBackends::HeadlessViewBackend(800, 600); return webkit_web_view_backend_new(headlessBackend->backend(), [](gpointer userData) { - delete static_cast(userData); + delete static_cast(userData); }, headlessBackend); } #endif diff --git a/Tools/TestWebKitAPI/wpe/PlatformWebViewWPE.cpp b/Tools/TestWebKitAPI/wpe/PlatformWebViewWPE.cpp index 7d8358e818270..8cc3b71a73218 100644 --- a/Tools/TestWebKitAPI/wpe/PlatformWebViewWPE.cpp +++ b/Tools/TestWebKitAPI/wpe/PlatformWebViewWPE.cpp @@ -66,7 +66,7 @@ PlatformWebView::~PlatformWebView() void PlatformWebView::initialize(WKPageConfigurationRef configuration) { - m_window = new HeadlessViewBackend; + m_window = new WPEToolingBackends::HeadlessViewBackend(800, 600); m_view = WKViewCreate(m_window->backend(), configuration); } diff --git a/Tools/WebKitTestRunner/PlatformWPE.cmake b/Tools/WebKitTestRunner/PlatformWPE.cmake index 89faeded44d66..0453ff6ea7216 100644 --- a/Tools/WebKitTestRunner/PlatformWPE.cmake +++ b/Tools/WebKitTestRunner/PlatformWPE.cmake @@ -19,7 +19,7 @@ list(APPEND WebKitTestRunner_SOURCES list(APPEND WebKitTestRunner_INCLUDE_DIRECTORIES ${WEBKIT_TESTRUNNER_DIR}/InjectedBundle/wpe ${FORWARDING_HEADERS_DIR} - ${TOOLS_DIR}/wpe/HeadlessViewBackend + ${TOOLS_DIR}/wpe/backends ) list(APPEND WebKitTestRunner_SYSTEM_INCLUDE_DIRECTORIES @@ -34,7 +34,7 @@ list(APPEND WebKitTestRunner_LIBRARIES ${GLIB_LIBRARIES} ${LIBXKBCOMMON_LIBRARIES} ${WPEBACKEND_FDO_LIBRARIES} - WPEHeadlessViewBackend + WPEToolingBackends ) set(WebKitTestRunnerInjectedBundle_LIBRARIES diff --git a/Tools/WebKitTestRunner/PlatformWebView.h b/Tools/WebKitTestRunner/PlatformWebView.h index 1cc8131a19a1b..6d0e9b0c7485f 100644 --- a/Tools/WebKitTestRunner/PlatformWebView.h +++ b/Tools/WebKitTestRunner/PlatformWebView.h @@ -51,9 +51,11 @@ typedef WKViewRef PlatformWKView; typedef GtkWidget* PlatformWindow; typedef cairo_surface_t *PlatformImage; #elif PLATFORM(WPE) +namespace WPEToolingBackends { class HeadlessViewBackend; +} typedef WKViewRef PlatformWKView; -typedef HeadlessViewBackend* PlatformWindow; +typedef WPEToolingBackends::HeadlessViewBackend* PlatformWindow; typedef cairo_surface_t* PlatformImage; #endif diff --git a/Tools/WebKitTestRunner/wpe/PlatformWebViewWPE.cpp b/Tools/WebKitTestRunner/wpe/PlatformWebViewWPE.cpp index fe582c73d5fc9..05c6568127334 100644 --- a/Tools/WebKitTestRunner/wpe/PlatformWebViewWPE.cpp +++ b/Tools/WebKitTestRunner/wpe/PlatformWebViewWPE.cpp @@ -39,7 +39,7 @@ PlatformWebView::PlatformWebView(WKPageConfigurationRef configuration, const Tes : m_windowIsKey(true) , m_options(options) { - m_window = new HeadlessViewBackend; + m_window = new WPEToolingBackends::HeadlessViewBackend(800, 600); m_view = WKViewCreate(m_window->backend(), configuration); } diff --git a/Tools/wpe/HeadlessViewBackend/CMakeLists.txt b/Tools/wpe/HeadlessViewBackend/CMakeLists.txt deleted file mode 100644 index 13dbd42299945..0000000000000 --- a/Tools/wpe/HeadlessViewBackend/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -find_package(WPEBackend-fdo REQUIRED) - -set(WPEHeadlessViewBackend_SOURCES - ${TOOLS_DIR}/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp -) - -set(WPEHeadlessViewBackend_SYSTEM_INCLUDE_DIRECTORIES - ${CAIRO_INCLUDE_DIRS} - ${GLIB_INCLUDE_DIRS} - ${WPE_INCLUDE_DIRS} - ${WPEBACKEND_FDO_INCLUDE_DIRS} -) - -set(WPEHeadlessViewBackend_LIBRARIES - ${CAIRO_LIBRARIES} - ${GLIB_LIBRARIES} - ${WPE_LIBRARIES} - ${WPEBACKEND_FDO_LIBRARIES} -) - -add_library(WPEHeadlessViewBackend ${WPEHeadlessViewBackend_SOURCES}) -include_directories(SYSTEM ${WPEHeadlessViewBackend_SYSTEM_INCLUDE_DIRECTORIES}) -target_link_libraries(WPEHeadlessViewBackend ${WPEHeadlessViewBackend_LIBRARIES}) diff --git a/Tools/wpe/backends/CMakeLists.txt b/Tools/wpe/backends/CMakeLists.txt new file mode 100644 index 0000000000000..f9e8b43b9fb9a --- /dev/null +++ b/Tools/wpe/backends/CMakeLists.txt @@ -0,0 +1,53 @@ +find_package(Libxkbcommon 0.4.0 REQUIRED) +find_package(Wayland REQUIRED) +find_package(WaylandProtocols 1.12 REQUIRED) +find_package(WPEBackend-fdo REQUIRED) + +set(DERIVED_SOURCES_WPETOOLINGBACKENDS_DIR "${CMAKE_BINARY_DIR}/DerivedSources/WPEToolingBackends") + +file(MAKE_DIRECTORY ${DERIVED_SOURCES_WPETOOLINGBACKENDS_DIR}) + +set(WPEToolingBackends_SOURCES + ${DERIVED_SOURCES_WPETOOLINGBACKENDS_DIR}/xdg-shell-unstable-v6-protocol.c + ${TOOLS_DIR}/wpe/backends/HeadlessViewBackend.cpp + ${TOOLS_DIR}/wpe/backends/ViewBackend.cpp + ${TOOLS_DIR}/wpe/backends/WindowViewBackend.cpp +) + +set(WPEToolingBackends_INCLUDE_DIRECTORIES + ${DERIVED_SOURCES_WPETOOLINGBACKENDS_DIR} +) + +set(WPEToolingBackends_SYSTEM_INCLUDE_DIRECTORIES + ${CAIRO_INCLUDE_DIRS} + ${GLIB_INCLUDE_DIRS} + ${WPE_INCLUDE_DIRS} + ${WPEBACKEND_FDO_INCLUDE_DIRS} +) + +set(WPEToolingBackends_LIBRARIES + ${CAIRO_LIBRARIES} + ${GLIB_LIBRARIES} + ${LIBXKBCOMMON_LIBRARIES} + ${WAYLAND_LIBRARIES} + ${WPE_LIBRARIES} + ${WPEBACKEND_FDO_LIBRARIES} +) + +add_custom_command( + OUTPUT ${DERIVED_SOURCES_WPETOOLINGBACKENDS_DIR}/xdg-shell-unstable-v6-protocol.c + MAIN_DEPENDENCY ${WAYLAND_PROTOCOLS_DATADIR}/unstable/xdg-shell/xdg-shell-unstable-v6.xml + DEPENDS ${DERIVED_SOURCES_WPETOOLINGBACKENDS_DIR}/xdg-shell-unstable-v6-client-protocol.h + COMMAND ${WAYLAND_SCANNER} code ${WAYLAND_PROTOCOLS_DATADIR}/unstable/xdg-shell/xdg-shell-unstable-v6.xml ${DERIVED_SOURCES_WPETOOLINGBACKENDS_DIR}/xdg-shell-unstable-v6-protocol.c + VERBATIM) + +add_custom_command( + OUTPUT ${DERIVED_SOURCES_WPETOOLINGBACKENDS_DIR}/xdg-shell-unstable-v6-client-protocol.h + MAIN_DEPENDENCY ${WAYLAND_PROTOCOLS_DATADIR}/unstable/xdg-shell/xdg-shell-unstable-v6.xml + COMMAND ${WAYLAND_SCANNER} client-header ${WAYLAND_PROTOCOLS_DATADIR}/unstable/xdg-shell/xdg-shell-unstable-v6.xml ${DERIVED_SOURCES_WPETOOLINGBACKENDS_DIR}/xdg-shell-unstable-v6-client-protocol.h + VERBATIM) + +add_library(WPEToolingBackends ${WPEToolingBackends_SOURCES}) +include_directories(${WPEToolingBackends_INCLUDE_DIRECTORIES}) +include_directories(SYSTEM ${WPEToolingBackends_SYSTEM_INCLUDE_DIRECTORIES}) +target_link_libraries(WPEToolingBackends ${WPEToolingBackends_LIBRARIES}) diff --git a/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp b/Tools/wpe/backends/HeadlessViewBackend.cpp similarity index 57% rename from Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp rename to Tools/wpe/backends/HeadlessViewBackend.cpp index 1a6b799572243..79953a433ebd7 100644 --- a/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp +++ b/Tools/wpe/backends/HeadlessViewBackend.cpp @@ -28,13 +28,27 @@ #include #include #include + +// This include order is necessary to enforce the GBM EGL platform. +#include +#include #include -// Manually provide the EGL_CAST C++ definition in case eglplatform.h doesn't provide it. -#ifndef EGL_CAST -#define EGL_CAST(type, value) (static_cast(value)) +#ifndef EGL_WL_bind_wayland_display +#define EGL_WL_bind_wayland_display 1 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, struct wl_resource* buffer, EGLint attribute, EGLint* value); + +#define EGL_WAYLAND_BUFFER_WL 0x31D5 // eglCreateImageKHR target +#define EGL_WAYLAND_PLANE_WL 0x31D6 // eglCreateImageKHR target #endif +namespace WPEToolingBackends { + +static PFNEGLCREATEIMAGEKHRPROC createImage; +static PFNEGLDESTROYIMAGEKHRPROC destroyImage; +static PFNEGLQUERYWAYLANDBUFFERWL queryBuffer; +static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC imageTargetTexture2DOES; + // Keep this in sync with wtf/glib/RunLoopSourcePriority.h. static int kRunLoopSourcePriorityDispatcher = -70; @@ -46,64 +60,32 @@ static EGLDisplay getEGLDisplay() if (display == EGL_NO_DISPLAY) return EGL_NO_DISPLAY; - if (!eglInitialize(display, nullptr, nullptr)) - return EGL_NO_DISPLAY; - - if (!eglBindAPI(EGL_OPENGL_ES_API)) - return EGL_NO_DISPLAY; - - wpe_fdo_initialize_for_egl_display(display); s_display = display; } return s_display; } -HeadlessViewBackend::HeadlessViewBackend() +HeadlessViewBackend::HeadlessViewBackend(uint32_t width, uint32_t height) + : ViewBackend(width, height) { - m_egl.display = getEGLDisplay(); - - static const EGLint configAttributes[13] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_ALPHA_SIZE, 1, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - EGLint numConfigs; - EGLBoolean ret = eglChooseConfig(m_egl.display, configAttributes, &m_egl.config, 1, &numConfigs); - if (!ret || !numConfigs) - return; - - static const EGLint contextAttributes[3] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - m_egl.context = eglCreateContext(m_egl.display, m_egl.config, EGL_NO_CONTEXT, contextAttributes); - if (m_egl.context == EGL_NO_CONTEXT) + m_eglDisplay = getEGLDisplay(); + if (!initialize()) return; - if (!eglMakeCurrent(m_egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_egl.context)) + if (!eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, m_eglContext)) return; - m_egl.createImage = reinterpret_cast(eglGetProcAddress("eglCreateImageKHR")); - m_egl.destroyImage = reinterpret_cast(eglGetProcAddress("eglDestroyImageKHR")); - m_egl.queryBuffer = reinterpret_cast(eglGetProcAddress("eglQueryWaylandBufferWL")); - m_egl.imageTargetTexture2DOES = reinterpret_cast(eglGetProcAddress("glEGLImageTargetTexture2DOES")); - - m_exportable = wpe_view_backend_exportable_fdo_create(&s_exportableClient, this, 800, 600); + createImage = reinterpret_cast(eglGetProcAddress("eglCreateImageKHR")); + destroyImage = reinterpret_cast(eglGetProcAddress("eglDestroyImageKHR")); + queryBuffer = reinterpret_cast(eglGetProcAddress("eglQueryWaylandBufferWL")); + imageTargetTexture2DOES = reinterpret_cast(eglGetProcAddress("glEGLImageTargetTexture2DOES")); m_updateSource = g_timeout_source_new(m_frameRate / 1000); - g_source_set_callback(m_updateSource, - [](gpointer data) -> gboolean { - auto& backend = *static_cast(data); - backend.performUpdate(); - return TRUE; - }, this, nullptr); + g_source_set_callback(m_updateSource, [](gpointer data) -> gboolean { + static_cast(data)->performUpdate(); + return TRUE; + }, this, nullptr); g_source_set_priority(m_updateSource, kRunLoopSourcePriorityDispatcher); g_source_attach(m_updateSource, g_main_context_default()); } @@ -116,23 +98,16 @@ HeadlessViewBackend::~HeadlessViewBackend() } if (auto image = std::get<0>(m_pendingImage.second)) - m_egl.destroyImage(m_egl.display, image); + destroyImage(m_eglDisplay, image); if (auto image = std::get<0>(m_lockedImage.second)) - m_egl.destroyImage(m_egl.display, image); - - if (m_egl.context) - eglDestroyContext(m_egl.display, m_egl.context); - - wpe_view_backend_exportable_fdo_destroy(m_exportable); -} - -struct wpe_view_backend* HeadlessViewBackend::backend() const -{ - return wpe_view_backend_exportable_fdo_get_view_backend(m_exportable); + destroyImage(m_eglDisplay, image); } cairo_surface_t* HeadlessViewBackend::createSnapshot() { + if (!m_eglContext) + return nullptr; + performUpdate(); EGLImageKHR image = std::get<0>(m_lockedImage.second); @@ -145,7 +120,7 @@ cairo_surface_t* HeadlessViewBackend::createSnapshot() uint8_t* buffer = new uint8_t[4 * width * height]; bool successfulSnapshot = false; - if (!eglMakeCurrent(m_egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_egl.context)) + if (!eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, m_eglContext)) return nullptr; GLuint imageTexture; @@ -157,7 +132,7 @@ cairo_surface_t* HeadlessViewBackend::createSnapshot() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, nullptr); - m_egl.imageTargetTexture2DOES(GL_TEXTURE_2D, image); + imageTargetTexture2DOES(GL_TEXTURE_2D, image); glBindTexture(GL_TEXTURE_2D, 0); GLuint imageFramebuffer; @@ -202,39 +177,30 @@ void HeadlessViewBackend::performUpdate() wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable); if (m_lockedImage.first) { wpe_view_backend_exportable_fdo_dispatch_release_buffer(m_exportable, m_lockedImage.first); - m_egl.destroyImage(m_egl.display, std::get<0>(m_lockedImage.second)); + destroyImage(m_eglDisplay, std::get<0>(m_lockedImage.second)); } m_lockedImage = m_pendingImage; m_pendingImage = std::pair> { }; } -struct wpe_view_backend_exportable_fdo_client HeadlessViewBackend::s_exportableClient = { - // export_buffer_resource - [](void* data, struct wl_resource* bufferResource) - { - auto& backend = *static_cast(data); - if (backend.m_pendingImage.first) - std::abort(); - - auto& egl = backend.m_egl; - - EGLint format = 0; - if (!egl.queryBuffer(egl.display, bufferResource, EGL_TEXTURE_FORMAT, &format) || format != EGL_TEXTURE_RGBA) - return; - - EGLint width, height; - if (!egl.queryBuffer(egl.display, bufferResource, EGL_WIDTH, &width) - || !egl.queryBuffer(egl.display, bufferResource, EGL_HEIGHT, &height)) - return; - - EGLint attributes[] = { EGL_WAYLAND_PLANE_WL, 0, EGL_NONE }; - EGLImageKHR image = egl.createImage(egl.display, EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, bufferResource, attributes); - backend.m_pendingImage = { bufferResource, std::make_tuple(image, width, height) }; - }, - // padding - nullptr, - nullptr, - nullptr, - nullptr -}; +void HeadlessViewBackend::displayBuffer(struct wl_resource* bufferResource) +{ + if (m_pendingImage.first) + std::abort(); + + EGLint format = 0; + if (!queryBuffer(m_eglDisplay, bufferResource, EGL_TEXTURE_FORMAT, &format) || format != EGL_TEXTURE_RGBA) + return; + + EGLint width, height; + if (!queryBuffer(m_eglDisplay, bufferResource, EGL_WIDTH, &width) + || !queryBuffer(m_eglDisplay, bufferResource, EGL_HEIGHT, &height)) + return; + + EGLint attributes[] = { EGL_WAYLAND_PLANE_WL, 0, EGL_NONE }; + EGLImageKHR image = createImage(m_eglDisplay, EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, bufferResource, attributes); + m_pendingImage = { bufferResource, std::make_tuple(image, width, height) }; +} + +} // namespace WPEToolingBackends diff --git a/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.h b/Tools/wpe/backends/HeadlessViewBackend.h similarity index 61% rename from Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.h rename to Tools/wpe/backends/HeadlessViewBackend.h index e142e69d83452..178424e978e52 100644 --- a/Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.h +++ b/Tools/wpe/backends/HeadlessViewBackend.h @@ -25,53 +25,32 @@ #pragma once -// This include order is necessary to enforce the GBM EGL platform. -#include -#include - +#include "ViewBackend.h" #include #include #include -#include -#ifndef EGL_WL_bind_wayland_display -#define EGL_WL_bind_wayland_display 1 -typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); +typedef void* EGLImageKHR; -#define EGL_WAYLAND_BUFFER_WL 0x31D5 /* eglCreateImageKHR target */ -#define EGL_WAYLAND_PLANE_WL 0x31D6 /* eglCreateImageKHR target */ -#endif +namespace WPEToolingBackends { -class HeadlessViewBackend { +class HeadlessViewBackend final : public ViewBackend { public: - HeadlessViewBackend(); + HeadlessViewBackend(uint32_t width, uint32_t height); ~HeadlessViewBackend(); - struct wpe_view_backend* backend() const; - cairo_surface_t* createSnapshot(); private: - void performUpdate(); - - static struct wpe_view_backend_exportable_fdo_client s_exportableClient; + void displayBuffer(struct wl_resource*) override; - struct { - EGLDisplay display; - EGLConfig config; - EGLContext context { nullptr }; - - PFNEGLCREATEIMAGEKHRPROC createImage; - PFNEGLDESTROYIMAGEKHRPROC destroyImage; - PFNEGLQUERYWAYLANDBUFFERWL queryBuffer; - PFNGLEGLIMAGETARGETTEXTURE2DOESPROC imageTargetTexture2DOES; - } m_egl; - - struct wpe_view_backend_exportable_fdo* m_exportable; + void performUpdate(); std::pair> m_pendingImage { }; std::pair> m_lockedImage { }; - GSource* m_updateSource; + GSource* m_updateSource { nullptr }; gint64 m_frameRate { G_USEC_PER_SEC / 60 }; }; + +} // namespace WPEToolingBackends diff --git a/Tools/wpe/backends/ViewBackend.cpp b/Tools/wpe/backends/ViewBackend.cpp new file mode 100644 index 0000000000000..3d0ac69c3a211 --- /dev/null +++ b/Tools/wpe/backends/ViewBackend.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2018 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ViewBackend.h" + +#include +#include +#include + +namespace WPEToolingBackends { + +ViewBackend::ViewBackend(uint32_t width, uint32_t height) + : m_width(width) + , m_height(height) +{ +} + +ViewBackend::~ViewBackend() +{ + if (m_exportable) + wpe_view_backend_exportable_fdo_destroy(m_exportable); + + if (m_eglContext) + eglDestroyContext(m_eglDisplay, m_eglContext); +} + +bool ViewBackend::initialize() +{ + if (m_eglDisplay == EGL_NO_DISPLAY) + return false; + + eglInitialize(m_eglDisplay, nullptr, nullptr); + + if (!eglBindAPI(EGL_OPENGL_ES_API)) + return false; + + wpe_fdo_initialize_for_egl_display(m_eglDisplay); + + static const EGLint configAttributes[13] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_ALPHA_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + { + EGLint count = 0; + if (!eglGetConfigs(m_eglDisplay, nullptr, 0, &count) || count < 1) + return false; + + EGLConfig* configs = g_new0(EGLConfig, count); + EGLint matched = 0; + if (eglChooseConfig(m_eglDisplay, configAttributes, configs, count, &matched) && !!matched) + m_eglConfig = configs[0]; + g_free(configs); + } + + static const EGLint contextAttributes[3] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes); + if (!m_eglContext) + return false; + + static struct wpe_view_backend_exportable_fdo_client exportableClient = { + // export_buffer_resource + [](void* data, struct wl_resource* bufferResource) + { + static_cast(data)->displayBuffer(bufferResource); + }, + // padding + nullptr, nullptr, nullptr, nullptr + }; + m_exportable = wpe_view_backend_exportable_fdo_create(&exportableClient, this, m_width, m_height); + + return true; +} + +void ViewBackend::setInputClient(std::unique_ptr&& client) +{ + m_inputClient = std::move(client); +} + +struct wpe_view_backend* ViewBackend::backend() const +{ + return m_exportable ? wpe_view_backend_exportable_fdo_get_view_backend(m_exportable) : nullptr; +} + +void ViewBackend::dispatchInputPointerEvent(struct wpe_input_pointer_event* event) +{ + if (m_inputClient && m_inputClient->dispatchPointerEvent(event)) + return; + wpe_view_backend_dispatch_pointer_event(backend(), event); +} + +void ViewBackend::dispatchInputAxisEvent(struct wpe_input_axis_event* event) +{ + if (m_inputClient && m_inputClient->dispatchAxisEvent(event)) + return; + wpe_view_backend_dispatch_axis_event(backend(), event); +} + +void ViewBackend::dispatchInputKeyboardEvent(struct wpe_input_keyboard_event* event) +{ + if (m_inputClient && m_inputClient->dispatchKeyboardEvent(event)) + return; + wpe_view_backend_dispatch_keyboard_event(backend(), event); +} + +} // namespace WPEToolingBackends diff --git a/Tools/wpe/backends/ViewBackend.h b/Tools/wpe/backends/ViewBackend.h new file mode 100644 index 0000000000000..e6cb2687ff367 --- /dev/null +++ b/Tools/wpe/backends/ViewBackend.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +typedef void* EGLConfig; +typedef void* EGLContext; +typedef void* EGLDisplay; + +// Manually provide the EGL_CAST C++ definition in case eglplatform.h doesn't provide it. +#ifndef EGL_CAST +#define EGL_CAST(type, value) (static_cast(value)) +#endif + +namespace WPEToolingBackends { + +class ViewBackend { +public: + virtual ~ViewBackend(); + + class InputClient { + public: + virtual ~InputClient() = default; + + virtual bool dispatchPointerEvent(struct wpe_input_pointer_event*) { return false; } + virtual bool dispatchAxisEvent(struct wpe_input_axis_event*) { return false; } + virtual bool dispatchKeyboardEvent(struct wpe_input_keyboard_event*) { return false; } + }; + void setInputClient(std::unique_ptr&&); + + struct wpe_view_backend* backend() const; + +protected: + ViewBackend(uint32_t width, uint32_t height); + + bool initialize(); + + void dispatchInputPointerEvent(struct wpe_input_pointer_event*); + void dispatchInputAxisEvent(struct wpe_input_axis_event*); + void dispatchInputKeyboardEvent(struct wpe_input_keyboard_event*); + + virtual void displayBuffer(struct wl_resource*) = 0; + + uint32_t m_width { 0 }; + uint32_t m_height { 0 }; + EGLDisplay m_eglDisplay { nullptr }; + EGLContext m_eglContext { nullptr }; + EGLConfig m_eglConfig; + struct wpe_view_backend_exportable_fdo* m_exportable { nullptr }; + std::unique_ptr m_inputClient; +}; + +} // namespace WPEToolingBackends diff --git a/Tools/wpe/backends/WindowViewBackend.cpp b/Tools/wpe/backends/WindowViewBackend.cpp new file mode 100644 index 0000000000000..8134a1b306f74 --- /dev/null +++ b/Tools/wpe/backends/WindowViewBackend.cpp @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2018 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "WindowViewBackend.h" + +#include +#include +#include +#include +#include +#include + +// This include order is necessary to enforce the Wayland EGL platform. +#include +#include + +#ifndef EGL_WL_bind_wayland_display +#define EGL_WL_bind_wayland_display 1 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, struct wl_resource* buffer, EGLint attribute, EGLint* value); + +#define EGL_WAYLAND_BUFFER_WL 0x31D5 // eglCreateImageKHR target +#define EGL_WAYLAND_PLANE_WL 0x31D6 // eglCreateImageKHR target +#endif + +namespace WPEToolingBackends { + +static PFNEGLCREATEIMAGEKHRPROC createImage; +static PFNEGLDESTROYIMAGEKHRPROC destroyImage; +static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC imageTargetTexture2DOES; + +struct EventSource { + static GSourceFuncs sourceFuncs; + + GSource source; + GPollFD pfd; + struct wl_display* display; +}; + +GSourceFuncs EventSource::sourceFuncs = { + // prepare + [](GSource* base, gint* timeout) -> gboolean + { + auto* source = reinterpret_cast(base); + struct wl_display* display = source->display; + + *timeout = -1; + + wl_display_dispatch_pending(display); + wl_display_flush(display); + + return FALSE; + }, + // check + [](GSource* base) -> gboolean + { + auto* source = reinterpret_cast(base); + return !!source->pfd.revents; + }, + // dispatch + [](GSource* base, GSourceFunc, gpointer) -> gboolean + { + auto* source = reinterpret_cast(base); + struct wl_display* display = source->display; + + if (source->pfd.revents & G_IO_IN) + wl_display_dispatch(display); + + if (source->pfd.revents & (G_IO_ERR | G_IO_HUP)) + return FALSE; + + source->pfd.revents = 0; + return TRUE; + }, + nullptr, // finalize + nullptr, // closure_callback + nullptr, // closure_marshall +}; + +const struct wl_registry_listener WindowViewBackend::s_registryListener = { + // global + [](void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t) + { + auto* window = static_cast(data); + + if (!std::strcmp(interface, "wl_compositor")) + window->m_compositor = static_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 1)); + + if (!std::strcmp(interface, "zxdg_shell_v6")) + window->m_xdg = static_cast(wl_registry_bind(registry, name, &zxdg_shell_v6_interface, 1)); + + if (!std::strcmp(interface, "wl_seat")) + window->m_seat = static_cast(wl_registry_bind(registry, name, &wl_seat_interface, 4)); + }, + // global_remove + [](void*, struct wl_registry*, uint32_t) { }, +}; + +const struct zxdg_shell_v6_listener WindowViewBackend::s_xdgWmBaseListener = { + // ping + [](void*, struct zxdg_shell_v6* shell, uint32_t serial) + { + zxdg_shell_v6_pong(shell, serial); + }, +}; + +const struct wl_pointer_listener WindowViewBackend::s_pointerListener = { + // enter + [](void* data, struct wl_pointer*, uint32_t /*serial*/, struct wl_surface* surface, wl_fixed_t, wl_fixed_t) + { + auto& window = *static_cast(data); + if (window.m_surface == surface) + window.m_seatData.pointer.target = surface; + }, + // leave + [](void* data, struct wl_pointer*, uint32_t /*serial*/, struct wl_surface* surface) + { + auto& window = *static_cast(data); + if (window.m_surface == surface && window.m_seatData.pointer.target == surface) + window.m_seatData.pointer.target = nullptr; + }, + // motion + [](void* data, struct wl_pointer*, uint32_t time, wl_fixed_t fixedX, wl_fixed_t fixedY) + { + auto& window = *static_cast(data); + int x = wl_fixed_to_int(fixedX); + int y = wl_fixed_to_int(fixedY); + window.m_seatData.pointer.coords = { x, y }; + + if (window.m_seatData.pointer.target) { + struct wpe_input_pointer_event event = { wpe_input_pointer_event_type_motion, + time, x, y, window.m_seatData.pointer.button, window.m_seatData.pointer.state }; + window.dispatchInputPointerEvent(&event); + } + }, + // button + [](void* data, struct wl_pointer*, uint32_t /*serial*/, uint32_t time, uint32_t button, uint32_t state) + { + auto& window = *static_cast(data); + if (button >= BTN_MOUSE) + button = button - BTN_MOUSE + 1; + else + button = 0; + + window.m_seatData.pointer.button = !!state ? button : 0; + window.m_seatData.pointer.state = state; + + if (window.m_seatData.pointer.target) { + struct wpe_input_pointer_event event = { wpe_input_pointer_event_type_button, + time, window.m_seatData.pointer.coords.first, window.m_seatData.pointer.coords.second, button, state }; + window.dispatchInputPointerEvent(&event); + } + }, + // axis + [](void* data, struct wl_pointer*, uint32_t time, uint32_t axis, wl_fixed_t value) + { + auto& window = *static_cast(data); + if (window.m_seatData.pointer.target) { + struct wpe_input_axis_event event = { wpe_input_axis_event_type_motion, + time, window.m_seatData.pointer.coords.first, window.m_seatData.pointer.coords.second, axis, -wl_fixed_to_int(value) }; + window.dispatchInputAxisEvent(&event); + } + }, +}; + +const struct wl_keyboard_listener WindowViewBackend::s_keyboardListener = { + // keymap + [](void* data, struct wl_keyboard*, uint32_t format, int fd, uint32_t size) + { + auto& window = *static_cast(data); + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + return; + } + + void* mapping = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0); + if (mapping == MAP_FAILED) { + close(fd); + return; + } + + auto& xkb = window.m_seatData.xkb; + xkb.keymap = xkb_keymap_new_from_string(xkb.context, static_cast(mapping), + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(mapping, size); + close(fd); + + if (!xkb.keymap) + return; + + xkb.state = xkb_state_new(xkb.keymap); + if (!xkb.state) + return; + + xkb.indexes.control = xkb_keymap_mod_get_index(xkb.keymap, XKB_MOD_NAME_CTRL); + xkb.indexes.alt = xkb_keymap_mod_get_index(xkb.keymap, XKB_MOD_NAME_ALT); + xkb.indexes.shift = xkb_keymap_mod_get_index(xkb.keymap, XKB_MOD_NAME_SHIFT); + }, + // enter + [](void* data, struct wl_keyboard*, uint32_t /*serial*/, struct wl_surface* surface, struct wl_array*) + { + auto& window = *static_cast(data); + if (window.m_surface == surface) + window.m_seatData.keyboard.target = surface; + }, + // leave + [](void* data, struct wl_keyboard*, uint32_t /*serial*/, struct wl_surface* surface) + { + auto& window = *static_cast(data); + if (window.m_surface == surface && window.m_seatData.keyboard.target == surface) + window.m_seatData.keyboard.target = nullptr; + }, + // key + [](void* data, struct wl_keyboard*, uint32_t /*serial*/, uint32_t time, uint32_t key, uint32_t state) + { + auto& window = *static_cast(data); + + // IDK. + key += 8; + + window.handleKeyEvent(key, state, time); + + auto& seatData = window.m_seatData; + if (!seatData.repeatInfo.rate) + return; + + if (state == WL_KEYBOARD_KEY_STATE_RELEASED + && seatData.repeatData.key == key) { + if (seatData.repeatData.eventSource) + g_source_remove(seatData.repeatData.eventSource); + seatData.repeatData = { 0, 0, 0, 0 }; + } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED + && xkb_keymap_key_repeats(seatData.xkb.keymap, key)) { + + if (seatData.repeatData.eventSource) + g_source_remove(seatData.repeatData.eventSource); + + auto sourceID = g_timeout_add(seatData.repeatInfo.delay, [](void* data) -> gboolean { + auto& window = *static_cast(data); + auto& seatData = window.m_seatData; + window.handleKeyEvent(seatData.repeatData.key, seatData.repeatData.state, seatData.repeatData.time); + seatData.repeatData.eventSource = g_timeout_add(seatData.repeatInfo.rate, [](void* data) -> gboolean { + auto& window = *static_cast(data); + auto& seatData = window.m_seatData; + window.handleKeyEvent(seatData.repeatData.key, seatData.repeatData.state, seatData.repeatData.time); + return G_SOURCE_CONTINUE; + }, data); + return G_SOURCE_REMOVE; + }, data); + seatData.repeatData = { key, time, state, sourceID }; + } + }, + // modifiers + [](void* data, struct wl_keyboard*, uint32_t /*serial*/, uint32_t depressedMods, uint32_t latchedMods, uint32_t lockedMods, uint32_t group) + { + auto& xkb = static_cast(data)->m_seatData.xkb; + + xkb_state_update_mask(xkb.state, depressedMods, latchedMods, lockedMods, 0, 0, group); + + auto& modifiers = xkb.modifiers; + modifiers = 0; + auto component = static_cast(XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); + if (xkb_state_mod_index_is_active(xkb.state, xkb.indexes.control, component)) + modifiers |= wpe_input_keyboard_modifier_control; + if (xkb_state_mod_index_is_active(xkb.state, xkb.indexes.alt, component)) + modifiers |= wpe_input_keyboard_modifier_alt; + if (xkb_state_mod_index_is_active(xkb.state, xkb.indexes.shift, component)) + modifiers |= wpe_input_keyboard_modifier_shift; + }, + // repeat_info + [](void* data, struct wl_keyboard*, int32_t rate, int32_t delay) + { + auto& seatData = static_cast(data)->m_seatData; + + auto& repeatInfo = seatData.repeatInfo; + repeatInfo = { rate, delay }; + + // A rate of zero disables any repeating. + if (!rate) { + auto& repeatData = seatData.repeatData; + if (repeatData.eventSource) { + g_source_remove(repeatData.eventSource); + repeatData = { 0, 0, 0, 0 }; + } + } + }, +}; + +const struct wl_seat_listener WindowViewBackend::s_seatListener = { + // capabilities + [](void* data, struct wl_seat* seat, uint32_t capabilities) + { + auto* window = static_cast(data); + auto& seatData = window->m_seatData; + + // WL_SEAT_CAPABILITY_POINTER + const bool hasPointerCap = capabilities & WL_SEAT_CAPABILITY_POINTER; + if (hasPointerCap && !seatData.pointer.object) { + seatData.pointer.object = wl_seat_get_pointer(seat); + wl_pointer_add_listener(seatData.pointer.object, &s_pointerListener, window); + } + if (!hasPointerCap && seatData.pointer.object) { + wl_pointer_destroy(seatData.pointer.object); + seatData.pointer.object = nullptr; + } + + // WL_SEAT_CAPABILITY_KEYBOARD + const bool hasKeyboardCap = capabilities & WL_SEAT_CAPABILITY_KEYBOARD; + if (hasKeyboardCap && !seatData.keyboard.object) { + seatData.keyboard.object = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(seatData.keyboard.object, &s_keyboardListener, window); + } + if (!hasKeyboardCap && seatData.keyboard.object) { + wl_keyboard_destroy(seatData.keyboard.object); + seatData.keyboard.object = nullptr; + } + }, + // name + [](void*, struct wl_seat*, const char*) { } +}; + +const struct zxdg_surface_v6_listener WindowViewBackend::s_xdgSurfaceListener = { + // configure + [](void*, struct zxdg_surface_v6* surface, uint32_t serial) + { + zxdg_surface_v6_ack_configure(surface, serial); + }, +}; + +const struct zxdg_toplevel_v6_listener WindowViewBackend::s_xdgToplevelListener = { + // configure + [](void*, struct zxdg_toplevel_v6*, int32_t /*width*/, int32_t /*height*/, struct wl_array*) + { + // FIXME: dispatch the size against wpe_view_backend. + }, + // close + [](void*, struct zxdg_toplevel_v6*) { }, +}; + +WindowViewBackend::WindowViewBackend(uint32_t width, uint32_t height) + : ViewBackend(width, height) +{ + m_display = wl_display_connect(nullptr); + if (!m_display) + return; + + m_eglDisplay = eglGetDisplay(m_display); + if (!initialize()) + return; + + { + auto* registry = wl_display_get_registry(m_display); + wl_registry_add_listener(registry, &s_registryListener, this); + wl_display_roundtrip(m_display); + + if (m_xdg) + zxdg_shell_v6_add_listener(m_xdg, &s_xdgWmBaseListener, nullptr); + + if (m_seat) + wl_seat_add_listener(m_seat, &s_seatListener, this); + + m_seatData.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + m_seatData.xkb.composeTable = xkb_compose_table_new_from_locale(m_seatData.xkb.context, setlocale(LC_CTYPE, nullptr), XKB_COMPOSE_COMPILE_NO_FLAGS); + if (m_seatData.xkb.composeTable) + m_seatData.xkb.composeState = xkb_compose_state_new(m_seatData.xkb.composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + } + + m_eventSource = g_source_new(&EventSource::sourceFuncs, sizeof(EventSource)); + { + auto& source = *reinterpret_cast(m_eventSource); + source.display = m_display; + + source.pfd.fd = wl_display_get_fd(m_display); + source.pfd.events = G_IO_IN | G_IO_ERR | G_IO_HUP; + source.pfd.revents = 0; + g_source_add_poll(&source.source, &source.pfd); + + g_source_set_priority(&source.source, G_PRIORITY_HIGH + 30); + g_source_set_can_recurse(&source.source, TRUE); + g_source_attach(&source.source, g_main_context_get_thread_default()); + } + + m_surface = wl_compositor_create_surface(m_compositor); + if (m_xdg) { + m_xdgSurface = zxdg_shell_v6_get_xdg_surface(m_xdg, m_surface); + zxdg_surface_v6_add_listener(m_xdgSurface, &s_xdgSurfaceListener, nullptr); + m_xdgToplevel = zxdg_surface_v6_get_toplevel(m_xdgSurface); + if (m_xdgToplevel) { + zxdg_toplevel_v6_add_listener(m_xdgToplevel, &s_xdgToplevelListener, nullptr); + zxdg_toplevel_v6_set_title(m_xdgToplevel, "WPE"); + wl_surface_commit(m_surface); + } + } + + m_eglWindow = wl_egl_window_create(m_surface, m_width, m_height); + + auto createPlatformWindowSurface = + reinterpret_cast(eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT")); + m_eglSurface = createPlatformWindowSurface(m_eglDisplay, m_eglConfig, m_eglWindow, nullptr); + if (!m_eglSurface) + return; + + if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) + return; + + createImage = reinterpret_cast(eglGetProcAddress("eglCreateImageKHR")); + destroyImage = reinterpret_cast(eglGetProcAddress("eglDestroyImageKHR")); + imageTargetTexture2DOES = reinterpret_cast(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + + { + static const char* vertexShaderSource = + "attribute vec2 pos;\n" + "attribute vec2 texture;\n" + "varying vec2 v_texture;\n" + "void main() {\n" + " v_texture = texture;\n" + " gl_Position = vec4(pos, 0, 1);\n" + "}\n"; + static const char* fragmentShaderSource = + "precision mediump float;\n" + "uniform sampler2D u_texture;\n" + "varying vec2 v_texture;\n" + "void main() {\n" + " gl_FragColor = texture2D(u_texture, v_texture);\n" + "}\n"; + + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); + glCompileShader(vertexShader); + + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); + glCompileShader(fragmentShader); + + m_program = glCreateProgram(); + glAttachShader(m_program, vertexShader); + glAttachShader(m_program, fragmentShader); + glLinkProgram(m_program); + + glBindAttribLocation(m_program, 0, "pos"); + glBindAttribLocation(m_program, 1, "texture"); + m_textureUniform = glGetUniformLocation(m_program, "u_texture"); + } + { + glGenTextures(1, &m_viewTexture); + glBindTexture(GL_TEXTURE_2D, m_viewTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glBindTexture(GL_TEXTURE_2D, 0); + } +} + +WindowViewBackend::~WindowViewBackend() +{ + if (m_eventSource) { + g_source_destroy(m_eventSource); + g_source_unref(m_eventSource); + } + + if (m_xdgToplevel) + zxdg_toplevel_v6_destroy(m_xdgToplevel); + + if (m_xdgSurface) + zxdg_surface_v6_destroy(m_xdgSurface); + + if (m_surface) + wl_surface_destroy(m_surface); + + if (m_eglWindow) + wl_egl_window_destroy(m_eglWindow); + + if (m_xdg) + zxdg_shell_v6_destroy(m_xdg); + + if (m_seat) + wl_seat_destroy(m_seat); + + if (m_compositor) + wl_compositor_destroy(m_compositor); + + if (m_committed.image) + destroyImage(m_eglDisplay, m_committed.image); + + if (m_eglSurface) + eglDestroySurface(m_eglDisplay, m_eglSurface); + + if (m_display) + wl_display_disconnect(m_display); +} + +const struct wl_callback_listener WindowViewBackend::s_frameListener = { + // frame + [](void* data, struct wl_callback* callback, uint32_t) + { + if (callback) + wl_callback_destroy(callback); + + auto& window = *static_cast(data); + wpe_view_backend_exportable_fdo_dispatch_frame_complete(window.m_exportable); + + if (window.m_committed.image) + destroyImage(window.m_eglDisplay, window.m_committed.image); + if (window.m_committed.bufferResource) + wpe_view_backend_exportable_fdo_dispatch_release_buffer(window.m_exportable, window.m_committed.bufferResource); + window.m_committed = { nullptr, nullptr }; + } +}; + +void WindowViewBackend::displayBuffer(struct wl_resource* bufferResource) +{ + if (!m_eglContext) + return; + + eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext); + + glClearColor(1, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(m_program); + + { + static EGLint imageAttributes[] = { + EGL_WAYLAND_PLANE_WL, 0, + EGL_NONE + }; + EGLImageKHR image = createImage(m_eglDisplay, EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, bufferResource, imageAttributes); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_viewTexture); + imageTargetTexture2DOES(GL_TEXTURE_2D, image); + glUniform1i(m_textureUniform, 0); + + m_committed = { bufferResource, image }; + } + + static const GLfloat vertices[4][2] = { + { -1.0, 1.0 }, + { 1.0, 1.0 }, + { -1.0, -1.0 }, + { 1.0, -1.0 }, + }; + + static const GLfloat texturePos[4][2] = { + { 0, 0 }, + { 1, 0 }, + { 0, 1 }, + { 1, 1 }, + }; + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texturePos); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + + struct wl_callback* callback = wl_surface_frame(m_surface); + wl_callback_add_listener(callback, &s_frameListener, this); + + eglSwapBuffers(m_eglDisplay, m_eglSurface); +} + +void WindowViewBackend::handleKeyEvent(uint32_t key, uint32_t state, uint32_t time) +{ + auto& xkb = m_seatData.xkb; + uint32_t keysym = xkb_state_key_get_one_sym(xkb.state, key); + uint32_t unicode = xkb_state_key_get_utf32(xkb.state, key); + + if (xkb.composeState + && state == WL_KEYBOARD_KEY_STATE_PRESSED + && xkb_compose_state_feed(xkb.composeState, keysym) == XKB_COMPOSE_FEED_ACCEPTED + && xkb_compose_state_get_status(xkb.composeState) == XKB_COMPOSE_COMPOSED) { + keysym = xkb_compose_state_get_one_sym(xkb.composeState); + unicode = xkb_keysym_to_utf32(keysym); + } + + if (m_seatData.keyboard.target) { + struct wpe_input_keyboard_event event = { time, keysym, unicode, !!state, xkb.modifiers }; + dispatchInputKeyboardEvent(&event); + } +} + +} // namespace WPEToolingBackends diff --git a/Tools/wpe/backends/WindowViewBackend.h b/Tools/wpe/backends/WindowViewBackend.h new file mode 100644 index 0000000000000..044b5b7ddd64b --- /dev/null +++ b/Tools/wpe/backends/WindowViewBackend.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ViewBackend.h" +#include "xdg-shell-unstable-v6-client-protocol.h" +#include +#include +#include +#include + +typedef void* EGLImageKHR; +typedef void* EGLSurface; +typedef struct wl_egl_window *EGLNativeWindowType; + +namespace WPEToolingBackends { + +class WindowViewBackend final : public ViewBackend { +public: + WindowViewBackend(uint32_t width, uint32_t height); + ~WindowViewBackend(); + +private: + void displayBuffer(struct wl_resource*) override; + + static const struct wl_registry_listener s_registryListener; + static const struct zxdg_shell_v6_listener s_xdgWmBaseListener; + static const struct wl_pointer_listener s_pointerListener; + static const struct wl_keyboard_listener s_keyboardListener; + static const struct wl_seat_listener s_seatListener; + static const struct wl_callback_listener s_frameListener; + static const struct zxdg_surface_v6_listener s_xdgSurfaceListener; + static const struct zxdg_toplevel_v6_listener s_xdgToplevelListener; + + void handleKeyEvent(uint32_t key, uint32_t state, uint32_t time); + + struct SeatData { + struct { + struct wl_pointer* object { nullptr }; + struct wl_surface* target { nullptr }; + std::pair coords { 0, 0 }; + uint32_t button { 0 }; + uint32_t state { 0 }; + } pointer; + + struct { + struct wl_keyboard* object { nullptr }; + struct wl_surface* target { nullptr }; + } keyboard; + + struct { + struct xkb_context* context { nullptr }; + struct xkb_keymap* keymap { nullptr }; + struct xkb_state* state { nullptr }; + struct { + xkb_mod_index_t control { 0 }; + xkb_mod_index_t alt { 0 }; + xkb_mod_index_t shift { 0 }; + } indexes; + uint8_t modifiers { 0 }; + struct xkb_compose_table* composeTable { nullptr }; + struct xkb_compose_state* composeState { nullptr }; + } xkb; + + struct { + int32_t rate { 0 }; + int32_t delay { 0 }; + } repeatInfo; + + struct { + uint32_t key { 0 }; + uint32_t time { 0 }; + uint32_t state { 0 }; + uint32_t eventSource { 0 }; + } repeatData; + + } m_seatData; + + struct wl_display* m_display { nullptr }; + struct wl_compositor* m_compositor { nullptr }; + struct zxdg_shell_v6* m_xdg { nullptr }; + struct wl_seat* m_seat { nullptr }; + GSource* m_eventSource { nullptr }; + struct wl_surface* m_surface { nullptr }; + struct zxdg_surface_v6* m_xdgSurface { nullptr }; + struct zxdg_toplevel_v6* m_xdgToplevel { nullptr }; + struct wl_egl_window* m_eglWindow { nullptr }; + EGLSurface m_eglSurface { nullptr }; + unsigned m_program { 0 }; + unsigned m_textureUniform { 0 }; + unsigned m_viewTexture { 0 }; + struct { + struct wl_resource* bufferResource { nullptr }; + EGLImageKHR image; + } m_committed; +}; + +} // WPEToolingBackends diff --git a/Tools/wpe/jhbuild.modules b/Tools/wpe/jhbuild.modules index 85d91455d4762..f08621cac02d5 100644 --- a/Tools/wpe/jhbuild.modules +++ b/Tools/wpe/jhbuild.modules @@ -23,7 +23,7 @@ - + @@ -36,6 +36,8 @@ href="http://cairographics.org"/> + - - + +