blob: 13fd37aeff2b6535d54f314c416e1cb868e3e825 [file] [log] [blame]
/*
* Copyright (C) 2018, 2019 Igalia S.L
* Copyright (C) 2018, 2019 Zodiac Inflight Innovations
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "WPEQtViewBackend.h"
#include "WPEQtView.h"
#include <QGuiApplication>
#include <QOpenGLFunctions>
static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC imageTargetTexture2DOES;
std::unique_ptr<WPEQtViewBackend> WPEQtViewBackend::create(const QSizeF& size, QPointer<QOpenGLContext> context, EGLDisplay eglDisplay, QPointer<WPEQtView> view)
{
if (!context || !view)
return nullptr;
if (eglDisplay == EGL_NO_DISPLAY)
return nullptr;
eglInitialize(eglDisplay, nullptr, nullptr);
if (!eglBindAPI(EGL_OPENGL_ES_API) || !wpe_fdo_initialize_for_egl_display(eglDisplay))
return nullptr;
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 nullptr;
EGLConfig eglConfig;
EGLint matched = 0;
EGLContext eglContext = nullptr;
if (eglChooseConfig(eglDisplay, configAttributes, &eglConfig, 1, &matched) && !!matched) {
static const EGLint contextAttributes[3] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
eglContext = eglCreateContext(eglDisplay, eglConfig, nullptr, contextAttributes);
}
if (!eglContext)
return nullptr;
return makeUnique<WPEQtViewBackend>(size, eglDisplay, eglContext, context, view);
}
WPEQtViewBackend::WPEQtViewBackend(const QSizeF& size, EGLDisplay display, EGLContext eglContext, QPointer<QOpenGLContext> context, QPointer<WPEQtView> view)
: m_eglDisplay(display)
, m_eglContext(eglContext)
, m_view(view)
, m_size(size)
{
wpe_loader_init("libWPEBackend-fdo-1.0.so");
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";
QOpenGLFunctions* glFunctions = context->functions();
GLuint vertexShader = glFunctions->glCreateShader(GL_VERTEX_SHADER);
glFunctions->glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glFunctions->glCompileShader(vertexShader);
GLuint fragmentShader = glFunctions->glCreateShader(GL_FRAGMENT_SHADER);
glFunctions->glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glFunctions->glCompileShader(fragmentShader);
m_program = glFunctions->glCreateProgram();
glFunctions->glAttachShader(m_program, vertexShader);
glFunctions->glAttachShader(m_program, fragmentShader);
glFunctions->glLinkProgram(m_program);
glFunctions->glBindAttribLocation(m_program, 0, "pos");
glFunctions->glBindAttribLocation(m_program, 1, "texture");
m_textureUniform = glFunctions->glGetUniformLocation(m_program, "u_texture");
static struct wpe_view_backend_exportable_fdo_egl_client exportableClient = {
// export_egl_image
nullptr,
[](void* data, struct wpe_fdo_egl_exported_image* image)
{
static_cast<WPEQtViewBackend*>(data)->displayImage(image);
},
// padding
nullptr, nullptr, nullptr
};
m_exportable = wpe_view_backend_exportable_fdo_egl_create(&exportableClient, this, m_size.width(), m_size.height());
wpe_view_backend_add_activity_state(backend(), wpe_view_activity_state_visible | wpe_view_activity_state_focused | wpe_view_activity_state_in_window);
m_surface.setFormat(context->format());
m_surface.create();
}
WPEQtViewBackend::~WPEQtViewBackend()
{
wpe_view_backend_exportable_fdo_destroy(m_exportable);
eglDestroyContext(m_eglDisplay, m_eglContext);
}
void WPEQtViewBackend::resize(const QSizeF& newSize)
{
if (!newSize.isValid())
return;
m_size = newSize;
wpe_view_backend_dispatch_set_size(backend(), m_size.width(), m_size.height());
}
GLuint WPEQtViewBackend::texture(QOpenGLContext* context)
{
if (!m_lockedImage || !hasValidSurface())
return 0;
context->makeCurrent(&m_surface);
QOpenGLFunctions* glFunctions = context->functions();
if (!m_textureId) {
glFunctions->glGenTextures(1, &m_textureId);
glFunctions->glBindTexture(GL_TEXTURE_2D, m_textureId);
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFunctions->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_size.width(), m_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFunctions->glBindTexture(GL_TEXTURE_2D, 0);
}
glFunctions->glClearColor(1, 0, 0, 1);
glFunctions->glClear(GL_COLOR_BUFFER_BIT);
glFunctions->glUseProgram(m_program);
glFunctions->glActiveTexture(GL_TEXTURE0);
glFunctions->glBindTexture(GL_TEXTURE_2D, m_textureId);
imageTargetTexture2DOES(GL_TEXTURE_2D, wpe_fdo_egl_exported_image_get_egl_image(m_lockedImage));
glFunctions->glUniform1i(m_textureUniform, 0);
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 },
};
glFunctions->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glFunctions->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texturePos);
glFunctions->glEnableVertexAttribArray(0);
glFunctions->glEnableVertexAttribArray(1);
glFunctions->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glFunctions->glDisableVertexAttribArray(0);
glFunctions->glDisableVertexAttribArray(1);
wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(m_exportable, m_lockedImage);
m_lockedImage = nullptr;
return m_textureId;
}
void WPEQtViewBackend::displayImage(struct wpe_fdo_egl_exported_image* image)
{
RELEASE_ASSERT(!m_lockedImage);
m_lockedImage = image;
if (m_view)
m_view->triggerUpdate();
}
uint32_t WPEQtViewBackend::modifiers() const
{
uint32_t mask = m_keyboardModifiers;
if (m_mouseModifiers)
mask |= m_mouseModifiers;
return mask;
}
void WPEQtViewBackend::dispatchHoverEnterEvent(QHoverEvent*)
{
m_hovering = true;
m_mouseModifiers = 0;
}
void WPEQtViewBackend::dispatchHoverLeaveEvent(QHoverEvent*)
{
m_hovering = false;
}
void WPEQtViewBackend::dispatchHoverMoveEvent(QHoverEvent* event)
{
if (!m_hovering)
return;
uint32_t state = !!m_mousePressedButton;
struct wpe_input_pointer_event wpeEvent = { wpe_input_pointer_event_type_motion,
static_cast<uint32_t>(event->timestamp()),
event->pos().x(), event->pos().y(),
m_mousePressedButton, state, modifiers() };
wpe_view_backend_dispatch_pointer_event(backend(), &wpeEvent);
}
void WPEQtViewBackend::dispatchMousePressEvent(QMouseEvent* event)
{
uint32_t button = 0;
uint32_t modifier = 0;
switch (event->button()) {
case Qt::LeftButton:
button = 1;
modifier = wpe_input_pointer_modifier_button1;
break;
case Qt::RightButton:
button = 2;
modifier = wpe_input_pointer_modifier_button2;
break;
default:
break;
}
m_mousePressedButton = button;
uint32_t state = 1;
m_mouseModifiers |= modifier;
struct wpe_input_pointer_event wpeEvent = { wpe_input_pointer_event_type_button,
static_cast<uint32_t>(event->timestamp()),
event->x(), event->y(), button, state, modifiers() };
wpe_view_backend_dispatch_pointer_event(backend(), &wpeEvent);
}
void WPEQtViewBackend::dispatchMouseReleaseEvent(QMouseEvent* event)
{
uint32_t button = 0;
uint32_t modifier = 0;
switch (event->button()) {
case Qt::LeftButton:
button = 1;
modifier = wpe_input_pointer_modifier_button1;
break;
case Qt::RightButton:
button = 2;
modifier = wpe_input_pointer_modifier_button2;
break;
default:
break;
}
m_mousePressedButton = 0;
uint32_t state = 0;
m_mouseModifiers &= ~modifier;
struct wpe_input_pointer_event wpeEvent = { wpe_input_pointer_event_type_button,
static_cast<uint32_t>(event->timestamp()),
event->x(), event->y(), button, state, modifiers() };
wpe_view_backend_dispatch_pointer_event(backend(), &wpeEvent);
}
void WPEQtViewBackend::dispatchWheelEvent(QWheelEvent* event)
{
QPoint delta = event->angleDelta();
uint32_t axis = delta.y() == event->y();
QPoint numDegrees = delta / 8;
QPoint numSteps = numDegrees / 15;
int32_t length = numSteps.y() ? numSteps.y() : numSteps.x();
struct wpe_input_axis_event wpeEvent = { wpe_input_axis_event_type_motion,
static_cast<uint32_t>(event->timestamp()),
event->x(), event->y(), axis, length, modifiers() };
wpe_view_backend_dispatch_axis_event(backend(), &wpeEvent);
}
void WPEQtViewBackend::dispatchKeyEvent(QKeyEvent* event, bool state)
{
uint32_t keysym = event->nativeVirtualKey();
if (!keysym)
keysym = wpe_input_xkb_context_get_key_code(wpe_input_xkb_context_get_default(), event->key(), state);
uint32_t modifiers = 0;
Qt::KeyboardModifiers qtModifiers = event->modifiers();
if (!qtModifiers)
qtModifiers = QGuiApplication::keyboardModifiers();
if (qtModifiers & Qt::ShiftModifier)
modifiers |= wpe_input_keyboard_modifier_shift;
if (qtModifiers & Qt::ControlModifier)
modifiers |= wpe_input_keyboard_modifier_control;
if (qtModifiers & Qt::MetaModifier)
modifiers |= wpe_input_keyboard_modifier_meta;
if (qtModifiers & Qt::AltModifier)
modifiers |= wpe_input_keyboard_modifier_alt;
struct wpe_input_keyboard_event wpeEvent = { static_cast<uint32_t>(event->timestamp()),
keysym, event->nativeScanCode(), state, modifiers };
wpe_view_backend_dispatch_keyboard_event(backend(), &wpeEvent);
}
void WPEQtViewBackend::dispatchTouchEvent(QTouchEvent* event)
{
wpe_input_touch_event_type eventType;
switch (event->type()) {
case QEvent::TouchBegin:
eventType = wpe_input_touch_event_type_down;
break;
case QEvent::TouchUpdate:
eventType = wpe_input_touch_event_type_motion;
break;
case QEvent::TouchEnd:
eventType = wpe_input_touch_event_type_up;
break;
default:
eventType = wpe_input_touch_event_type_null;
break;
}
int i = 0;
struct wpe_input_touch_event_raw* rawEvents = g_new0(wpe_input_touch_event_raw, event->touchPoints().length());
for (auto& point : event->touchPoints()) {
rawEvents[i] = { eventType, static_cast<uint32_t>(event->timestamp()),
point.id(), static_cast<int32_t>(point.pos().x()), static_cast<int32_t>(point.pos().y()) };
i++;
}
struct wpe_input_touch_event wpeEvent = { rawEvents, static_cast<uint64_t>(i), eventType,
static_cast<int32_t>(rawEvents[0].id),
static_cast<uint32_t>(event->timestamp()), modifiers() };
wpe_view_backend_dispatch_touch_event(backend(), &wpeEvent);
g_free(rawEvents);
}