Transfer touch event handling to QWasmWindow

Fixes: QTBUG-103498
Change-Id: Iec8b5cfba75131e7ddf855e6b729291950888fd3
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
Reviewed-by: Aleksandr Reviakin <aleksandr.reviakin@qt.io>
This commit is contained in:
Mikolaj Boc 2023-01-27 15:01:34 +01:00
parent 22fc1d08bb
commit da5dc20625
14 changed files with 127 additions and 172 deletions

View File

@ -31,14 +31,6 @@ Q_GUI_EXPORT int qt_defaultDpiX();
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
: QObject(screen), m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this))
{
m_touchDevice = std::make_unique<QPointingDevice>(
"touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
QPointingDevice::PointerType::Finger,
QPointingDevice::Capability::Position | QPointingDevice::Capability::Area
| QPointingDevice::Capability::NormalizedPosition,
10, 0);
QWindowSystemInterface::registerInputDevice(m_touchDevice.get());
QWindowSystemInterface::setSynchronousWindowSystemEvents(true);
}
@ -73,21 +65,6 @@ void QWasmCompositor::destroy()
m_isEnabled = false; // prevent frame() from creating a new m_context
}
void QWasmCompositor::initEventHandlers()
{
constexpr EM_BOOL UseCapture = 1;
const QByteArray screenElementSelector = screen()->eventTargetId().toUtf8();
emscripten_set_touchstart_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&touchCallback);
emscripten_set_touchend_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&touchCallback);
emscripten_set_touchmove_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&touchCallback);
emscripten_set_touchcancel_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&touchCallback);
}
void QWasmCompositor::addWindow(QWasmWindow *window)
{
m_windowStack.pushWindow(window);
@ -273,95 +250,3 @@ QWasmScreen *QWasmCompositor::screen()
{
return static_cast<QWasmScreen *>(parent());
}
int QWasmCompositor::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
{
auto compositor = reinterpret_cast<QWasmCompositor*>(userData);
return static_cast<int>(compositor->processTouch(eventType, touchEvent));
}
bool QWasmCompositor::processTouch(int eventType, const EmscriptenTouchEvent *touchEvent)
{
QList<QWindowSystemInterface::TouchPoint> touchPointList;
touchPointList.reserve(touchEvent->numTouches);
QWindow *targetWindow = nullptr;
qWarning() << Q_FUNC_INFO << "number emTouchPoint:" << touchEvent->numTouches;
for (int i = 0; i < touchEvent->numTouches; i++) {
const EmscriptenTouchPoint *emTouchPoint = &touchEvent->touches[i];
QPointF targetPointInScreenCoords =
screen()->mapFromLocal(QPoint(emTouchPoint->targetX, emTouchPoint->targetY));
targetWindow = screen()->compositor()->windowAt(targetPointInScreenCoords.toPoint(), 5);
if (targetWindow == nullptr)
continue;
QWindowSystemInterface::TouchPoint touchPoint;
touchPoint.area = QRect(0, 0, 8, 8);
touchPoint.id = emTouchPoint->identifier;
touchPoint.pressure = 1.0;
touchPoint.area.moveCenter(targetPointInScreenCoords);
const auto tp = m_pressedTouchIds.constFind(touchPoint.id);
if (tp != m_pressedTouchIds.constEnd())
touchPoint.normalPosition = tp.value();
QPointF pointInTargetWindowCoords = targetWindow->mapFromGlobal(targetPointInScreenCoords);
QPointF normalPosition(pointInTargetWindowCoords.x() / targetWindow->width(),
pointInTargetWindowCoords.y() / targetWindow->height());
const bool stationaryTouchPoint = (normalPosition == touchPoint.normalPosition);
touchPoint.normalPosition = normalPosition;
switch (eventType) {
case EMSCRIPTEN_EVENT_TOUCHSTART:
if (emTouchPoint->isChanged) {
if (tp != m_pressedTouchIds.constEnd()) {
touchPoint.state = (stationaryTouchPoint
? QEventPoint::State::Stationary
: QEventPoint::State::Updated);
} else {
touchPoint.state = QEventPoint::State::Pressed;
}
m_pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
}
break;
case EMSCRIPTEN_EVENT_TOUCHEND:
if (emTouchPoint->isChanged) {
touchPoint.state = QEventPoint::State::Released;
m_pressedTouchIds.remove(touchPoint.id);
}
break;
case EMSCRIPTEN_EVENT_TOUCHMOVE:
if (emTouchPoint->isChanged) {
touchPoint.state = (stationaryTouchPoint
? QEventPoint::State::Stationary
: QEventPoint::State::Updated);
m_pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
}
break;
default:
break;
}
touchPointList.append(touchPoint);
}
QFlags<Qt::KeyboardModifier> keyModifier = KeyboardModifier::getForEvent(*touchEvent);
bool accepted = false;
if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL)
accepted = QWindowSystemInterface::handleTouchCancelEvent(targetWindow, QWasmIntegration::getTimestamp(), m_touchDevice.get(), keyModifier);
else
accepted = QWindowSystemInterface::handleTouchEvent(
targetWindow, QWasmIntegration::getTimestamp(), m_touchDevice.get(), touchPointList, keyModifier);
return static_cast<int>(accepted);
}

View File

@ -12,16 +12,12 @@
#include <QtGui/qinputdevice.h>
#include <QtCore/private/qstdweb_p.h>
#include <QPointer>
#include <QPointingDevice>
#include <emscripten/html5.h>
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
QT_BEGIN_NAMESPACE
struct PointerEvent;
class QWasmWindow;
class QWasmScreen;
class QOpenGLContext;
@ -34,8 +30,6 @@ public:
QWasmCompositor(QWasmScreen *screen);
~QWasmCompositor() final;
void initEventHandlers();
void addWindow(QWasmWindow *window);
void removeWindow(QWasmWindow *window);
@ -81,10 +75,6 @@ private:
bool m_requestUpdateAllWindows = false;
int m_requestAnimationFrameId = -1;
bool m_inDeliverUpdateRequest = false;
std::unique_ptr<QPointingDevice> m_touchDevice;
QMap<int, QPointF> m_pressedTouchIds;
};
QT_END_NAMESPACE

View File

@ -26,6 +26,10 @@ const char *Style = R"css(
overflow: hidden;
}
.qt-screen div {
touch-action: none;
}
.qt-window {
position: absolute;
background-color: lightgray;
@ -152,6 +156,7 @@ const char *Style = R"css(
.qt-window-canvas-container {
display: flex;
pointer-events: none;
}
.title-bar div {

View File

@ -133,8 +133,17 @@ MouseEvent &MouseEvent::operator=(MouseEvent &&other) = default;
PointerEvent::PointerEvent(EventType type, emscripten::val event) : MouseEvent(type, event)
{
pointerId = event["pointerId"].as<int>();
pointerType = event["pointerType"].as<std::string>() == "mouse" ? PointerType::Mouse
: PointerType::Other;
pointerType = ([type = event["pointerType"].as<std::string>()]() {
if (type == "mouse")
return PointerType::Mouse;
if (type == "touch")
return PointerType::Touch;
return PointerType::Other;
})();
width = event["width"].as<qreal>();
height = event["height"].as<qreal>();
pressure = event["pressure"].as<qreal>();
isPrimary = event["isPrimary"].as<bool>();
}
PointerEvent::~PointerEvent() = default;

View File

@ -28,11 +28,13 @@ enum class EventType {
PointerUp,
PointerEnter,
PointerLeave,
PointerCancel,
Wheel,
};
enum class PointerType {
Mouse,
Touch,
Other,
};
@ -211,6 +213,10 @@ struct PointerEvent : public MouseEvent
PointerType pointerType;
int pointerId;
qreal pressure;
qreal width;
qreal height;
bool isPrimary;
};
struct DragEvent : public MouseEvent

View File

@ -89,14 +89,11 @@ void QWasmInputContext::focusWindowChanged(QWindow *focusWindow)
m_focusWindow = focusWindow;
}
emscripten::val QWasmInputContext::focusScreenElement()
emscripten::val QWasmInputContext::inputHandlerElementForFocusedWindow()
{
if (!m_focusWindow)
return emscripten::val::undefined();
QScreen *screen = m_focusWindow->screen();
if (!screen)
return emscripten::val::undefined();
return QWasmScreen::get(screen)->element();
return static_cast<QWasmWindow *>(m_focusWindow->handle())->inputHandlerElement();
}
void QWasmInputContext::update(Qt::InputMethodQueries queries)
@ -121,10 +118,10 @@ void QWasmInputContext::showInputPanel()
if (platform() == Platform::MacOS // keep for compatibility
|| platform() == Platform::iPhone
|| platform() == Platform::Windows) {
emscripten::val screenElement = focusScreenElement();
if (screenElement.isUndefined())
emscripten::val inputWrapper = inputHandlerElementForFocusedWindow();
if (inputWrapper.isUndefined())
return;
screenElement.call<void>("appendChild", m_inputElement);
inputWrapper.call<void>("appendChild", m_inputElement);
}
m_inputElement.call<void>("focus");

View File

@ -32,7 +32,7 @@ public:
void inputStringChanged(QString &, QWasmInputContext *context);
private:
emscripten::val focusScreenElement();
emscripten::val inputHandlerElementForFocusedWindow();
bool m_inputPanelVisible = false;

View File

@ -71,12 +71,17 @@ QWasmScreen::QWasmScreen(const emscripten::val &containerOrCanvas)
emscripten::val::module_property("specialHTMLTargets")
.set(outerScreenId().toStdString(), m_container);
// Install event handlers on the container/canvas. This must be
// done after the canvas has been created above.
m_compositor->initEventHandlers();
updateQScreenAndCanvasRenderSize();
m_shadowContainer.call<void>("focus");
m_touchDevice = std::make_unique<QPointingDevice>(
"touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
QPointingDevice::PointerType::Finger,
QPointingDevice::Capability::Position | QPointingDevice::Capability::Area
| QPointingDevice::Capability::NormalizedPosition,
10, 0);
QWindowSystemInterface::registerInputDevice(m_touchDevice.get());
}
QWasmScreen::~QWasmScreen()

View File

@ -36,6 +36,7 @@ public:
emscripten::val element() const;
QString eventTargetId() const;
QString outerScreenId() const;
QPointingDevice *touchDevice() { return m_touchDevice.get(); }
QWasmCompositor *compositor();
QWasmDeadKeySupport *deadKeySupport() { return m_deadKeySupport.get(); }
@ -67,6 +68,7 @@ private:
emscripten::val m_container;
emscripten::val m_shadowContainer;
std::unique_ptr<QWasmCompositor> m_compositor;
std::unique_ptr<QPointingDevice> m_touchDevice;
std::unique_ptr<QWasmDeadKeySupport> m_deadKeySupport;
QRect m_geometry = QRect(0, 0, 100, 100);
int m_depth = 32;

View File

@ -54,7 +54,7 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
m_nonClientArea = std::make_unique<NonClientArea>(this, m_qtWindow);
m_nonClientArea->titleBar()->setTitle(window()->title());
m_clientArea = std::make_unique<ClientArea>(this, compositor->screen(), m_canvas);
m_clientArea = std::make_unique<ClientArea>(this, compositor->screen(), m_windowContents);
m_qtWindow.call<void>("appendChild", m_windowContents);
@ -237,7 +237,7 @@ void QWasmWindow::setZOrder(int z)
void QWasmWindow::setWindowCursor(QByteArray cssCursorName)
{
m_canvas["style"].set("cursor", emscripten::val(cssCursorName.constData()));
m_windowContents["style"].set("cursor", emscripten::val(cssCursorName.constData()));
}
void QWasmWindow::setGeometry(const QRect &rect)
@ -576,7 +576,8 @@ void QWasmWindow::requestActivateWindow()
if (window()->isTopLevel())
raise();
m_canvas.call<void>("focus");
if (!QWasmIntegration::get()->inputContext())
m_canvas.call<void>("focus");
QPlatformWindow::requestActivateWindow();
}

View File

@ -86,9 +86,9 @@ public:
QWindow *window() const { return m_window; }
std::string canvasSelector() const;
emscripten::val context2d() { return m_context2d; }
emscripten::val a11yContainer() { return m_a11yContainer; }
emscripten::val context2d() const { return m_context2d; }
emscripten::val a11yContainer() const { return m_a11yContainer; }
emscripten::val inputHandlerElement() const { return m_windowContents; }
private:
friend class QWasmScreen;

View File

@ -9,7 +9,7 @@
#include "qwasmwindow.h"
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/qpointingdevice.h>
#include <QtCore/qassert.h>
@ -28,13 +28,12 @@ ClientArea::ClientArea(QWasmWindow *window, QWasmScreen *screen, emscripten::val
m_pointerMoveCallback =
std::make_unique<qstdweb::EventCallback>(element, "pointermove", callback);
m_pointerUpCallback = std::make_unique<qstdweb::EventCallback>(element, "pointerup", callback);
m_pointerCancelCallback =
std::make_unique<qstdweb::EventCallback>(element, "pointercancel", callback);
}
bool ClientArea::processPointer(const PointerEvent &event)
{
if (event.pointerType != PointerType::Mouse)
return false;
const auto localScreenPoint =
dom::mapPoint(event.target, m_screen->element(), event.localPoint);
const auto pointInScreen = m_screen->mapFromLocal(localScreenPoint);
@ -42,21 +41,22 @@ bool ClientArea::processPointer(const PointerEvent &event)
const QPointF pointInTargetWindowCoords = m_window->window()->mapFromGlobal(pointInScreen);
switch (event.type) {
case EventType::PointerDown: {
case EventType::PointerDown:
m_element.call<void>("setPointerCapture", event.pointerId);
m_window->window()->requestActivate();
break;
}
case EventType::PointerUp: {
case EventType::PointerUp:
m_element.call<void>("releasePointerCapture", event.pointerId);
break;
}
case EventType::PointerEnter:;
QWindowSystemInterface::handleEnterEvent(
m_window->window(), pointInTargetWindowCoords, pointInScreen);
case EventType::PointerEnter:
if (event.isPrimary) {
QWindowSystemInterface::handleEnterEvent(m_window->window(), pointInTargetWindowCoords,
pointInScreen);
}
break;
case EventType::PointerLeave:
QWindowSystemInterface::handleLeaveEvent(m_window->window());
if (event.isPrimary)
QWindowSystemInterface::handleLeaveEvent(m_window->window());
break;
default:
break;
@ -78,15 +78,72 @@ bool ClientArea::deliverEvent(const PointerEvent &event)
qBound(geometryF.left(), pointInScreen.x(), geometryF.right()),
qBound(geometryF.top(), pointInScreen.y(), geometryF.bottom()));
const QEvent::Type eventType =
MouseEvent::mouseEventTypeFromEventType(event.type, WindowArea::Client);
if (event.pointerType == PointerType::Mouse) {
const QEvent::Type eventType =
MouseEvent::mouseEventTypeFromEventType(event.type, WindowArea::Client);
return eventType != QEvent::None
&& QWindowSystemInterface::handleMouseEvent(
m_window->window(), QWasmIntegration::getTimestamp(),
m_window->window()->mapFromGlobal(targetPointClippedToScreen),
targetPointClippedToScreen, event.mouseButtons, event.mouseButton, eventType,
event.modifiers);
return eventType != QEvent::None
&& QWindowSystemInterface::handleMouseEvent(
m_window->window(), QWasmIntegration::getTimestamp(),
m_window->window()->mapFromGlobal(targetPointClippedToScreen),
targetPointClippedToScreen, event.mouseButtons, event.mouseButton,
eventType, event.modifiers);
}
QWindowSystemInterface::TouchPoint *touchPoint;
QPointF pointInTargetWindowCoords =
QPointF(m_window->window()->mapFromGlobal(targetPointClippedToScreen));
QPointF normalPosition(pointInTargetWindowCoords.x() / m_window->window()->width(),
pointInTargetWindowCoords.y() / m_window->window()->height());
const auto tp = m_pointerIdToTouchPoints.find(event.pointerId);
if (tp != m_pointerIdToTouchPoints.end()) {
touchPoint = &tp.value();
} else {
touchPoint = &m_pointerIdToTouchPoints
.insert(event.pointerId, QWindowSystemInterface::TouchPoint())
.value();
touchPoint->id = event.pointerId;
touchPoint->state = QEventPoint::State::Pressed;
}
const bool stationaryTouchPoint = (normalPosition == touchPoint->normalPosition);
touchPoint->normalPosition = normalPosition;
touchPoint->area = QRectF(targetPointClippedToScreen, QSizeF(event.width, event.height))
.translated(-event.width / 2, -event.height / 2);
touchPoint->pressure = event.pressure;
switch (event.type) {
case EventType::PointerUp:
touchPoint->state = QEventPoint::State::Released;
break;
case EventType::PointerMove:
touchPoint->state = (stationaryTouchPoint ? QEventPoint::State::Stationary
: QEventPoint::State::Updated);
break;
default:
break;
}
QList<QWindowSystemInterface::TouchPoint> touchPointList;
touchPointList.reserve(m_pointerIdToTouchPoints.size());
std::transform(m_pointerIdToTouchPoints.begin(), m_pointerIdToTouchPoints.end(),
std::back_inserter(touchPointList),
[](const QWindowSystemInterface::TouchPoint &val) { return val; });
if (event.type == EventType::PointerUp)
m_pointerIdToTouchPoints.remove(event.pointerId);
return event.type == EventType::PointerCancel
? QWindowSystemInterface::handleTouchCancelEvent(
m_window->window(), QWasmIntegration::getTimestamp(), m_screen->touchDevice(),
event.modifiers)
: QWindowSystemInterface::handleTouchEvent(
m_window->window(), QWasmIntegration::getTimestamp(), m_screen->touchDevice(),
touchPointList, event.modifiers);
}
QT_END_NAMESPACE

View File

@ -5,6 +5,7 @@
#define QWASMWINDOWCLIENTAREA_H
#include <QtCore/qnamespace.h>
#include <qpa/qwindowsysteminterface.h>
#include <emscripten/val.h>
@ -32,6 +33,9 @@ private:
std::unique_ptr<qstdweb::EventCallback> m_pointerDownCallback;
std::unique_ptr<qstdweb::EventCallback> m_pointerMoveCallback;
std::unique_ptr<qstdweb::EventCallback> m_pointerUpCallback;
std::unique_ptr<qstdweb::EventCallback> m_pointerCancelCallback;
QMap<int, QWindowSystemInterface::TouchPoint> m_pointerIdToTouchPoints;
QWasmScreen *m_screen;
QWasmWindow *m_window;

View File

@ -129,9 +129,6 @@ Resizer::ResizerElement::ResizerElement(ResizerElement &&other) = default;
bool Resizer::ResizerElement::onPointerDown(const PointerEvent &event)
{
if (event.pointerType != PointerType::Mouse)
return false;
m_element.call<void>("setPointerCapture", event.pointerId);
m_capturedPointerId = event.pointerId;
@ -371,9 +368,6 @@ QRectF TitleBar::geometry() const
bool TitleBar::onPointerDown(const PointerEvent &event)
{
if (event.pointerType != PointerType::Mouse)
return false;
m_element.call<void>("setPointerCapture", event.pointerId);
m_capturedPointerId = event.pointerId;