Diff
Modified: trunk/Tools/ChangeLog (288813 => 288814)
--- trunk/Tools/ChangeLog 2022-01-31 14:56:26 UTC (rev 288813)
+++ trunk/Tools/ChangeLog 2022-01-31 15:14:28 UTC (rev 288814)
@@ -1,3 +1,40 @@
+2022-01-31 Don Olmstead <[email protected]>
+
+ Support additional WPEToolingBackend types
+ https://bugs.webkit.org/show_bug.cgi?id=235745
+
+ Reviewed by Adrian Perez de Castro.
+
+ Split up code specific to the fdo backend to allow for additional WPE backend types to be
+ used. Add some platform defines based on the underlying backend.
+
+ This is a precursor to landing a PlayStation variant.
+
+ * wpe/backends/CMakeLists.txt:
+ * wpe/backends/HeadlessViewBackend.cpp:
+ (WPEToolingBackends::HeadlessInstance::singleton): Deleted.
+ (WPEToolingBackends::HeadlessViewBackend::HeadlessViewBackend): Deleted.
+ (WPEToolingBackends::HeadlessViewBackend::~HeadlessViewBackend): Deleted.
+ (WPEToolingBackends::HeadlessViewBackend::backend const): Deleted.
+ (WPEToolingBackends::HeadlessViewBackend::snapshot): Deleted.
+ (WPEToolingBackends::HeadlessViewBackend::updateSnapshot): Deleted.
+ (WPEToolingBackends::HeadlessViewBackend::vsync): Deleted.
+ * wpe/backends/HeadlessViewBackend.h:
+ * wpe/backends/PlatformWPE.cmake:
+ * wpe/backends/ViewBackend.cpp:
+ (WPEToolingBackends::ViewBackend::ViewBackend):
+ * wpe/backends/ViewBackend.h:
+ * wpe/backends/fdo/HeadlessViewBackendFdo.cpp: Copied from Tools/wpe/backends/HeadlessViewBackend.cpp.
+ (WPEToolingBackends::HeadlessInstance::singleton):
+ (WPEToolingBackends::HeadlessViewBackend::HeadlessViewBackend):
+ (WPEToolingBackends::HeadlessViewBackend::~HeadlessViewBackend):
+ (WPEToolingBackends::HeadlessViewBackend::backend const):
+ (WPEToolingBackends::HeadlessViewBackend::snapshot):
+ (WPEToolingBackends::HeadlessViewBackend::updateSnapshot):
+ (WPEToolingBackends::HeadlessViewBackend::vsync):
+ * wpe/backends/fdo/WindowViewBackend.cpp: Renamed from Tools/wpe/backends/WindowViewBackend.cpp.
+ * wpe/backends/fdo/WindowViewBackend.h: Renamed from Tools/wpe/backends/WindowViewBackend.h.
+
2022-01-31 Philippe Normand <[email protected]>
[Flatpak SDK] No longer updating due to expired GPG key
Modified: trunk/Tools/wpe/backends/CMakeLists.txt (288813 => 288814)
--- trunk/Tools/wpe/backends/CMakeLists.txt 2022-01-31 14:56:26 UTC (rev 288813)
+++ trunk/Tools/wpe/backends/CMakeLists.txt 2022-01-31 15:14:28 UTC (rev 288814)
@@ -7,13 +7,11 @@
set(WPEToolingBackends_PUBLIC_HEADERS
HeadlessViewBackend.h
ViewBackend.h
- WindowViewBackend.h
)
set(WPEToolingBackends_SOURCES
HeadlessViewBackend.cpp
ViewBackend.cpp
- WindowViewBackend.cpp
)
set(WPEToolingBackends_PRIVATE_INCLUDE_DIRECTORIES
@@ -26,6 +24,10 @@
WPE::libwpe
)
+if (USE_CAIRO)
+ list(APPEND WPEToolingBackends_DEFINITIONS USE_CAIRO=1)
+endif ()
+
set(WPEToolingBackends_INTERFACE_LIBRARIES WPEToolingBackends)
set(WPEToolingBackends_INTERFACE_INCLUDE_DIRECTORIES ${WPEToolingBackends_FRAMEWORK_HEADERS_DIR})
set(WPEToolingBackends_INTERFACE_DEPENDENCIES WPEToolingBackends_CopyHeaders)
Modified: trunk/Tools/wpe/backends/HeadlessViewBackend.cpp (288813 => 288814)
--- trunk/Tools/wpe/backends/HeadlessViewBackend.cpp 2022-01-31 14:56:26 UTC (rev 288813)
+++ trunk/Tools/wpe/backends/HeadlessViewBackend.cpp 2022-01-31 15:14:28 UTC (rev 288814)
@@ -25,182 +25,9 @@
#include "HeadlessViewBackend.h"
-#include <cassert>
-#include <fcntl.h>
-#include <mutex>
-#include <unistd.h>
-
-#if WPE_FDO_CHECK_VERSION(1,7,0)
-#include <wayland-server.h>
-#include <wpe/unstable/fdo-shm.h>
-#endif
-
namespace WPEToolingBackends {
-struct HeadlessInstance {
- static const HeadlessInstance& singleton()
- {
- static std::once_flag s_onceFlag;
- static HeadlessInstance s_instance;
- std::call_once(s_onceFlag,
- [] {
-#if WPE_FDO_CHECK_VERSION(1,7,0)
- wpe_fdo_initialize_shm();
-#endif
- });
-
- return s_instance;
- }
-};
-
-static cairo_user_data_key_t s_bufferKey;
-
-HeadlessViewBackend::HeadlessViewBackend(uint32_t width, uint32_t height)
- : ViewBackend(width, height)
-{
- // This should initialize the SHM mode.
- HeadlessInstance::singleton();
-
- static struct wpe_view_backend_exportable_fdo_client exportableClient = {
- nullptr,
- nullptr,
-#if WPE_FDO_CHECK_VERSION(1,7,0)
- // export_shm_buffer
- [](void* data, struct wpe_fdo_shm_exported_buffer* buffer)
- {
- auto& backend = *static_cast<HeadlessViewBackend*>(data);
- backend.m_update.pending = true;
-
- backend.updateSnapshot(buffer);
- wpe_view_backend_exportable_fdo_dispatch_release_shm_exported_buffer(backend.m_exportable, buffer);
- },
-#else
- nullptr,
-#endif
- nullptr,
- nullptr,
- };
- m_exportable = wpe_view_backend_exportable_fdo_create(&exportableClient, this, width, height);
-
- addActivityState(wpe_view_activity_state_visible | wpe_view_activity_state_focused | wpe_view_activity_state_in_window);
-
- {
- uint32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m_width);
- uint8_t* buffer = new uint8_t[stride * m_height];
- memset(buffer, 0, stride * m_height);
-
- m_snapshot = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_ARGB32,
- m_width, m_height, stride);
-
- cairo_surface_set_user_data(m_snapshot, &s_bufferKey, buffer,
- [](void* data) {
- auto* buffer = static_cast<uint8_t*>(data);
- delete[] buffer;
- });
- cairo_surface_mark_dirty(m_snapshot);
- }
-
#if WPE_CHECK_VERSION(1, 11, 1)
- wpe_view_backend_set_fullscreen_handler(backend(), onDOMFullScreenRequest, this);
-#endif
-
- m_update.source = g_timeout_source_new(G_USEC_PER_SEC / 60000);
- g_source_set_callback(m_update.source, [](gpointer data) -> gboolean {
- static_cast<HeadlessViewBackend*>(data)->vsync();
- return TRUE;
- }, this, nullptr);
- g_source_set_priority(m_update.source, G_PRIORITY_DEFAULT);
- g_source_attach(m_update.source, g_main_context_default());
-}
-
-HeadlessViewBackend::~HeadlessViewBackend()
-{
- if (m_update.source) {
- g_source_destroy(m_update.source);
- g_source_unref(m_update.source);
- }
-
- if (m_snapshot)
- cairo_surface_destroy(m_snapshot);
-
- if (m_exportable)
- wpe_view_backend_exportable_fdo_destroy(m_exportable);
-}
-
-struct wpe_view_backend* HeadlessViewBackend::backend() const
-{
- if (m_exportable)
- return wpe_view_backend_exportable_fdo_get_view_backend(m_exportable);
- return nullptr;
-}
-
-cairo_surface_t* HeadlessViewBackend::snapshot()
-{
- return cairo_surface_reference(m_snapshot);
-}
-
-void HeadlessViewBackend::updateSnapshot(struct wpe_fdo_shm_exported_buffer* exportedBuffer)
-{
-#if WPE_FDO_CHECK_VERSION(1,7,0)
- struct wl_shm_buffer* shmBuffer = wpe_fdo_shm_exported_buffer_get_shm_buffer(exportedBuffer);
- {
- auto format = wl_shm_buffer_get_format(shmBuffer);
- if (format != WL_SHM_FORMAT_ARGB8888 && format != WL_SHM_FORMAT_XRGB8888)
- return;
- }
-
- uint32_t bufferStride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m_width);
- uint8_t* buffer = new uint8_t[bufferStride * m_height];
- memset(buffer, 0, bufferStride * m_height);
-
- {
- uint32_t width = std::min<uint32_t>(m_width, std::max(0, wl_shm_buffer_get_width(shmBuffer)));
- uint32_t height = std::min<uint32_t>(m_height, std::max(0, wl_shm_buffer_get_height(shmBuffer)));
- uint32_t stride = std::max(0, wl_shm_buffer_get_stride(shmBuffer));
-
- wl_shm_buffer_begin_access(shmBuffer);
- auto* data = ""
-
- for (uint32_t y = 0; y < height; ++y) {
- for (uint32_t x = 0; x < width; ++x) {
- buffer[bufferStride * y + 4 * x + 0] = data[stride * y + 4 * x + 0];
- buffer[bufferStride * y + 4 * x + 1] = data[stride * y + 4 * x + 1];
- buffer[bufferStride * y + 4 * x + 2] = data[stride * y + 4 * x + 2];
- buffer[bufferStride * y + 4 * x + 3] = data[stride * y + 4 * x + 3];
- }
- }
-
- wl_shm_buffer_end_access(shmBuffer);
- }
-
- if (m_snapshot)
- cairo_surface_destroy(m_snapshot);
-
- m_snapshot = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_ARGB32,
- m_width, m_height, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m_width));
-
- static cairo_user_data_key_t bufferKey;
- cairo_surface_set_user_data(m_snapshot, &bufferKey, buffer,
- [](void* data) {
- auto* buffer = static_cast<uint8_t*>(data);
- delete[] buffer;
- });
- cairo_surface_mark_dirty(m_snapshot);
-#else
- (void)exportedBuffer;
-#endif
-}
-
-void HeadlessViewBackend::vsync()
-{
-#if WPE_FDO_CHECK_VERSION(1,7,0)
- if (m_update.pending)
- wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
-#endif
- m_update.pending = false;
-}
-
-#if WPE_CHECK_VERSION(1, 11, 1)
bool HeadlessViewBackend::onDOMFullScreenRequest(void* data, bool fullscreen)
{
auto& headless = *static_cast<HeadlessViewBackend*>(data);
@@ -224,5 +51,4 @@
#endif
-
} // namespace WPEToolingBackends
Modified: trunk/Tools/wpe/backends/HeadlessViewBackend.h (288813 => 288814)
--- trunk/Tools/wpe/backends/HeadlessViewBackend.h 2022-01-31 14:56:26 UTC (rev 288813)
+++ trunk/Tools/wpe/backends/HeadlessViewBackend.h 2022-01-31 15:14:28 UTC (rev 288814)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2016 Igalia S.L.
+ * Copyright (C) 2022 Sony Interactive Entertainment Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,8 +27,23 @@
#pragma once
#include "ViewBackend.h"
+
+#if defined(WPE_BACKEND_FDO)
+#include <wpe/fdo.h>
+
+using PlatformBuffer = struct wpe_fdo_shm_exported_buffer*;
+using PlatformViewBackend = struct wpe_view_backend_exportable_fdo*;
+#endif
+
+#if defined(USE_CAIRO) && USE_CAIRO
#include <cairo.h>
+
+using PlatformImage = cairo_surface_t*;
+#endif
+
+#if defined(USE_GLIB) && USE_GLIB
#include <glib.h>
+#endif
namespace WPEToolingBackends {
@@ -38,10 +54,10 @@
struct wpe_view_backend* backend() const override;
- cairo_surface_t* snapshot();
+ PlatformImage snapshot();
private:
- void updateSnapshot(struct wpe_fdo_shm_exported_buffer*);
+ void updateSnapshot(PlatformBuffer);
void vsync();
#if WPE_CHECK_VERSION(1, 11, 1)
@@ -49,15 +65,15 @@
void dispatchFullscreenEvent();
#endif
+ PlatformViewBackend m_exportable { nullptr };
+ PlatformImage m_snapshot { nullptr };
- struct wpe_view_backend_exportable_fdo* m_exportable { nullptr };
-
- cairo_surface_t* m_snapshot { nullptr };
-
+#if defined(USE_GLIB) && USE_GLIB
struct {
GSource* source { nullptr };
bool pending { false };
} m_update;
+#endif
#if WPE_CHECK_VERSION(1, 11, 1)
bool m_is_fullscreen { false };
Modified: trunk/Tools/wpe/backends/PlatformWPE.cmake (288813 => 288814)
--- trunk/Tools/wpe/backends/PlatformWPE.cmake 2022-01-31 14:56:26 UTC (rev 288813)
+++ trunk/Tools/wpe/backends/PlatformWPE.cmake 2022-01-31 15:14:28 UTC (rev 288814)
@@ -5,16 +5,22 @@
list(APPEND WPEToolingBackends_PUBLIC_HEADERS
${WPEToolingBackends_DERIVED_SOURCES_DIR}/xdg-shell-unstable-v6-client-protocol.h
+ fdo/WindowViewBackend.h
)
list(APPEND WPEToolingBackends_SOURCES
${WPEToolingBackends_DERIVED_SOURCES_DIR}/xdg-shell-unstable-v6-protocol.c
+
atk/ViewBackendAtk.cpp
atk/WebKitAccessibleApplication.cpp
+
+ fdo/HeadlessViewBackendFdo.cpp
+ fdo/WindowViewBackend.cpp
)
list(APPEND WPEToolingBackends_PRIVATE_INCLUDE_DIRECTORIES
${TOOLS_DIR}/wpe/backends/atk
+ ${TOOLS_DIR}/wpe/backends/fdo
)
list(APPEND WPEToolingBackends_SYSTEM_INCLUDE_DIRECTORIES
@@ -33,6 +39,7 @@
${WPEBACKEND_FDO_LIBRARIES}
)
+list(APPEND WPEToolingBackends_DEFINITIONS USE_GLIB=1)
list(APPEND WPEToolingBackends_PRIVATE_DEFINITIONS ${LIBEPOXY_DEFINITIONS})
add_custom_command(
@@ -48,9 +55,12 @@
COMMAND ${WAYLAND_SCANNER} client-header ${WAYLAND_PROTOCOLS_DATADIR}/unstable/xdg-shell/xdg-shell-unstable-v6.xml ${WPEToolingBackends_DERIVED_SOURCES_DIR}/xdg-shell-unstable-v6-client-protocol.h
VERBATIM)
+list(APPEND WPEToolingBackends_DEFINITIONS WPE_BACKEND_FDO)
+list(APPEND WPEToolingBackends_PRIVATE_DEFINITIONS WPE_BACKEND="libWPEBackend-fdo-1.0.so")
+
if (ENABLE_ACCESSIBILITY)
+ list(APPEND WPEToolingBackends_DEFINITIONS ENABLE_ACCESSIBILITY=1)
list(APPEND WPEToolingBackends_PRIVATE_DEFINITIONS
- ENABLE_ACCESSIBILITY=1
GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_40
)
list(APPEND WPEToolingBackends_LIBRARIES ATK::Bridge)
Modified: trunk/Tools/wpe/backends/ViewBackend.cpp (288813 => 288814)
--- trunk/Tools/wpe/backends/ViewBackend.cpp 2022-01-31 14:56:26 UTC (rev 288813)
+++ trunk/Tools/wpe/backends/ViewBackend.cpp 2022-01-31 15:14:28 UTC (rev 288814)
@@ -31,7 +31,7 @@
: m_width(width)
, m_height(height)
{
- wpe_loader_init("libWPEBackend-fdo-1.0.so");
+ wpe_loader_init(WPE_BACKEND);
}
ViewBackend::~ViewBackend() = default;
Modified: trunk/Tools/wpe/backends/ViewBackend.h (288813 => 288814)
--- trunk/Tools/wpe/backends/ViewBackend.h 2022-01-31 14:56:26 UTC (rev 288813)
+++ trunk/Tools/wpe/backends/ViewBackend.h 2022-01-31 15:14:28 UTC (rev 288814)
@@ -26,26 +26,12 @@
#pragma once
#include <memory>
-#include <wpe/fdo.h>
+#include <wpe/wpe.h>
#if defined(ENABLE_ACCESSIBILITY) && ENABLE_ACCESSIBILITY
typedef struct _AtkObject AtkObject;
#endif
-typedef void* EGLConfig;
-typedef void* EGLContext;
-typedef void* EGLDisplay;
-struct wpe_fdo_egl_exported_image;
-
-#if WPE_FDO_CHECK_VERSION(1, 5, 0)
-struct wpe_fdo_shm_exported_buffer;
-#endif
-
-// 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 {
Deleted: trunk/Tools/wpe/backends/WindowViewBackend.cpp (288813 => 288814)
--- trunk/Tools/wpe/backends/WindowViewBackend.cpp 2022-01-31 14:56:26 UTC (rev 288813)
+++ trunk/Tools/wpe/backends/WindowViewBackend.cpp 2022-01-31 15:14:28 UTC (rev 288814)
@@ -1,984 +0,0 @@
-/*
- * 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 <mutex>
-#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>
-#include <wpe/fdo-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 {
-
-struct WaylandEGLConnection {
- struct wl_display* display { nullptr };
- EGLDisplay eglDisplay { EGL_NO_DISPLAY };
-
- static const WaylandEGLConnection& singleton()
- {
- static std::once_flag s_onceFlag;
- static WaylandEGLConnection s_connection;
- std::call_once(s_onceFlag,
- [] {
- s_connection.display = wl_display_connect(nullptr);
- if (!s_connection.display) {
- g_warning("WaylandEGLConnection: Could not connect to Wayland Display");
- return;
- }
-
- EGLDisplay eglDisplay = eglGetDisplay(s_connection.display);
- if (eglDisplay == EGL_NO_DISPLAY) {
- g_warning("WaylandEGLConnection: No EGL Display available in this connection");
- return;
- }
-
- if (!eglInitialize(eglDisplay, nullptr, nullptr) || !eglBindAPI(EGL_OPENGL_ES_API)) {
- g_warning("WaylandEGLConnection: Failed to initialize and bind the EGL Display");
- return;
- }
-
- s_connection.eglDisplay = eglDisplay;
- wpe_fdo_initialize_for_egl_display(s_connection.eglDisplay);
- });
-
- return s_connection;
- }
-};
-
-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, 5));
- },
- // 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;
- window.m_seatData.pointer.modifiers = 0;
- }
- },
- // 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.modifiers() };
- 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;
-
- uint32_t modifier = 0;
- switch (button) {
- case 1:
- modifier = wpe_input_pointer_modifier_button1;
- break;
- case 2:
- modifier = wpe_input_pointer_modifier_button2;
- break;
- case 3:
- modifier = wpe_input_pointer_modifier_button3;
- break;
- case 4:
- modifier = wpe_input_pointer_modifier_button4;
- break;
- case 5:
- modifier = wpe_input_pointer_modifier_button5;
- break;
- default:
- break;
- }
-
- if (state)
- window.m_seatData.pointer.modifiers |= modifier;
- else
- window.m_seatData.pointer.modifiers &= ~modifier;
-
- 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.modifiers() };
- window.dispatchInputPointerEvent(&event);
- }
- },
- // axis
- [](void* data, struct wl_pointer*, uint32_t time, uint32_t axis, wl_fixed_t value)
- {
- if (axis != WL_POINTER_AXIS_HORIZONTAL_SCROLL && axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
- return;
-
- 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.modifiers() };
- if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL && window.m_seatData.axis_discrete.horizontal)
- event.value = window.m_seatData.axis_discrete.horizontal;
- else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL && window.m_seatData.axis_discrete.vertical)
- event.value = window.m_seatData.axis_discrete.vertical;
-#if WPE_CHECK_VERSION(1, 5, 0)
- else {
- struct wpe_input_axis_2d_event event2d = { event, 0, 0 };
- event2d.base.type = static_cast<wpe_input_axis_event_type>(wpe_input_axis_event_type_mask_2d | wpe_input_axis_event_type_motion_smooth);
-
- if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
- event2d.x_axis = wl_fixed_to_double(value);
- else
- event2d.y_axis = -wl_fixed_to_double(value);
-
- window.dispatchInputAxisEvent(&event2d.base);
- return;
- }
-#endif
- window.dispatchInputAxisEvent(&event);
- window.m_seatData.axis_discrete.horizontal = window.m_seatData.axis_discrete.vertical = 0;
- }
- },
- // frame
- [](void*, struct wl_pointer*) { },
- // axis_source
- [](void*, struct wl_pointer*, uint32_t) { },
- // axis_stop
- [](void* data, struct wl_pointer*, uint32_t time, uint32_t axis)
- {
- if (axis != WL_POINTER_AXIS_HORIZONTAL_SCROLL && axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
- return;
-
- 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, 0, window.modifiers() };
- window.dispatchInputAxisEvent(&event);
- }
- },
- // axis_discrete
- [](void* data, struct wl_pointer*, uint32_t axis, int32_t discrete) {
- auto& window = *static_cast<WindowViewBackend*>(data);
- switch (axis) {
- case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
- window.m_seatData.axis_discrete.horizontal = discrete;
- break;
- case WL_POINTER_AXIS_VERTICAL_SCROLL:
- window.m_seatData.axis_discrete.vertical = -discrete;
- break;
- }
- },
-};
-
-const struct wl_keyboard_listener WindowViewBackend::s_keyboardListener = {
- // keymap
- [](void*, struct wl_keyboard*, uint32_t format, int fd, uint32_t size)
- {
- 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 = wpe_input_xkb_context_get_default();
- auto* keymap = xkb_keymap_new_from_string(wpe_input_xkb_context_get_context(xkb), static_cast<char*>(mapping),
- XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
- munmap(mapping, size);
- close(fd);
-
- wpe_input_xkb_context_set_keymap(xkb, keymap);
- xkb_keymap_unref(keymap);
- },
- // 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;
-
- auto* keymap = wpe_input_xkb_context_get_keymap(wpe_input_xkb_context_get_default());
-
- 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
- && keymap && xkb_keymap_key_repeats(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& keyboard = static_cast<WindowViewBackend*>(data)->m_seatData.keyboard;
- keyboard.modifiers = wpe_input_xkb_context_get_modifiers(wpe_input_xkb_context_get_default(), depressedMods, latchedMods, lockedMods, group);
- },
- // 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_touch_listener WindowViewBackend::s_touchListener = {
- // down
- [](void* data, struct wl_touch*, uint32_t, uint32_t time, struct wl_surface* surface, int32_t id, wl_fixed_t x, wl_fixed_t y)
- {
- auto& window = *static_cast<WindowViewBackend*>(data);
- if (window.m_surface != surface || id < 0 || id >= 10)
- return;
-
- auto& seatData = window.m_seatData;
- seatData.touch.tracking = true;
- struct wpe_input_touch_event_raw rawEvent = { wpe_input_touch_event_type_down,
- time, id, wl_fixed_to_int(x), wl_fixed_to_int(y) };
- memcpy(&seatData.touch.points[id], &rawEvent, sizeof(struct wpe_input_touch_event_raw));
-
- struct wpe_input_touch_event event = { seatData.touch.points, 10,
- rawEvent.type, rawEvent.id, rawEvent.time, window.modifiers() };
- window.dispatchInputTouchEvent(&event);
- },
- // up
- [](void* data, struct wl_touch*, uint32_t, uint32_t time, int32_t id)
- {
- auto& window = *static_cast<WindowViewBackend*>(data);
- auto& seatData = window.m_seatData;
- if (!seatData.touch.tracking || id < 0 || id >= 10)
- return;
-
- seatData.touch.tracking = false;
-
- struct wpe_input_touch_event_raw rawEvent = { wpe_input_touch_event_type_up,
- time, id, seatData.touch.points[id].x, seatData.touch.points[id].y };
- memcpy(&seatData.touch.points[id], &rawEvent, sizeof(struct wpe_input_touch_event_raw));
-
- struct wpe_input_touch_event event = { seatData.touch.points, 10,
- rawEvent.type, rawEvent.id, rawEvent.time, window.modifiers() };
- window.dispatchInputTouchEvent(&event);
-
- memset(&seatData.touch.points[id], 0x00, sizeof(struct wpe_input_touch_event_raw));
- },
- // motion
- [](void* data, struct wl_touch*, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y)
- {
- auto& window = *static_cast<WindowViewBackend*>(data);
- auto& seatData = window.m_seatData;
- if (!seatData.touch.tracking || id < 0 || id >= 10)
- return;
-
- struct wpe_input_touch_event_raw rawEvent = { wpe_input_touch_event_type_motion,
- time, id, wl_fixed_to_int(x), wl_fixed_to_int(y) };
- memcpy(&seatData.touch.points[id], &rawEvent, sizeof(struct wpe_input_touch_event_raw));
-
- struct wpe_input_touch_event event = { seatData.touch.points, 10,
- rawEvent.type, rawEvent.id, rawEvent.time, window.modifiers() };
- window.dispatchInputTouchEvent(&event);
- },
- // frame
- [](void*, struct wl_touch*) { },
- // cancel
- [](void*, struct wl_touch*) { },
- // shape
- [](void*, struct wl_touch*, int32_t, wl_fixed_t, wl_fixed_t) { },
- // orientation
- [](void*, struct wl_touch*, int32_t, wl_fixed_t) { },
-};
-
-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;
- }
-
- // WL_SEAT_CAPABILITY_TOUCH
- const bool hasTouchCap = capabilities & WL_SEAT_CAPABILITY_TOUCH;
- if (hasTouchCap && !seatData.touch.object) {
- seatData.touch.object = wl_seat_get_touch(seat);
- wl_touch_add_listener(seatData.touch.object, &s_touchListener, window);
- }
- if (!hasTouchCap && seatData.touch.object) {
- wl_touch_destroy(seatData.touch.object);
- seatData.touch.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* data, struct zxdg_toplevel_v6*, int32_t width, int32_t height, struct wl_array* states)
- {
- auto& window = *static_cast<WindowViewBackend*>(data);
- window.resize(std::max(0, width), std::max(0, height));
-
- bool isFocused = false;
- bool isFullscreen = false;
- // FIXME: It would be nice if the following loop could use
- // wl_array_for_each, but at the time of writing it relies on
- // GCC specific extension to work properly:
- // https://gitlab.freedesktop.org/wayland/wayland/issues/34
- uint32_t* pos = static_cast<uint32_t*>(states->data);
- uint32_t* end = static_cast<uint32_t*>(states->data) + states->size;
-
- for (; pos < end; pos++) {
- uint32_t state = *pos;
-
- switch (state) {
- case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED:
- isFocused = true;
- break;
- case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN:
- isFullscreen = true;
- break;
- case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED:
- case ZXDG_TOPLEVEL_V6_STATE_RESIZING:
- default:
- break;
- }
- }
-
- if (isFocused)
- window.addActivityState(wpe_view_activity_state_focused);
- else
- window.removeActivityState(wpe_view_activity_state_focused);
-
- if (window.m_is_fullscreen != isFullscreen)
- window.onFullscreenChanged(isFullscreen);
- },
- // close
- [](void* data, struct zxdg_toplevel_v6*)
- {
- auto& window = *static_cast<WindowViewBackend*>(data);
- window.removeActivityState(wpe_view_activity_state_visible | wpe_view_activity_state_focused | wpe_view_activity_state_in_window);
- },
-};
-
-#if WPE_CHECK_VERSION(1, 11, 1)
-
-bool WindowViewBackend::onDOMFullscreenRequest(void* data, bool fullscreen)
-{
- auto& window = *static_cast<WindowViewBackend*>(data);
- if (window.m_waiting_fullscreen_notify)
- return false;
-
- if (fullscreen == window.m_is_fullscreen) {
- // Handle situations where DOM fullscreen requests are mixed with system fullscreen commands (e.g F11)
- window.dispatchFullscreenEvent();
- return true;
- }
-
- window.m_waiting_fullscreen_notify = true;
- if (fullscreen)
- zxdg_toplevel_v6_set_fullscreen(window.m_xdgToplevel, nullptr);
- else
- zxdg_toplevel_v6_unset_fullscreen(window.m_xdgToplevel);
-
- return true;
-}
-
-void WindowViewBackend::dispatchFullscreenEvent()
-{
- if (m_is_fullscreen)
- wpe_view_backend_dispatch_did_enter_fullscreen(backend());
- else
- wpe_view_backend_dispatch_did_exit_fullscreen(backend());
-}
-
-void WindowViewBackend::onFullscreenChanged(bool fullscreen)
-{
- bool wasRequestedFromDOM = m_waiting_fullscreen_notify;
- m_waiting_fullscreen_notify= false;
- m_is_fullscreen = fullscreen;
-
- if (!fullscreen && !wasRequestedFromDOM)
- wpe_view_backend_dispatch_request_exit_fullscreen(backend());
- else if (wasRequestedFromDOM)
- dispatchFullscreenEvent();
-}
-
-#else
-
-void WindowViewBackend::onFullscreenChanged(bool fullscreen)
-{
- m_is_fullscreen = fullscreen;
-}
-
-#endif // WPE_CHECK_VERSION(1, 11, 1)
-
-WindowViewBackend::WindowViewBackend(uint32_t width, uint32_t height)
- : ViewBackend(width, height)
-{
- m_initialSize.width = width;
- m_initialSize.height = height;
-
- auto& connection = WaylandEGLConnection::singleton();
- if (!connection.display) {
- g_warning("WindowViewBackend: No Wayland EGL connection available");
- return;
- }
-
- if (connection.eglDisplay == EGL_NO_DISPLAY || !initialize(connection.eglDisplay)) {
- g_warning("WindowViewBackend: Could not initialize EGL display");
- return;
- }
-
- {
- auto* registry = wl_display_get_registry(connection.display);
- wl_registry_add_listener(registry, &s_registryListener, this);
- wl_display_roundtrip(connection.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_eventSource = g_source_new(&EventSource::sourceFuncs, sizeof(EventSource));
- {
- auto& source = *reinterpret_cast<EventSource*>(m_eventSource);
- source.display = connection.display;
-
- source.pfd.fd = wl_display_get_fd(connection.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_DEFAULT);
- 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, this);
- zxdg_toplevel_v6_set_title(m_xdgToplevel, "WPE");
- wl_surface_commit(m_surface);
- addActivityState(wpe_view_activity_state_visible | wpe_view_activity_state_in_window);
- }
- }
-
- m_eglWindow = wl_egl_window_create(m_surface, m_width, m_height);
-
- auto createPlatformWindowSurface =
- reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"));
- m_eglSurface = createPlatformWindowSurface(connection.eglDisplay, m_eglConfig, m_eglWindow, nullptr);
- if (!m_eglSurface) {
- g_warning("WindowViewBackend: Could not create EGL platform window surface");
- return;
- }
-
- if (!eglMakeCurrent(connection.eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) {
- g_warning("WindowViewBackend: Could not make EGL surface current");
- return;
- }
-
- 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");
- }
-
- createViewTexture();
-}
-
-WindowViewBackend::~WindowViewBackend()
-{
- auto& connection = WaylandEGLConnection::singleton();
-
- 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_eglSurface)
- eglDestroySurface(connection.eglDisplay, m_eglSurface);
-
- if (m_display)
- wl_display_disconnect(m_display);
-
- deinitialize(connection.eglDisplay);
-}
-
-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_committedImage)
- wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(window.m_exportable, window.m_committedImage);
- window.m_committedImage = nullptr;
- }
-};
-
-struct wpe_view_backend* WindowViewBackend::backend() const
-{
- return m_exportable ? wpe_view_backend_exportable_fdo_get_view_backend(m_exportable) : nullptr;
-}
-
-void WindowViewBackend::createViewTexture()
-{
- 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);
-}
-
-void WindowViewBackend::resize(uint32_t width, uint32_t height)
-{
- if (!width)
- width = m_initialSize.width;
- if (!height)
- height = m_initialSize.height;
-
- if (width == m_width && height == m_height)
- return;
-
- m_width = width;
- m_height = height;
-
- wl_egl_window_resize(m_eglWindow, m_width, m_height, 0, 0);
- wpe_view_backend_dispatch_set_size(backend(), m_width, m_height);
-
- if (m_viewTexture)
- glDeleteTextures(1, &m_viewTexture);
- createViewTexture();
-}
-
-bool WindowViewBackend::initialize(EGLDisplay 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(eglDisplay, nullptr, 0, &count) || count < 1)
- return false;
-
- EGLConfig* configs = g_new0(EGLConfig, count);
- EGLint matched = 0;
- if (eglChooseConfig(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(eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
- if (!m_eglContext)
- return false;
-
- static struct wpe_view_backend_exportable_fdo_egl_client exportableClient = {
- // export_egl_image
- nullptr,
- // export_fdo_egl_image
- [](void* data, struct wpe_fdo_egl_exported_image* image)
- {
- static_cast<WindowViewBackend*>(data)->displayBuffer(image);
- },
-#if WPE_FDO_CHECK_VERSION(1, 5, 0)
- // export_shm_buffer
- [](void* data, struct wpe_fdo_shm_exported_buffer* buffer)
- {
- static_cast<WindowViewBackend*>(data)->displayBuffer(buffer);
- },
- // padding
- nullptr, nullptr
-#else
- // padding
- nullptr, nullptr, nullptr
-#endif
-
- };
- m_exportable = wpe_view_backend_exportable_fdo_egl_create(&exportableClient, this, m_width, m_height);
-
-#if WPE_CHECK_VERSION(1, 11, 1)
- wpe_view_backend_set_fullscreen_handler(backend(), onDOMFullscreenRequest, this);
-#endif
-
- initializeAccessibility();
-
- return true;
-}
-
-void WindowViewBackend::deinitialize(EGLDisplay eglDisplay)
-{
- m_inputClient = nullptr;
-
- if (m_eglContext)
- eglDestroyContext(eglDisplay, m_eglContext);
-
- if (m_exportable)
- wpe_view_backend_exportable_fdo_destroy(m_exportable);
-}
-
-void WindowViewBackend::displayBuffer(struct wpe_fdo_egl_exported_image* image)
-{
- if (!m_eglContext)
- return;
-
- auto& connection = WaylandEGLConnection::singleton();
- eglMakeCurrent(connection.eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
-
- glViewport(0, 0, m_width, m_height);
- glClearColor(1, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glUseProgram(m_program);
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, m_viewTexture);
- imageTargetTexture2DOES(GL_TEXTURE_2D, wpe_fdo_egl_exported_image_get_egl_image(image));
- glUniform1i(m_textureUniform, 0);
-
- m_committedImage = 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(connection.eglDisplay, m_eglSurface);
-}
-
-#if WPE_FDO_CHECK_VERSION(1, 5, 0)
-void WindowViewBackend::displayBuffer(struct wpe_fdo_shm_exported_buffer*)
-{
- g_warning("WindowViewBackend: cannot yet handle wpe_fdo_shm_exported_buffer.");
-}
-#endif
-
-void WindowViewBackend::handleKeyEvent(uint32_t key, uint32_t state, uint32_t time)
-{
- uint32_t keysym = wpe_input_xkb_context_get_key_code(wpe_input_xkb_context_get_default(), key, state == WL_KEYBOARD_KEY_STATE_PRESSED);
- if (!keysym)
- return;
-
- if (m_seatData.keyboard.target) {
- struct wpe_input_keyboard_event event = { time, keysym, key, !!state, modifiers() };
- dispatchInputKeyboardEvent(&event);
- }
-}
-
-uint32_t WindowViewBackend::modifiers() const
-{
- uint32_t mask = m_seatData.keyboard.modifiers;
- if (m_seatData.pointer.object)
- mask |= m_seatData.pointer.modifiers;
- return mask;
-}
-
-} // namespace WPEToolingBackends
Deleted: trunk/Tools/wpe/backends/WindowViewBackend.h (288813 => 288814)
--- trunk/Tools/wpe/backends/WindowViewBackend.h 2022-01-31 14:56:26 UTC (rev 288813)
+++ trunk/Tools/wpe/backends/WindowViewBackend.h 2022-01-31 15:14:28 UTC (rev 288814)
@@ -1,146 +0,0 @@
-/*
- * 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>
-
-typedef void* EGLSurface;
-typedef struct wl_egl_window *EGLNativeWindowType;
-
-namespace WPEToolingBackends {
-
-class WindowViewBackend final : public ViewBackend {
-public:
- WindowViewBackend(uint32_t width, uint32_t height);
- virtual ~WindowViewBackend();
-
- struct wpe_view_backend* backend() const override;
-
-private:
- void createViewTexture();
- void resize(uint32_t width, uint32_t height);
-
- bool initialize(EGLDisplay);
- void deinitialize(EGLDisplay);
-
- void displayBuffer(struct wpe_fdo_egl_exported_image*);
-#if WPE_FDO_CHECK_VERSION(1, 5, 0)
- void displayBuffer(struct wpe_fdo_shm_exported_buffer*);
-#endif
-
- 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_touch_listener s_touchListener;
- 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;
-
-#if WPE_CHECK_VERSION(1, 11, 1)
- static bool onDOMFullscreenRequest(void* data, bool fullscreen);
- void dispatchFullscreenEvent();
-#endif
- void onFullscreenChanged(bool fullscreen);
-
- void handleKeyEvent(uint32_t key, uint32_t state, uint32_t time);
- uint32_t modifiers() const;
-
- 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 };
- uint32_t modifiers { 0 };
- } pointer;
-
- struct {
- struct wl_keyboard* object { nullptr };
- struct wl_surface* target { nullptr };
- uint32_t modifiers { 0 };
- } keyboard;
-
- struct {
- struct wl_touch* object { nullptr };
- struct wpe_input_touch_event_raw points[10];
- bool tracking { false };
- } touch;
-
- struct {
- int32_t horizontal { 0 };
- int32_t vertical { 0 };
- } axis_discrete;
-
- 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 {
- uint32_t width;
- uint32_t height;
- } m_initialSize;
-
- struct wpe_view_backend_exportable_fdo* m_exportable { nullptr };
-
- 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 };
- EGLContext m_eglContext { nullptr };
- EGLConfig m_eglConfig;
- EGLSurface m_eglSurface { nullptr };
- unsigned m_program { 0 };
- unsigned m_textureUniform { 0 };
- unsigned m_viewTexture { 0 };
- struct wpe_fdo_egl_exported_image* m_committedImage { nullptr };
- bool m_is_fullscreen { false };
-#if WPE_CHECK_VERSION(1, 11, 1)
- bool m_waiting_fullscreen_notify { false };
-#endif
-};
-
-} // WPEToolingBackends
Copied: trunk/Tools/wpe/backends/fdo/HeadlessViewBackendFdo.cpp (from rev 288813, trunk/Tools/wpe/backends/HeadlessViewBackend.cpp) (0 => 288814)
--- trunk/Tools/wpe/backends/fdo/HeadlessViewBackendFdo.cpp (rev 0)
+++ trunk/Tools/wpe/backends/fdo/HeadlessViewBackendFdo.cpp 2022-01-31 15:14:28 UTC (rev 288814)
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2016 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 "HeadlessViewBackend.h"
+
+#include <cassert>
+#include <fcntl.h>
+#include <mutex>
+#include <unistd.h>
+
+#if WPE_FDO_CHECK_VERSION(1,7,0)
+#include <wayland-server.h>
+#include <wpe/unstable/fdo-shm.h>
+#endif
+
+namespace WPEToolingBackends {
+
+struct HeadlessInstance {
+ static const HeadlessInstance& singleton()
+ {
+ static std::once_flag s_onceFlag;
+ static HeadlessInstance s_instance;
+ std::call_once(s_onceFlag,
+ [] {
+#if WPE_FDO_CHECK_VERSION(1,7,0)
+ wpe_fdo_initialize_shm();
+#endif
+ });
+
+ return s_instance;
+ }
+};
+
+static cairo_user_data_key_t s_bufferKey;
+
+HeadlessViewBackend::HeadlessViewBackend(uint32_t width, uint32_t height)
+ : ViewBackend(width, height)
+{
+ // This should initialize the SHM mode.
+ HeadlessInstance::singleton();
+
+ static struct wpe_view_backend_exportable_fdo_client exportableClient = {
+ nullptr,
+ nullptr,
+#if WPE_FDO_CHECK_VERSION(1,7,0)
+ // export_shm_buffer
+ [](void* data, struct wpe_fdo_shm_exported_buffer* buffer)
+ {
+ auto& backend = *static_cast<HeadlessViewBackend*>(data);
+ backend.m_update.pending = true;
+
+ backend.updateSnapshot(buffer);
+ wpe_view_backend_exportable_fdo_dispatch_release_shm_exported_buffer(backend.m_exportable, buffer);
+ },
+#else
+ nullptr,
+#endif
+ nullptr,
+ nullptr,
+ };
+ m_exportable = wpe_view_backend_exportable_fdo_create(&exportableClient, this, width, height);
+
+ addActivityState(wpe_view_activity_state_visible | wpe_view_activity_state_focused | wpe_view_activity_state_in_window);
+
+ {
+ uint32_t stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m_width);
+ uint8_t* buffer = new uint8_t[stride * m_height];
+ memset(buffer, 0, stride * m_height);
+
+ m_snapshot = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_ARGB32,
+ m_width, m_height, stride);
+
+ cairo_surface_set_user_data(m_snapshot, &s_bufferKey, buffer,
+ [](void* data) {
+ auto* buffer = static_cast<uint8_t*>(data);
+ delete[] buffer;
+ });
+ cairo_surface_mark_dirty(m_snapshot);
+ }
+
+#if WPE_CHECK_VERSION(1, 11, 1)
+ wpe_view_backend_set_fullscreen_handler(backend(), onDOMFullScreenRequest, this);
+#endif
+
+ m_update.source = g_timeout_source_new(G_USEC_PER_SEC / 60000);
+ g_source_set_callback(m_update.source, [](gpointer data) -> gboolean {
+ static_cast<HeadlessViewBackend*>(data)->vsync();
+ return TRUE;
+ }, this, nullptr);
+ g_source_set_priority(m_update.source, G_PRIORITY_DEFAULT);
+ g_source_attach(m_update.source, g_main_context_default());
+}
+
+HeadlessViewBackend::~HeadlessViewBackend()
+{
+ if (m_update.source) {
+ g_source_destroy(m_update.source);
+ g_source_unref(m_update.source);
+ }
+
+ if (m_snapshot)
+ cairo_surface_destroy(m_snapshot);
+
+ if (m_exportable)
+ wpe_view_backend_exportable_fdo_destroy(m_exportable);
+}
+
+struct wpe_view_backend* HeadlessViewBackend::backend() const
+{
+ if (m_exportable)
+ return wpe_view_backend_exportable_fdo_get_view_backend(m_exportable);
+ return nullptr;
+}
+
+PlatformImage HeadlessViewBackend::snapshot()
+{
+ return cairo_surface_reference(m_snapshot);
+}
+
+void HeadlessViewBackend::updateSnapshot(PlatformBuffer exportedBuffer)
+{
+#if WPE_FDO_CHECK_VERSION(1,7,0)
+ struct wl_shm_buffer* shmBuffer = wpe_fdo_shm_exported_buffer_get_shm_buffer(exportedBuffer);
+ {
+ auto format = wl_shm_buffer_get_format(shmBuffer);
+ if (format != WL_SHM_FORMAT_ARGB8888 && format != WL_SHM_FORMAT_XRGB8888)
+ return;
+ }
+
+ uint32_t bufferStride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m_width);
+ uint8_t* buffer = new uint8_t[bufferStride * m_height];
+ memset(buffer, 0, bufferStride * m_height);
+
+ {
+ uint32_t width = std::min<uint32_t>(m_width, std::max(0, wl_shm_buffer_get_width(shmBuffer)));
+ uint32_t height = std::min<uint32_t>(m_height, std::max(0, wl_shm_buffer_get_height(shmBuffer)));
+ uint32_t stride = std::max(0, wl_shm_buffer_get_stride(shmBuffer));
+
+ wl_shm_buffer_begin_access(shmBuffer);
+ auto* data = ""
+
+ for (uint32_t y = 0; y < height; ++y) {
+ for (uint32_t x = 0; x < width; ++x) {
+ buffer[bufferStride * y + 4 * x + 0] = data[stride * y + 4 * x + 0];
+ buffer[bufferStride * y + 4 * x + 1] = data[stride * y + 4 * x + 1];
+ buffer[bufferStride * y + 4 * x + 2] = data[stride * y + 4 * x + 2];
+ buffer[bufferStride * y + 4 * x + 3] = data[stride * y + 4 * x + 3];
+ }
+ }
+
+ wl_shm_buffer_end_access(shmBuffer);
+ }
+
+ if (m_snapshot)
+ cairo_surface_destroy(m_snapshot);
+
+ m_snapshot = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_ARGB32,
+ m_width, m_height, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m_width));
+
+ static cairo_user_data_key_t bufferKey;
+ cairo_surface_set_user_data(m_snapshot, &bufferKey, buffer,
+ [](void* data) {
+ auto* buffer = static_cast<uint8_t*>(data);
+ delete[] buffer;
+ });
+ cairo_surface_mark_dirty(m_snapshot);
+#else
+ (void)exportedBuffer;
+#endif
+}
+
+void HeadlessViewBackend::vsync()
+{
+#if WPE_FDO_CHECK_VERSION(1,7,0)
+ if (m_update.pending)
+ wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
+#endif
+ m_update.pending = false;
+}
+
+} // namespace WPEToolingBackends
Copied: trunk/Tools/wpe/backends/fdo/WindowViewBackend.cpp (from rev 288813, trunk/Tools/wpe/backends/WindowViewBackend.cpp) (0 => 288814)
--- trunk/Tools/wpe/backends/fdo/WindowViewBackend.cpp (rev 0)
+++ trunk/Tools/wpe/backends/fdo/WindowViewBackend.cpp 2022-01-31 15:14:28 UTC (rev 288814)
@@ -0,0 +1,984 @@
+/*
+ * 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 <mutex>
+#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>
+#include <wpe/fdo-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 {
+
+struct WaylandEGLConnection {
+ struct wl_display* display { nullptr };
+ EGLDisplay eglDisplay { EGL_NO_DISPLAY };
+
+ static const WaylandEGLConnection& singleton()
+ {
+ static std::once_flag s_onceFlag;
+ static WaylandEGLConnection s_connection;
+ std::call_once(s_onceFlag,
+ [] {
+ s_connection.display = wl_display_connect(nullptr);
+ if (!s_connection.display) {
+ g_warning("WaylandEGLConnection: Could not connect to Wayland Display");
+ return;
+ }
+
+ EGLDisplay eglDisplay = eglGetDisplay(s_connection.display);
+ if (eglDisplay == EGL_NO_DISPLAY) {
+ g_warning("WaylandEGLConnection: No EGL Display available in this connection");
+ return;
+ }
+
+ if (!eglInitialize(eglDisplay, nullptr, nullptr) || !eglBindAPI(EGL_OPENGL_ES_API)) {
+ g_warning("WaylandEGLConnection: Failed to initialize and bind the EGL Display");
+ return;
+ }
+
+ s_connection.eglDisplay = eglDisplay;
+ wpe_fdo_initialize_for_egl_display(s_connection.eglDisplay);
+ });
+
+ return s_connection;
+ }
+};
+
+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, 5));
+ },
+ // 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;
+ window.m_seatData.pointer.modifiers = 0;
+ }
+ },
+ // 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.modifiers() };
+ 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;
+
+ uint32_t modifier = 0;
+ switch (button) {
+ case 1:
+ modifier = wpe_input_pointer_modifier_button1;
+ break;
+ case 2:
+ modifier = wpe_input_pointer_modifier_button2;
+ break;
+ case 3:
+ modifier = wpe_input_pointer_modifier_button3;
+ break;
+ case 4:
+ modifier = wpe_input_pointer_modifier_button4;
+ break;
+ case 5:
+ modifier = wpe_input_pointer_modifier_button5;
+ break;
+ default:
+ break;
+ }
+
+ if (state)
+ window.m_seatData.pointer.modifiers |= modifier;
+ else
+ window.m_seatData.pointer.modifiers &= ~modifier;
+
+ 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.modifiers() };
+ window.dispatchInputPointerEvent(&event);
+ }
+ },
+ // axis
+ [](void* data, struct wl_pointer*, uint32_t time, uint32_t axis, wl_fixed_t value)
+ {
+ if (axis != WL_POINTER_AXIS_HORIZONTAL_SCROLL && axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
+ return;
+
+ 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.modifiers() };
+ if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL && window.m_seatData.axis_discrete.horizontal)
+ event.value = window.m_seatData.axis_discrete.horizontal;
+ else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL && window.m_seatData.axis_discrete.vertical)
+ event.value = window.m_seatData.axis_discrete.vertical;
+#if WPE_CHECK_VERSION(1, 5, 0)
+ else {
+ struct wpe_input_axis_2d_event event2d = { event, 0, 0 };
+ event2d.base.type = static_cast<wpe_input_axis_event_type>(wpe_input_axis_event_type_mask_2d | wpe_input_axis_event_type_motion_smooth);
+
+ if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
+ event2d.x_axis = wl_fixed_to_double(value);
+ else
+ event2d.y_axis = -wl_fixed_to_double(value);
+
+ window.dispatchInputAxisEvent(&event2d.base);
+ return;
+ }
+#endif
+ window.dispatchInputAxisEvent(&event);
+ window.m_seatData.axis_discrete.horizontal = window.m_seatData.axis_discrete.vertical = 0;
+ }
+ },
+ // frame
+ [](void*, struct wl_pointer*) { },
+ // axis_source
+ [](void*, struct wl_pointer*, uint32_t) { },
+ // axis_stop
+ [](void* data, struct wl_pointer*, uint32_t time, uint32_t axis)
+ {
+ if (axis != WL_POINTER_AXIS_HORIZONTAL_SCROLL && axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
+ return;
+
+ 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, 0, window.modifiers() };
+ window.dispatchInputAxisEvent(&event);
+ }
+ },
+ // axis_discrete
+ [](void* data, struct wl_pointer*, uint32_t axis, int32_t discrete) {
+ auto& window = *static_cast<WindowViewBackend*>(data);
+ switch (axis) {
+ case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
+ window.m_seatData.axis_discrete.horizontal = discrete;
+ break;
+ case WL_POINTER_AXIS_VERTICAL_SCROLL:
+ window.m_seatData.axis_discrete.vertical = -discrete;
+ break;
+ }
+ },
+};
+
+const struct wl_keyboard_listener WindowViewBackend::s_keyboardListener = {
+ // keymap
+ [](void*, struct wl_keyboard*, uint32_t format, int fd, uint32_t size)
+ {
+ 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 = wpe_input_xkb_context_get_default();
+ auto* keymap = xkb_keymap_new_from_string(wpe_input_xkb_context_get_context(xkb), static_cast<char*>(mapping),
+ XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ munmap(mapping, size);
+ close(fd);
+
+ wpe_input_xkb_context_set_keymap(xkb, keymap);
+ xkb_keymap_unref(keymap);
+ },
+ // 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;
+
+ auto* keymap = wpe_input_xkb_context_get_keymap(wpe_input_xkb_context_get_default());
+
+ 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
+ && keymap && xkb_keymap_key_repeats(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& keyboard = static_cast<WindowViewBackend*>(data)->m_seatData.keyboard;
+ keyboard.modifiers = wpe_input_xkb_context_get_modifiers(wpe_input_xkb_context_get_default(), depressedMods, latchedMods, lockedMods, group);
+ },
+ // 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_touch_listener WindowViewBackend::s_touchListener = {
+ // down
+ [](void* data, struct wl_touch*, uint32_t, uint32_t time, struct wl_surface* surface, int32_t id, wl_fixed_t x, wl_fixed_t y)
+ {
+ auto& window = *static_cast<WindowViewBackend*>(data);
+ if (window.m_surface != surface || id < 0 || id >= 10)
+ return;
+
+ auto& seatData = window.m_seatData;
+ seatData.touch.tracking = true;
+ struct wpe_input_touch_event_raw rawEvent = { wpe_input_touch_event_type_down,
+ time, id, wl_fixed_to_int(x), wl_fixed_to_int(y) };
+ memcpy(&seatData.touch.points[id], &rawEvent, sizeof(struct wpe_input_touch_event_raw));
+
+ struct wpe_input_touch_event event = { seatData.touch.points, 10,
+ rawEvent.type, rawEvent.id, rawEvent.time, window.modifiers() };
+ window.dispatchInputTouchEvent(&event);
+ },
+ // up
+ [](void* data, struct wl_touch*, uint32_t, uint32_t time, int32_t id)
+ {
+ auto& window = *static_cast<WindowViewBackend*>(data);
+ auto& seatData = window.m_seatData;
+ if (!seatData.touch.tracking || id < 0 || id >= 10)
+ return;
+
+ seatData.touch.tracking = false;
+
+ struct wpe_input_touch_event_raw rawEvent = { wpe_input_touch_event_type_up,
+ time, id, seatData.touch.points[id].x, seatData.touch.points[id].y };
+ memcpy(&seatData.touch.points[id], &rawEvent, sizeof(struct wpe_input_touch_event_raw));
+
+ struct wpe_input_touch_event event = { seatData.touch.points, 10,
+ rawEvent.type, rawEvent.id, rawEvent.time, window.modifiers() };
+ window.dispatchInputTouchEvent(&event);
+
+ memset(&seatData.touch.points[id], 0x00, sizeof(struct wpe_input_touch_event_raw));
+ },
+ // motion
+ [](void* data, struct wl_touch*, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y)
+ {
+ auto& window = *static_cast<WindowViewBackend*>(data);
+ auto& seatData = window.m_seatData;
+ if (!seatData.touch.tracking || id < 0 || id >= 10)
+ return;
+
+ struct wpe_input_touch_event_raw rawEvent = { wpe_input_touch_event_type_motion,
+ time, id, wl_fixed_to_int(x), wl_fixed_to_int(y) };
+ memcpy(&seatData.touch.points[id], &rawEvent, sizeof(struct wpe_input_touch_event_raw));
+
+ struct wpe_input_touch_event event = { seatData.touch.points, 10,
+ rawEvent.type, rawEvent.id, rawEvent.time, window.modifiers() };
+ window.dispatchInputTouchEvent(&event);
+ },
+ // frame
+ [](void*, struct wl_touch*) { },
+ // cancel
+ [](void*, struct wl_touch*) { },
+ // shape
+ [](void*, struct wl_touch*, int32_t, wl_fixed_t, wl_fixed_t) { },
+ // orientation
+ [](void*, struct wl_touch*, int32_t, wl_fixed_t) { },
+};
+
+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;
+ }
+
+ // WL_SEAT_CAPABILITY_TOUCH
+ const bool hasTouchCap = capabilities & WL_SEAT_CAPABILITY_TOUCH;
+ if (hasTouchCap && !seatData.touch.object) {
+ seatData.touch.object = wl_seat_get_touch(seat);
+ wl_touch_add_listener(seatData.touch.object, &s_touchListener, window);
+ }
+ if (!hasTouchCap && seatData.touch.object) {
+ wl_touch_destroy(seatData.touch.object);
+ seatData.touch.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* data, struct zxdg_toplevel_v6*, int32_t width, int32_t height, struct wl_array* states)
+ {
+ auto& window = *static_cast<WindowViewBackend*>(data);
+ window.resize(std::max(0, width), std::max(0, height));
+
+ bool isFocused = false;
+ bool isFullscreen = false;
+ // FIXME: It would be nice if the following loop could use
+ // wl_array_for_each, but at the time of writing it relies on
+ // GCC specific extension to work properly:
+ // https://gitlab.freedesktop.org/wayland/wayland/issues/34
+ uint32_t* pos = static_cast<uint32_t*>(states->data);
+ uint32_t* end = static_cast<uint32_t*>(states->data) + states->size;
+
+ for (; pos < end; pos++) {
+ uint32_t state = *pos;
+
+ switch (state) {
+ case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED:
+ isFocused = true;
+ break;
+ case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN:
+ isFullscreen = true;
+ break;
+ case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED:
+ case ZXDG_TOPLEVEL_V6_STATE_RESIZING:
+ default:
+ break;
+ }
+ }
+
+ if (isFocused)
+ window.addActivityState(wpe_view_activity_state_focused);
+ else
+ window.removeActivityState(wpe_view_activity_state_focused);
+
+ if (window.m_is_fullscreen != isFullscreen)
+ window.onFullscreenChanged(isFullscreen);
+ },
+ // close
+ [](void* data, struct zxdg_toplevel_v6*)
+ {
+ auto& window = *static_cast<WindowViewBackend*>(data);
+ window.removeActivityState(wpe_view_activity_state_visible | wpe_view_activity_state_focused | wpe_view_activity_state_in_window);
+ },
+};
+
+#if WPE_CHECK_VERSION(1, 11, 1)
+
+bool WindowViewBackend::onDOMFullscreenRequest(void* data, bool fullscreen)
+{
+ auto& window = *static_cast<WindowViewBackend*>(data);
+ if (window.m_waiting_fullscreen_notify)
+ return false;
+
+ if (fullscreen == window.m_is_fullscreen) {
+ // Handle situations where DOM fullscreen requests are mixed with system fullscreen commands (e.g F11)
+ window.dispatchFullscreenEvent();
+ return true;
+ }
+
+ window.m_waiting_fullscreen_notify = true;
+ if (fullscreen)
+ zxdg_toplevel_v6_set_fullscreen(window.m_xdgToplevel, nullptr);
+ else
+ zxdg_toplevel_v6_unset_fullscreen(window.m_xdgToplevel);
+
+ return true;
+}
+
+void WindowViewBackend::dispatchFullscreenEvent()
+{
+ if (m_is_fullscreen)
+ wpe_view_backend_dispatch_did_enter_fullscreen(backend());
+ else
+ wpe_view_backend_dispatch_did_exit_fullscreen(backend());
+}
+
+void WindowViewBackend::onFullscreenChanged(bool fullscreen)
+{
+ bool wasRequestedFromDOM = m_waiting_fullscreen_notify;
+ m_waiting_fullscreen_notify= false;
+ m_is_fullscreen = fullscreen;
+
+ if (!fullscreen && !wasRequestedFromDOM)
+ wpe_view_backend_dispatch_request_exit_fullscreen(backend());
+ else if (wasRequestedFromDOM)
+ dispatchFullscreenEvent();
+}
+
+#else
+
+void WindowViewBackend::onFullscreenChanged(bool fullscreen)
+{
+ m_is_fullscreen = fullscreen;
+}
+
+#endif // WPE_CHECK_VERSION(1, 11, 1)
+
+WindowViewBackend::WindowViewBackend(uint32_t width, uint32_t height)
+ : ViewBackend(width, height)
+{
+ m_initialSize.width = width;
+ m_initialSize.height = height;
+
+ auto& connection = WaylandEGLConnection::singleton();
+ if (!connection.display) {
+ g_warning("WindowViewBackend: No Wayland EGL connection available");
+ return;
+ }
+
+ if (connection.eglDisplay == EGL_NO_DISPLAY || !initialize(connection.eglDisplay)) {
+ g_warning("WindowViewBackend: Could not initialize EGL display");
+ return;
+ }
+
+ {
+ auto* registry = wl_display_get_registry(connection.display);
+ wl_registry_add_listener(registry, &s_registryListener, this);
+ wl_display_roundtrip(connection.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_eventSource = g_source_new(&EventSource::sourceFuncs, sizeof(EventSource));
+ {
+ auto& source = *reinterpret_cast<EventSource*>(m_eventSource);
+ source.display = connection.display;
+
+ source.pfd.fd = wl_display_get_fd(connection.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_DEFAULT);
+ 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, this);
+ zxdg_toplevel_v6_set_title(m_xdgToplevel, "WPE");
+ wl_surface_commit(m_surface);
+ addActivityState(wpe_view_activity_state_visible | wpe_view_activity_state_in_window);
+ }
+ }
+
+ m_eglWindow = wl_egl_window_create(m_surface, m_width, m_height);
+
+ auto createPlatformWindowSurface =
+ reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"));
+ m_eglSurface = createPlatformWindowSurface(connection.eglDisplay, m_eglConfig, m_eglWindow, nullptr);
+ if (!m_eglSurface) {
+ g_warning("WindowViewBackend: Could not create EGL platform window surface");
+ return;
+ }
+
+ if (!eglMakeCurrent(connection.eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) {
+ g_warning("WindowViewBackend: Could not make EGL surface current");
+ return;
+ }
+
+ 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");
+ }
+
+ createViewTexture();
+}
+
+WindowViewBackend::~WindowViewBackend()
+{
+ auto& connection = WaylandEGLConnection::singleton();
+
+ 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_eglSurface)
+ eglDestroySurface(connection.eglDisplay, m_eglSurface);
+
+ if (m_display)
+ wl_display_disconnect(m_display);
+
+ deinitialize(connection.eglDisplay);
+}
+
+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_committedImage)
+ wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(window.m_exportable, window.m_committedImage);
+ window.m_committedImage = nullptr;
+ }
+};
+
+struct wpe_view_backend* WindowViewBackend::backend() const
+{
+ return m_exportable ? wpe_view_backend_exportable_fdo_get_view_backend(m_exportable) : nullptr;
+}
+
+void WindowViewBackend::createViewTexture()
+{
+ 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);
+}
+
+void WindowViewBackend::resize(uint32_t width, uint32_t height)
+{
+ if (!width)
+ width = m_initialSize.width;
+ if (!height)
+ height = m_initialSize.height;
+
+ if (width == m_width && height == m_height)
+ return;
+
+ m_width = width;
+ m_height = height;
+
+ wl_egl_window_resize(m_eglWindow, m_width, m_height, 0, 0);
+ wpe_view_backend_dispatch_set_size(backend(), m_width, m_height);
+
+ if (m_viewTexture)
+ glDeleteTextures(1, &m_viewTexture);
+ createViewTexture();
+}
+
+bool WindowViewBackend::initialize(EGLDisplay 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(eglDisplay, nullptr, 0, &count) || count < 1)
+ return false;
+
+ EGLConfig* configs = g_new0(EGLConfig, count);
+ EGLint matched = 0;
+ if (eglChooseConfig(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(eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
+ if (!m_eglContext)
+ return false;
+
+ static struct wpe_view_backend_exportable_fdo_egl_client exportableClient = {
+ // export_egl_image
+ nullptr,
+ // export_fdo_egl_image
+ [](void* data, struct wpe_fdo_egl_exported_image* image)
+ {
+ static_cast<WindowViewBackend*>(data)->displayBuffer(image);
+ },
+#if WPE_FDO_CHECK_VERSION(1, 5, 0)
+ // export_shm_buffer
+ [](void* data, struct wpe_fdo_shm_exported_buffer* buffer)
+ {
+ static_cast<WindowViewBackend*>(data)->displayBuffer(buffer);
+ },
+ // padding
+ nullptr, nullptr
+#else
+ // padding
+ nullptr, nullptr, nullptr
+#endif
+
+ };
+ m_exportable = wpe_view_backend_exportable_fdo_egl_create(&exportableClient, this, m_width, m_height);
+
+#if WPE_CHECK_VERSION(1, 11, 1)
+ wpe_view_backend_set_fullscreen_handler(backend(), onDOMFullscreenRequest, this);
+#endif
+
+ initializeAccessibility();
+
+ return true;
+}
+
+void WindowViewBackend::deinitialize(EGLDisplay eglDisplay)
+{
+ m_inputClient = nullptr;
+
+ if (m_eglContext)
+ eglDestroyContext(eglDisplay, m_eglContext);
+
+ if (m_exportable)
+ wpe_view_backend_exportable_fdo_destroy(m_exportable);
+}
+
+void WindowViewBackend::displayBuffer(struct wpe_fdo_egl_exported_image* image)
+{
+ if (!m_eglContext)
+ return;
+
+ auto& connection = WaylandEGLConnection::singleton();
+ eglMakeCurrent(connection.eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
+
+ glViewport(0, 0, m_width, m_height);
+ glClearColor(1, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glUseProgram(m_program);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, m_viewTexture);
+ imageTargetTexture2DOES(GL_TEXTURE_2D, wpe_fdo_egl_exported_image_get_egl_image(image));
+ glUniform1i(m_textureUniform, 0);
+
+ m_committedImage = 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(connection.eglDisplay, m_eglSurface);
+}
+
+#if WPE_FDO_CHECK_VERSION(1, 5, 0)
+void WindowViewBackend::displayBuffer(struct wpe_fdo_shm_exported_buffer*)
+{
+ g_warning("WindowViewBackend: cannot yet handle wpe_fdo_shm_exported_buffer.");
+}
+#endif
+
+void WindowViewBackend::handleKeyEvent(uint32_t key, uint32_t state, uint32_t time)
+{
+ uint32_t keysym = wpe_input_xkb_context_get_key_code(wpe_input_xkb_context_get_default(), key, state == WL_KEYBOARD_KEY_STATE_PRESSED);
+ if (!keysym)
+ return;
+
+ if (m_seatData.keyboard.target) {
+ struct wpe_input_keyboard_event event = { time, keysym, key, !!state, modifiers() };
+ dispatchInputKeyboardEvent(&event);
+ }
+}
+
+uint32_t WindowViewBackend::modifiers() const
+{
+ uint32_t mask = m_seatData.keyboard.modifiers;
+ if (m_seatData.pointer.object)
+ mask |= m_seatData.pointer.modifiers;
+ return mask;
+}
+
+} // namespace WPEToolingBackends
Copied: trunk/Tools/wpe/backends/fdo/WindowViewBackend.h (from rev 288813, trunk/Tools/wpe/backends/WindowViewBackend.h) (0 => 288814)
--- trunk/Tools/wpe/backends/fdo/WindowViewBackend.h (rev 0)
+++ trunk/Tools/wpe/backends/fdo/WindowViewBackend.h 2022-01-31 15:14:28 UTC (rev 288814)
@@ -0,0 +1,157 @@
+/*
+ * 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 <wpe/fdo.h>
+
+typedef void* EGLConfig;
+typedef void* EGLContext;
+typedef void* EGLDisplay;
+typedef void* EGLSurface;
+typedef struct wl_egl_window *EGLNativeWindowType;
+
+// 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
+
+struct wpe_fdo_egl_exported_image;
+
+namespace WPEToolingBackends {
+
+class WindowViewBackend final : public ViewBackend {
+public:
+ WindowViewBackend(uint32_t width, uint32_t height);
+ virtual ~WindowViewBackend();
+
+ struct wpe_view_backend* backend() const override;
+
+private:
+ void createViewTexture();
+ void resize(uint32_t width, uint32_t height);
+
+ bool initialize(EGLDisplay);
+ void deinitialize(EGLDisplay);
+
+ void displayBuffer(struct wpe_fdo_egl_exported_image*);
+#if WPE_FDO_CHECK_VERSION(1, 5, 0)
+ void displayBuffer(struct wpe_fdo_shm_exported_buffer*);
+#endif
+
+ 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_touch_listener s_touchListener;
+ 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;
+
+#if WPE_CHECK_VERSION(1, 11, 1)
+ static bool onDOMFullscreenRequest(void* data, bool fullscreen);
+ void dispatchFullscreenEvent();
+#endif
+ void onFullscreenChanged(bool fullscreen);
+
+ void handleKeyEvent(uint32_t key, uint32_t state, uint32_t time);
+ uint32_t modifiers() const;
+
+ 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 };
+ uint32_t modifiers { 0 };
+ } pointer;
+
+ struct {
+ struct wl_keyboard* object { nullptr };
+ struct wl_surface* target { nullptr };
+ uint32_t modifiers { 0 };
+ } keyboard;
+
+ struct {
+ struct wl_touch* object { nullptr };
+ struct wpe_input_touch_event_raw points[10];
+ bool tracking { false };
+ } touch;
+
+ struct {
+ int32_t horizontal { 0 };
+ int32_t vertical { 0 };
+ } axis_discrete;
+
+ 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 {
+ uint32_t width;
+ uint32_t height;
+ } m_initialSize;
+
+ struct wpe_view_backend_exportable_fdo* m_exportable { nullptr };
+
+ 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 };
+ EGLContext m_eglContext { nullptr };
+ EGLConfig m_eglConfig;
+ EGLSurface m_eglSurface { nullptr };
+ unsigned m_program { 0 };
+ unsigned m_textureUniform { 0 };
+ unsigned m_viewTexture { 0 };
+ struct wpe_fdo_egl_exported_image* m_committedImage { nullptr };
+ bool m_is_fullscreen { false };
+#if WPE_CHECK_VERSION(1, 11, 1)
+ bool m_waiting_fullscreen_notify { false };
+#endif
+};
+
+} // WPEToolingBackends