diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp
index d8da482ecc3..02781f4aa0c 100644
--- a/src/gui/kernel/qopenglcontext.cpp
+++ b/src/gui/kernel/qopenglcontext.cpp
@@ -138,6 +138,22 @@ QOpenGLContext *qt_gl_global_share_context()
application is portable between different platforms. However, if you use
QOpenGLFunctions::glBindFramebuffer(), this is done automatically for you.
+ \warning WebAssembly
+
+ We recommend that only one QOpenGLContext is made current with a QSurface,
+ for the entire lifetime of the QSurface. Should more than once context be used,
+ it is important to understand that multiple QOpenGLContext instances may be
+ backed by the same native context underneath with the WebAssembly platform.
+ Therefore, calling makeCurrent() with the same QSurface on two QOpenGLContext
+ objects may not switch to a different native context in the second call. As
+ a result, any OpenGL state changes done after the second makeCurrent() may
+ alter the state of the first QOpenGLContext as well, as they are all backed
+ by the same native context.
+
+ \note This means that when targeting WebAssembly with existing OpenGL-based
+ Qt code, some porting may be required to cater to these limitations.
+
+
\sa QOpenGLFunctions, QOpenGLBuffer, QOpenGLShaderProgram, QOpenGLFramebufferObject
*/
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
index 07ae1d1cf53..8a4664ec8cd 100644
--- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
+++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
@@ -152,7 +152,13 @@ GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) c
bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface)
{
- if (auto *shareContext = m_qGlContext->shareContext())
+ static bool sentSharingWarning = false;
+ if (!sentSharingWarning && isSharing()) {
+ qWarning() << "The functionality for sharing OpenGL contexts is limited, see documentation";
+ sentSharingWarning = true;
+ }
+
+ if (auto *shareContext = m_qGlContext->shareContext())
return shareContext->makeCurrent(surface->surface());
const auto context = obtainEmscriptenContext(surface);
diff --git a/tests/auto/wasm/selenium/CMakeLists.txt b/tests/auto/wasm/selenium/CMakeLists.txt
index b544c2a0d65..335b6d23d9b 100644
--- a/tests/auto/wasm/selenium/CMakeLists.txt
+++ b/tests/auto/wasm/selenium/CMakeLists.txt
@@ -12,9 +12,23 @@ qt_internal_add_test(tst_qwasmwindow_harness
LIBRARIES
Qt::Core
Qt::Gui
+ Qt::OpenGL
Qt::Widgets
)
+# Resources:
+set(shaders_resource_files
+ "fshader.glsl"
+ "vshader.glsl"
+)
+
+qt6_add_resources(tst_qwasmwindow_harness "shaders"
+ PREFIX
+ "/"
+ FILES
+ ${shaders_resource_files}
+)
+
if(CMAKE_HOST_WIN32)
SET(RUNSHCMD run.bat)
SET(RUNSHARG "NotUsed")
diff --git a/tests/auto/wasm/selenium/fshader.glsl b/tests/auto/wasm/selenium/fshader.glsl
new file mode 100644
index 00000000000..252735f91c0
--- /dev/null
+++ b/tests/auto/wasm/selenium/fshader.glsl
@@ -0,0 +1,21 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifdef GL_ES
+// Set default precision to medium
+precision mediump int;
+precision mediump float;
+#endif
+
+uniform sampler2D texture;
+
+varying vec2 v_texcoord;
+
+//! [0]
+void main()
+{
+ // Set fragment color from texture
+ gl_FragColor = texture2D(texture, v_texcoord);
+}
+//! [0]
+
diff --git a/tests/auto/wasm/selenium/qwasmwindow.py b/tests/auto/wasm/selenium/qwasmwindow.py
index 1932feb4bb9..dbec21f61a7 100644
--- a/tests/auto/wasm/selenium/qwasmwindow.py
+++ b/tests/auto/wasm/selenium/qwasmwindow.py
@@ -13,6 +13,7 @@ from selenium.webdriver.support.expected_conditions import presence_of_element_l
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager
+import time
import unittest
from enum import Enum, auto
@@ -436,7 +437,40 @@ class WidgetTestCase(unittest.TestCase):
self.assertEqual(w1_w1_w1.color_at(0, 0), Color(r=255, g=255, b=0))
- #TODO FIX IN CI
+ def test_opengl_painting(self):
+ screen = Screen(self._driver, ScreenPosition.FIXED,
+ x=0, y=0, width=800, height=800)
+ bottom = Window(parent=screen, rect=Rect(x=0, y=0, width=400, height=400), title='root',opengl=1)
+ bottom.set_background_color(Color(r=255, g=0, b=0))
+ wait_for_animation_frame(self._driver)
+ time.sleep(1)
+
+ self.assertEqual(bottom.window_color_at_0_0(), Color(r=255, g=0, b=0))
+
+ w1 = Window(parent=screen, rect=Rect(x=100, y=100, width=600, height=600), title='w1', opengl=1)
+ w1.set_background_color(Color(r=0, g=255, b=0))
+ wait_for_animation_frame(self._driver)
+ time.sleep(1)
+
+ self.assertEqual(w1.window_color_at_0_0(), Color(r=0, g=255, b=0))
+
+ w1_w1 = Window(parent=screen, rect=Rect(x=100, y=100, width=400, height=400), title='w1_w1', opengl=1)
+ w1_w1.set_parent(w1)
+ w1_w1.set_background_color(Color(r=0, g=0, b=255))
+ wait_for_animation_frame(self._driver)
+ time.sleep(1)
+
+ self.assertEqual(w1_w1.window_color_at_0_0(), Color(r=0, g=0, b=255))
+
+ w1_w1_w1 = Window(parent=screen, rect=Rect(x=100, y=100, width=200, height=200), title='w1_w1_w1', opengl=1)
+ w1_w1_w1.set_parent(w1_w1)
+ w1_w1_w1.set_background_color(Color(r=255, g=255, b=0))
+ wait_for_animation_frame(self._driver)
+ time.sleep(1)
+
+ self.assertEqual(w1_w1_w1.window_color_at_0_0(), Color(r=255, g=255, b=0))
+
+#TODO FIX IN CI
@unittest.skip('Does not work in CI')
def test_keyboard_input(self):
screen = Screen(self._driver, ScreenPosition.FIXED,
@@ -607,8 +641,9 @@ class Widget:
class Window:
- def __init__(self, parent=None, rect=None, title=None, element=None, visible=True):
+ def __init__(self, parent=None, rect=None, title=None, element=None, visible=True, opengl=0):
self.driver = parent.driver
+ self.opengl = opengl
if element is not None:
self.element = element
self.title = element.find_element(
@@ -621,7 +656,7 @@ class Window:
if isinstance(parent, Window):
self.driver.execute_script(
f'''
- instance.createWindow({rect.x}, {rect.y}, {rect.width}, {rect.height}, 'window', '{parent.title}', '{title}');
+ instance.createWindow({rect.x}, {rect.y}, {rect.width}, {rect.height}, 'window', '{parent.title}', '{title}', {opengl});
'''
)
self.screen = parent.screen
@@ -629,7 +664,7 @@ class Window:
assert(isinstance(parent, Screen))
self.driver.execute_script(
f'''
- instance.createWindow({rect.x}, {rect.y}, {rect.width}, {rect.height}, 'screen', '{parent.name}', '{title}');
+ instance.createWindow({rect.x}, {rect.y}, {rect.width}, {rect.height}, 'screen', '{parent.name}', '{title}', {opengl});
'''
)
self.screen = parent
@@ -766,6 +801,16 @@ class Window:
'''
)
+ def window_color_at_0_0(self):
+ color = call_instance_function_arg(self.driver, 'getOpenGLColorAt_0_0', self.title)
+
+ wcol = color[0]
+ r = wcol['r']
+ g = wcol['g']
+ b = wcol['b']
+
+ return Color(r,g,b)
+
def color_at(self, x, y):
raw = self.driver.execute_script(
f'''
diff --git a/tests/auto/wasm/selenium/shaders.qrc b/tests/auto/wasm/selenium/shaders.qrc
new file mode 100644
index 00000000000..bfc4b251116
--- /dev/null
+++ b/tests/auto/wasm/selenium/shaders.qrc
@@ -0,0 +1,6 @@
+
+
+ vshader.glsl
+ fshader.glsl
+
+
diff --git a/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp b/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp
index a54173e058a..365fc74a34e 100644
--- a/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp
+++ b/tests/auto/wasm/selenium/tst_qwasmwindow_harness.cpp
@@ -17,6 +17,10 @@
#include
#include
+#include
+#include
+#include
+
#include
#include
#include
@@ -25,25 +29,58 @@
#include
#include
+class TestWindowBase
+{
+public:
+ virtual ~TestWindowBase() {}
+ virtual void setBackgroundColor(int r, int g, int b) = 0;
+ virtual void setVisible(bool visible) = 0;
+ virtual void setParent(QWindow *parent) = 0;
+ virtual bool close() = 0;
+ virtual QWindow *qWindow() = 0;
+ virtual void opengl_color_at_0_0(int *r, int *g, int *b) = 0;
+};
+
class TestWidget : public QDialog
{
Q_OBJECT
};
-
-class TestWindow : public QRasterWindow
+class TestWindow : public QRasterWindow, public TestWindowBase
{
Q_OBJECT
public:
- void setBackgroundColor(int r, int g, int b)
+ virtual void setBackgroundColor(int r, int g, int b) override final
{
m_backgroundColor = QColor::fromRgb(r, g, b);
update();
}
+ virtual void setVisible(bool visible) override final
+ {
+ QRasterWindow::setVisible(visible);
+ }
+ virtual void setParent(QWindow *parent) override final
+ {
+ QRasterWindow::setParent(parent);
+ }
+ virtual bool close() override final
+ {
+ return QRasterWindow::close();
+ }
+ virtual QWindow *qWindow() override final
+ {
+ return static_cast(this);
+ }
+ virtual void opengl_color_at_0_0(int *r, int *g, int *b) override final
+ {
+ *r = 0;
+ *g = 0;
+ *b = 0;
+ }
private:
- void closeEvent(QCloseEvent *ev) override
+ void closeEvent(QCloseEvent *ev) override final
{
Q_UNUSED(ev);
delete this;
@@ -78,14 +115,238 @@ private:
QColor m_backgroundColor = Qt::white;
};
+class ContextGuard
+{
+public:
+ ContextGuard(QOpenGLContext *context, QSurface *surface) : m_context(context)
+ {
+ m_contextMutex.lock();
+ m_context->makeCurrent(surface);
+ }
+
+ ~ContextGuard()
+ {
+ m_context->doneCurrent();
+ m_contextMutex.unlock();
+ }
+
+private:
+ QOpenGLContext *m_context = nullptr;
+ static std::mutex m_contextMutex;
+};
+
+std::mutex ContextGuard::m_contextMutex;
+
+class TestOpenGLWindow : public QWindow, QOpenGLFunctions, public TestWindowBase
+{
+ Q_OBJECT
+
+public:
+ TestOpenGLWindow()
+ {
+ setSurfaceType(OpenGLSurface);
+ create();
+
+ //
+ // Create the texture in the share context
+ //
+ m_shareContext = std::make_shared();
+ m_shareContext->create();
+
+ {
+ ContextGuard guard(m_shareContext.get(), this);
+ initializeOpenGLFunctions();
+
+ m_shaderProgram = std::make_shared();
+
+ if (!m_shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vshader.glsl")
+ || !m_shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment,
+ ":/fshader.glsl")
+ || !m_shaderProgram->link() || !m_shaderProgram->bind()) {
+
+ qDebug() << " Build problem";
+ qDebug() << "Log " << m_shaderProgram->log();
+
+ m_shaderProgram = nullptr;
+ } else {
+ m_shaderProgram->setUniformValue("texture", 0);
+ }
+
+ //
+ // Texture
+ //
+ glGenTextures(1, &m_TextureId);
+ glBindTexture(GL_TEXTURE_2D, m_TextureId);
+
+ uint8_t pixel[4] = { 255, 255, 255, 128 };
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ const GLfloat triangleData[] = { -1.0, -1.0, 0.0, 0.5, 0.5, 1.0, -1.0, 0.0,
+ 0.5, 0.5, -1.0, 1.0, 0.0, 0.5, 0.5 };
+ const GLushort indices[] = { 0, 1, 2 };
+
+ glGenBuffers(1, &m_vertexBufferId);
+ glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float[5]) * 3, &triangleData, GL_STATIC_DRAW);
+
+ glGenBuffers(1, &m_indexBufferId);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferId);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * 3, indices, GL_STATIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float[5]), 0);
+
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float[5]), (void *)(12));
+ }
+
+ //
+ // We will use the texture in this context
+ //
+ m_context = std::make_shared();
+ m_context->setShareContext(m_shareContext.get());
+ m_context->create();
+
+ {
+ ContextGuard guard(m_context.get(), this);
+ initializeOpenGLFunctions();
+
+ glBindTexture(GL_TEXTURE_2D, m_TextureId);
+ glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferId);
+ m_shaderProgram->bind();
+
+ // Tell OpenGL programmable pipeline how to locate vertex position data
+ const int vertexLocation = m_shaderProgram->attributeLocation("a_position");
+ m_shaderProgram->enableAttributeArray(vertexLocation);
+ m_shaderProgram->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, sizeof(float[5]));
+
+ // Tell OpenGL programmable pipeline how to locate vertex texture coordinate data
+ const int texcoordLocation = m_shaderProgram->attributeLocation("a_texcoord");
+ m_shaderProgram->enableAttributeArray(texcoordLocation);
+ m_shaderProgram->setAttributeBuffer(texcoordLocation, GL_FLOAT, sizeof(float[3]), 2,
+ sizeof(float[5]));
+ }
+
+ renderLater();
+ }
+
+public:
+ virtual void setBackgroundColor(int red, int green, int blue) override final
+ {
+ {
+ ContextGuard guard(m_shareContext.get(), this);
+
+ //
+ // Update texture
+ //
+ const uint8_t pixel[4] = { (uint8_t)red, (uint8_t)green, (uint8_t)blue, 128 };
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+ }
+
+ renderLater();
+ }
+ virtual void setVisible(bool visible) override final { QWindow::setVisible(visible); }
+ virtual void setParent(QWindow *parent) override final { QWindow::setParent(parent); }
+ virtual bool close() override final { return QWindow::close(); }
+ virtual QWindow *qWindow() override final { return static_cast(this); }
+ virtual void opengl_color_at_0_0(int *r, int *g, int *b) override final
+ {
+ ContextGuard guard(m_context.get(), this);
+
+ *r = m_rgba[0];
+ *g = m_rgba[1];
+ *b = m_rgba[2];
+ }
+
+private:
+ bool event(QEvent *event) override final
+ {
+ switch (event->type()) {
+ case QEvent::UpdateRequest:
+ renderNow();
+ return true;
+ default:
+ return QWindow::event(event);
+ }
+ }
+
+ void exposeEvent(QExposeEvent *event) override final
+ {
+ Q_UNUSED(event);
+
+ if (isExposed())
+ renderNow();
+ }
+
+ void closeEvent(QCloseEvent *ev) override final
+ {
+ Q_UNUSED(ev);
+ delete this;
+ }
+
+ void keyPressEvent(QKeyEvent *event) override final
+ {
+ auto data = emscripten::val::object();
+ data.set("type", emscripten::val("keyPress"));
+ data.set("windowId", emscripten::val(winId()));
+ data.set("windowTitle", emscripten::val(title().toStdString()));
+ data.set("key", emscripten::val(event->text().toStdString()));
+ emscripten::val::global("window")["testSupport"].call("reportEvent", std::move(data));
+ }
+
+ void keyReleaseEvent(QKeyEvent *event) override final
+ {
+ auto data = emscripten::val::object();
+ data.set("type", emscripten::val("keyRelease"));
+ data.set("windowId", emscripten::val(winId()));
+ data.set("windowTitle", emscripten::val(title().toStdString()));
+ data.set("key", emscripten::val(event->text().toStdString()));
+ emscripten::val::global("window")["testSupport"].call("reportEvent", std::move(data));
+ }
+ void renderLater() { requestUpdate(); }
+ void renderNow()
+ {
+ qDebug() << " Render now";
+ ContextGuard guard(m_context.get(), this);
+ const auto sz = size();
+ glViewport(0, 0, sz.width(), sz.height());
+
+ glClearColor(1, 1, 1, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Draw triangle using indices from VBO
+ glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, nullptr);
+
+ glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, m_rgba);
+ m_context->swapBuffers(this);
+ }
+
+private:
+ std::shared_ptr m_shaderProgram;
+ GLuint m_vertexBufferId = 0;
+ GLuint m_indexBufferId = 0;
+ GLuint m_TextureId = 0;
+
+ std::shared_ptr m_shareContext;
+ std::shared_ptr m_context;
+ uint8_t m_rgba[4]; // Color at location(0, 0)
+};
+
namespace {
-TestWindow *findWindowByTitle(const std::string &title)
+TestWindowBase *findWindowByTitle(const std::string &title)
{
auto windows = qGuiApp->allWindows();
auto window_it = std::find_if(windows.begin(), windows.end(), [&title](QWindow *window) {
return window->title() == QString::fromLatin1(title);
});
- return window_it == windows.end() ? nullptr : static_cast(*window_it);
+ return window_it == windows.end() ? nullptr : dynamic_cast(*window_it);
}
class WidgetStorage
@@ -280,7 +541,7 @@ void clearWidgets()
}
void createWindow(int x, int y, int w, int h, const std::string &parentType, const std::string &parentId,
- const std::string &title)
+ const std::string &title, bool opengl)
{
QScreen *parentScreen = nullptr;
QWindow *parentWindow = nullptr;
@@ -295,29 +556,38 @@ void createWindow(int x, int y, int w, int h, const std::string &parentType, con
}
parentScreen = *screen_it;
} else if (parentType == "window") {
- auto windows = qGuiApp->allWindows();
- auto window_it = std::find_if(windows.begin(), windows.end(), [&parentId](QWindow *window) {
- return window->title() == QString::fromLatin1(parentId);
- });
- if (window_it == windows.end()) {
- qWarning() << "No such window: " << parentId;
+ auto testWindow = findWindowByTitle(parentId);
+
+ if (!testWindow) {
+ qWarning() << "No parent window: " << parentId;
return;
}
- parentWindow = *window_it;
+ parentWindow = testWindow->qWindow();
parentScreen = parentWindow->screen();
} else {
qWarning() << "Wrong parent type " << parentType;
return;
}
- auto *window = new TestWindow;
-
- window->setFlag(Qt::WindowTitleHint);
- window->setFlag(Qt::WindowMaximizeButtonHint);
- window->setTitle(QString::fromLatin1(title));
- window->setGeometry(x, y, w, h);
- window->setScreen(parentScreen);
- window->setParent(parentWindow);
+ if (opengl) {
+ qDebug() << "Making OpenGL window";
+ auto window = new TestOpenGLWindow;
+ window->setFlag(Qt::WindowTitleHint);
+ window->setFlag(Qt::WindowMaximizeButtonHint);
+ window->setTitle(QString::fromLatin1(title));
+ window->setGeometry(x, y, w, h);
+ window->setScreen(parentScreen);
+ window->setParent(parentWindow);
+ } else {
+ qDebug() << "Making Raster window";
+ auto window = new TestWindow;
+ window->setFlag(Qt::WindowTitleHint);
+ window->setFlag(Qt::WindowMaximizeButtonHint);
+ window->setTitle(QString::fromLatin1(title));
+ window->setGeometry(x, y, w, h);
+ window->setScreen(parentScreen);
+ window->setParent(parentWindow);
+ }
}
void setWindowBackgroundColor(const std::string &title, int r, int g, int b)
@@ -330,7 +600,8 @@ void setWindowBackgroundColor(const std::string &title, int r, int g, int b)
window->setBackgroundColor(r, g, b);
}
-void setWindowVisible(int windowId, bool visible) {
+void setWindowVisible(int windowId, bool visible)
+{
auto windows = qGuiApp->allWindows();
auto window_it = std::find_if(windows.begin(), windows.end(), [windowId](QWindow *window) {
return window->winId() == WId(windowId);
@@ -345,27 +616,54 @@ void setWindowVisible(int windowId, bool visible) {
void setWindowParent(const std::string &windowTitle, const std::string &parentTitle)
{
- QWindow *window = findWindowByTitle(windowTitle);
+ TestWindowBase *window = findWindowByTitle(windowTitle);
if (!window) {
- qWarning() << "Window could not be found " << parentTitle;
+ qWarning() << "Window could not be found " << windowTitle;
return;
}
- QWindow *parent = nullptr;
+ TestWindowBase *parent = nullptr;
if (parentTitle != "none") {
if ((parent = findWindowByTitle(parentTitle)) == nullptr) {
qWarning() << "Parent window could not be found " << parentTitle;
return;
}
}
- window->setParent(parent);
+ window->setParent(parent ? parent->qWindow() : nullptr);
}
bool closeWindow(const std::string &title)
{
- QWindow *window = findWindowByTitle(title);
+ TestWindowBase *window = findWindowByTitle(title);
return window ? window->close() : false;
}
+std::string colorToJs(int r, int g, int b)
+{
+ return
+ "[{"
+ " r: " + std::to_string(r) + ","
+ " g: " + std::to_string(g) + ","
+ " b: " + std::to_string(b) + ""
+ "}]";
+}
+
+void getOpenGLColorAt_0_0(const std::string &windowTitle)
+{
+ TestWindowBase *window = findWindowByTitle(windowTitle);
+ int r = 0;
+ int g = 0;
+ int b = 0;
+
+ if (!window) {
+ qWarning() << "Window could not be found " << windowTitle;
+ } else {
+ window->opengl_color_at_0_0(&r, &g, &b);
+ }
+
+ emscripten::val::global("window").call("getOpenGLColorAt_0_0Callback",
+ emscripten::val(colorToJs(r, g, b)));
+}
+
EMSCRIPTEN_BINDINGS(qwasmwindow)
{
emscripten::function("screenInformation", &screenInformation);
@@ -377,6 +675,8 @@ EMSCRIPTEN_BINDINGS(qwasmwindow)
emscripten::function("closeWindow", &closeWindow);
emscripten::function("setWindowBackgroundColor", &setWindowBackgroundColor);
+ emscripten::function("getOpenGLColorAt_0_0", &getOpenGLColorAt_0_0);
+
emscripten::function("createWidget", &createWidget);
emscripten::function("setWidgetNoFocusShow", &setWidgetNoFocusShow);
emscripten::function("showWidget", &showWidget);
diff --git a/tests/auto/wasm/selenium/vshader.glsl b/tests/auto/wasm/selenium/vshader.glsl
new file mode 100644
index 00000000000..95e2bab607e
--- /dev/null
+++ b/tests/auto/wasm/selenium/vshader.glsl
@@ -0,0 +1,27 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifdef GL_ES
+// Set default precision to medium
+precision mediump int;
+precision mediump float;
+#endif
+
+uniform mat4 mvp_matrix;
+
+attribute vec4 a_position;
+attribute vec2 a_texcoord;
+
+varying vec2 v_texcoord;
+
+//! [0]
+void main()
+{
+ // Calculate vertex position in screen space
+ gl_Position = a_position;
+
+ // Pass texture coordinate to fragment shader
+ // Value will be automatically interpolated to fragments inside polygon faces
+ v_texcoord = a_texcoord;
+}
+//! [0]