[WPE] Add a MiniBrowser and use it to run WebDriver tests
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Jun 2018 06:43:14 +0000 (06:43 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Jun 2018 06:43:14 +0000 (06:43 +0000)
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:

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: https://svn.webkit.org/repository/webkit/trunk@232670 268f45cc-cd09-0410-ab3c-d52691b4dbfc

31 files changed:
ChangeLog
Source/WebDriver/ChangeLog
Source/WebDriver/wpe/WebDriverServiceWPE.cpp
Source/cmake/FindWaylandProtocols.cmake [new file with mode: 0644]
Source/cmake/OptionsWPE.cmake
Tools/CMakeLists.txt
Tools/ChangeLog
Tools/MiniBrowser/wpe/CMakeLists.txt [new file with mode: 0644]
Tools/MiniBrowser/wpe/main.cpp [new file with mode: 0644]
Tools/Scripts/run-minibrowser
Tools/Scripts/run-webdriver-tests
Tools/Scripts/webkitdirs.pm
Tools/Scripts/webkitpy/webdriver_tests/webdriver_driver_wpe.py
Tools/Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py
Tools/TestWebKitAPI/PlatformWPE.cmake
Tools/TestWebKitAPI/PlatformWebView.h
Tools/TestWebKitAPI/glib/PlatformWPE.cmake
Tools/TestWebKitAPI/glib/WebKitGLib/TestMain.h
Tools/TestWebKitAPI/wpe/PlatformWebViewWPE.cpp
Tools/WebKitTestRunner/PlatformWPE.cmake
Tools/WebKitTestRunner/PlatformWebView.h
Tools/WebKitTestRunner/wpe/PlatformWebViewWPE.cpp
Tools/wpe/HeadlessViewBackend/CMakeLists.txt [deleted file]
Tools/wpe/backends/CMakeLists.txt [new file with mode: 0644]
Tools/wpe/backends/HeadlessViewBackend.cpp [moved from Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.cpp with 57% similarity]
Tools/wpe/backends/HeadlessViewBackend.h [moved from Tools/wpe/HeadlessViewBackend/HeadlessViewBackend.h with 61% similarity]
Tools/wpe/backends/ViewBackend.cpp [new file with mode: 0644]
Tools/wpe/backends/ViewBackend.h [new file with mode: 0644]
Tools/wpe/backends/WindowViewBackend.cpp [new file with mode: 0644]
Tools/wpe/backends/WindowViewBackend.h [new file with mode: 0644]
Tools/wpe/jhbuild.modules

index 4c8eae7..f4a6e7d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2018-06-10  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [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  <mitz@apple.com>
 
         [Xcode] Opt out of the New Build System
index e0a28bb..807a5be 100644 (file)
@@ -1,3 +1,15 @@
+2018-06-10  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [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  <cgarcia@igalia.com>
 
         [WPE] WebDriver: stop making mandatory to provide a browser path if wpe:browserOptions is present in capabilities
index 0aaac3d..d63f5a4 100644 (file)
@@ -81,7 +81,7 @@ bool WebDriverService::platformMatchCapability(const String&, const RefPtr<JSON:
 
 void WebDriverService::platformParseCapabilities(const JSON::Object& matchedCapabilities, Capabilities& capabilities) const
 {
-    capabilities.browserBinary = String("dyz");
+    capabilities.browserBinary = String("MiniBrowser");
     capabilities.browserArguments = Vector<String> { ASCIILiteral("--automation") };
 
     RefPtr<JSON::Object> browserOptions;
diff --git a/Source/cmake/FindWaylandProtocols.cmake b/Source/cmake/FindWaylandProtocols.cmake
new file mode 100644 (file)
index 0000000..69bdc7a
--- /dev/null
@@ -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 ()
index 8b0cd1f..10e156c 100644 (file)
@@ -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)
index 0a48118..5f4f9e0 100644 (file)
@@ -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)
index bd01940..9cc4ffe 100644 (file)
@@ -1,3 +1,71 @@
+2018-06-10  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [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  <Hironori.Fujii@sony.com>
 
         [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 (file)
index 0000000..f261379
--- /dev/null
@@ -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 (file)
index 0000000..26adc30
--- /dev/null
@@ -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 <gst/gst.h>
+#include <memory>
+#include <wpe/webkit.h>
+
+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<WPEToolingBackends::ViewBackend> createViewBackend(uint32_t width, uint32_t height)
+{
+    if (headlessMode)
+        return std::make_unique<WPEToolingBackends::HeadlessViewBackend>(width, height);
+    return std::make_unique<WPEToolingBackends::WindowViewBackend>(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<InputClient>(loop));
+
+    auto* viewBackend = webkit_web_view_backend_new(wpeBackend, [](gpointer data) {
+        delete static_cast<WPEToolingBackends::ViewBackend*>(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<GEnumClass*>(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<WebKitCookieAcceptPolicy>(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;
+}
index 86ccca0..4954405 100755 (executable)
@@ -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());
index 4051a1a..10f7fed 100755 (executable)
@@ -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()
 
index 27716a4..909218a 100755 (executable)
@@ -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";
     }
 }
 
index f025731..3ce48b0 100644 (file)
@@ -33,27 +33,21 @@ class WebDriverWPE(WebDriver):
         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)
index 4e58a3d..48f1112 100644 (file)
@@ -44,7 +44,7 @@ class WebDriverTestRunnerSelenium(object):
 
     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')
index 366255e..17c9f6f 100644 (file)
@@ -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})
index 6b39dc2..b957c5a 100644 (file)
@@ -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;
index c102b1a..f511cd2 100644 (file)
@@ -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
 )
index 1d3da7f..13ba28e 100644 (file)
@@ -142,9 +142,9 @@ public:
 #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<HeadlessViewBackend*>(userData);
+            delete static_cast<WPEToolingBackends::HeadlessViewBackend*>(userData);
         }, headlessBackend);
     }
 #endif
index 7d8358e..8cc3b71 100644 (file)
@@ -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);
 }
 
index 89faede..0453ff6 100644 (file)
@@ -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
index 1cc8131..6d0e9b0 100644 (file)
@@ -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
 
index fe582c7..05c6568 100644 (file)
@@ -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 (file)
index 13dbd42..0000000
+++ /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 (file)
index 0000000..f9e8b43
--- /dev/null
@@ -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})
 #include <cassert>
 #include <fcntl.h>
 #include <unistd.h>
+
+// This include order is necessary to enforce the GBM EGL platform.
+#include <gbm.h>
+#include <epoxy/egl.h>
 #include <wpe/fdo-egl.h>
 
-// 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<type>(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<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
-    m_egl.destroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
-    m_egl.queryBuffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL>(eglGetProcAddress("eglQueryWaylandBufferWL"));
-    m_egl.imageTargetTexture2DOES = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
-
-    m_exportable = wpe_view_backend_exportable_fdo_create(&s_exportableClient, this, 800, 600);
+    createImage = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
+    destroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
+    queryBuffer = reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL>(eglGetProcAddress("eglQueryWaylandBufferWL"));
+    imageTargetTexture2DOES = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
 
     m_updateSource = g_timeout_source_new(m_frameRate / 1000);
-    g_source_set_callback(m_updateSource,
-        [](gpointer data) -> gboolean {
-            auto& backend = *static_cast<HeadlessViewBackend*>(data);
-            backend.performUpdate();
-            return TRUE;
-        }, this, nullptr);
+    g_source_set_callback(m_updateSource, [](gpointer data) -> gboolean {
+        static_cast<HeadlessViewBackend*>(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 wl_resource*, std::tuple<EGLImageKHR, uint32_t, uint32_t>> { };
 }
 
-struct wpe_view_backend_exportable_fdo_client HeadlessViewBackend::s_exportableClient = {
-    // export_buffer_resource
-    [](void* data, struct wl_resource* bufferResource)
-    {
-        auto& backend = *static_cast<HeadlessViewBackend*>(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
 
 #pragma once
 
-// This include order is necessary to enforce the GBM EGL platform.
-#include <gbm.h>
-#include <epoxy/egl.h>
-
+#include "ViewBackend.h"
 #include <cairo.h>
 #include <glib.h>
 #include <unordered_map>
-#include <wpe/fdo.h>
 
-#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<struct wl_resource*, std::tuple<EGLImageKHR, uint32_t, uint32_t>> m_pendingImage { };
     std::pair<struct wl_resource*, std::tuple<EGLImageKHR, uint32_t, uint32_t>> 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 (file)
index 0000000..3d0ac69
--- /dev/null
@@ -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 <epoxy/egl.h>
+#include <glib.h>
+#include <wpe/fdo-egl.h>
+
+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<ViewBackend*>(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<InputClient>&& 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 (file)
index 0000000..e6cb268
--- /dev/null
@@ -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 <memory>
+#include <wpe/fdo.h>
+
+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<type>(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<InputClient>&&);
+
+    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<InputClient> m_inputClient;
+};
+
+} // namespace WPEToolingBackends
diff --git a/Tools/wpe/backends/WindowViewBackend.cpp b/Tools/wpe/backends/WindowViewBackend.cpp
new file mode 100644 (file)
index 0000000..8134a1b
--- /dev/null
@@ -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 <cstdio>
+#include <cstring>
+#include <linux/input.h>
+#include <memory>
+#include <sys/mman.h>
+#include <unistd.h>
+
+// This include order is necessary to enforce the Wayland EGL platform.
+#include <wayland-egl.h>
+#include <epoxy/egl.h>
+
+#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<EventSource*>(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<EventSource*>(base);
+        return !!source->pfd.revents;
+    },
+    // dispatch
+    [](GSource* base, GSourceFunc, gpointer) -> gboolean
+    {
+        auto* source = reinterpret_cast<EventSource*>(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<WindowViewBackend*>(data);
+
+        if (!std::strcmp(interface, "wl_compositor"))
+            window->m_compositor = static_cast<struct wl_compositor*>(wl_registry_bind(registry, name, &wl_compositor_interface, 1));
+
+        if (!std::strcmp(interface, "zxdg_shell_v6"))
+            window->m_xdg = static_cast<struct zxdg_shell_v6*>(wl_registry_bind(registry, name, &zxdg_shell_v6_interface, 1));
+
+        if (!std::strcmp(interface, "wl_seat"))
+            window->m_seat = static_cast<struct wl_seat*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<char*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<WindowViewBackend*>(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_component>(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<WindowViewBackend*>(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<WindowViewBackend*>(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<EventSource*>(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<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(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<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
+    destroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
+    imageTargetTexture2DOES = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(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<WindowViewBackend*>(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 (file)
index 0000000..044b5b7
--- /dev/null
@@ -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 <glib.h>
+#include <unordered_map>
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-compose.h>
+
+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<int, int> 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
index 85d9145..f08621c 100644 (file)
@@ -23,7 +23,7 @@
       <dep package="libgpg-error"/>
       <dep package="libgcrypt"/>
       <dep package="libepoxy"/>
-      <dep package="dyz"/>
+      <dep package="wayland-protocols"/>
     </dependencies>
   </metamodule>
 
@@ -36,6 +36,8 @@
       href="http://cairographics.org"/>
   <repository type="tarball" name="freedesktop.org"
       href="http://www.freedesktop.org"/>
+  <repository type="tarball" name="wayland.freedesktop.org"
+      href="http://wayland.freedesktop.org"/>
   <repository type="git" name="github.com"
       href="https://github.com"/>
   <repository type="tarball" name="github-tarball"
             version="1.4.3" repo="github-tarball"/>
   </autotools>
 
-  <autotools id="dyz" supports-non-srcdir-builds="no">
-    <branch repo="github.com" module="Igalia/dyz" tag="4f196778c82ed72095e72c9d401cd2ff8ba36c60"/>
+  <autotools id="wayland-protocols" autogen-sh="configure">
+    <branch module="releases/wayland-protocols-${version}.tar.xz"
+            version="1.12"
+           repo="wayland.freedesktop.org"
+           hash="sha256:3b19e8a9e1e19474756a7069db23b90ca9b8ebb438448c6063b4a7fc89b7c8b2"/>
   </autotools>
 
   <meson id="graphene">