diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-03-09 01:00:54 +0100 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-03-09 01:00:55 +0100 |
commit | 261c0dedac1d97b5960d27eedaa017d45050b654 (patch) | |
tree | f91c8e2a770f4c456f6465f28aecefc8c2bbeccf /src/plugins | |
parent | 830e06a3f8173621a99426e7da6ad9704eb701b3 (diff) | |
parent | caa74f16d41ebe65e1edbea219f799cf246d6067 (diff) |
Merge remote-tracking branch 'origin/5.13' into dev
Change-Id: I056b658ffe9390dfcbe2787e2bddc7f4e9b389dd
Diffstat (limited to 'src/plugins')
20 files changed, 408 insertions, 255 deletions
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp index 6b9687c22d..57fe7c2fa2 100644 --- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp @@ -70,7 +70,9 @@ void QComposeInputContext::ensureInitialized() } m_initialized = true; - const char *const locale = setlocale(LC_CTYPE, ""); + const char *locale = setlocale(LC_CTYPE, ""); + if (!locale) + locale = setlocale(LC_CTYPE, nullptr); qCDebug(lcXkbCompose) << "detected locale (LC_CTYPE):" << locale; m_composeTable = xkb_compose_table_new_from_locale(m_XkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index d86e935788..9c705616ba 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing"); -Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse"); +Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg); Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen"); // diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 17063f6e92..5309449dce 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -137,22 +137,13 @@ { if ((self = [super initWithFrame:NSZeroRect])) { m_platformWindow = platformWindow; - m_buttons = Qt::NoButton; - m_acceptedMouseDowns = Qt::NoButton; - m_frameStrutButtons = Qt::NoButton; m_sendKeyEvent = false; - m_sendUpAsRightButton = false; m_inputSource = nil; - m_mouseMoveHelper = [[QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) alloc] initWithView:self]; m_resendKeyEvent = false; - m_scrolling = false; m_updatingDrag = false; m_currentlyInterpretedKeyEvent = nil; - m_dontOverrideCtrlLMB = qt_mac_resolveOption(false, platformWindow->window(), - "_q_platform_MacDontOverrideCtrlLMB", "QT_MAC_DONT_OVERRIDE_CTRL_LMB"); self.focusRingType = NSFocusRingTypeNone; - self.cursor = nil; self.previousSuperview = nil; self.previousWindow = nil; diff --git a/src/plugins/platforms/cocoa/qnsview_dragging.mm b/src/plugins/platforms/cocoa/qnsview_dragging.mm index 1c38c5326c..002cb3279e 100644 --- a/src/plugins/platforms/cocoa/qnsview_dragging.mm +++ b/src/plugins/platforms/cocoa/qnsview_dragging.mm @@ -57,9 +57,9 @@ NSFilesPromisePboardType, NSInkTextPboardType, NSMultipleTextSelectionPboardType, mimeTypeGeneric]]; - // Add custom types supported by the application. + // Add custom types supported by the application for (const QString &customType : qt_mac_enabledDraggedTypes()) - [supportedTypes addObject:customType.toNSString()]; + [supportedTypes addObject:customType.toNSString()]; [self registerForDraggedTypes:supportedTypes]; } @@ -79,11 +79,11 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin return target->mapFromGlobal(source->mapToGlobal(point)); } -- (NSDragOperation)draggingSession:(NSDraggingSession *)session - sourceOperationMaskForDraggingContext:(NSDraggingContext)context +- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context { Q_UNUSED(session); Q_UNUSED(context); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions()); } @@ -134,30 +134,29 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin if (pixmapCursor.isNull()) { switch (response.acceptedAction()) { - case Qt::CopyAction: - nativeCursor = [NSCursor dragCopyCursor]; - break; - case Qt::LinkAction: - nativeCursor = [NSCursor dragLinkCursor]; - break; - case Qt::IgnoreAction: - // Uncomment the next lines if forbiden cursor wanted on non droppable targets. - /*nativeCursor = [NSCursor operationNotAllowedCursor]; - break;*/ - case Qt::MoveAction: - default: - nativeCursor = [NSCursor arrowCursor]; - break; + case Qt::CopyAction: + nativeCursor = [NSCursor dragCopyCursor]; + break; + case Qt::LinkAction: + nativeCursor = [NSCursor dragLinkCursor]; + break; + case Qt::IgnoreAction: + // Uncomment the next lines if forbidden cursor is wanted on undroppable targets. + /*nativeCursor = [NSCursor operationNotAllowedCursor]; + break;*/ + case Qt::MoveAction: + default: + nativeCursor = [NSCursor arrowCursor]; + break; } - } - else { + } else { NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor); nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize()); nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint]; [nsimage release]; } - // change the cursor + // Change the cursor [nativeCursor set]; // Make sure the cursor is updated correctly if the mouse does not move and window is under cursor @@ -169,39 +168,33 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin if (m_updatingDrag) return; - const QPoint mousePos(QCursor::pos()); - CGEventRef moveEvent(CGEventCreateMouseEvent( - NULL, kCGEventMouseMoved, - CGPointMake(mousePos.x(), mousePos.y()), + QCFType<CGEventRef> moveEvent = CGEventCreateMouseEvent( + nullptr, kCGEventMouseMoved, QCursor::pos().toCGPoint(), kCGMouseButtonLeft // ignored - )); + ); CGEventPost(kCGHIDEventTap, moveEvent); - CFRelease(moveEvent); } -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender { - return [self handleDrag : sender]; + return [self handleDrag:(QEvent::DragEnter) sender:sender]; } -- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender +- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender { - m_updatingDrag = true; - const NSDragOperation ret([self handleDrag : sender]); - m_updatingDrag = false; - - return ret; + QScopedValueRollback<bool> rollback(m_updatingDrag, true); + return [self handleDrag:(QEvent::DragMove) sender:sender]; } // Sends drag update to Qt, return the action -- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender +- (NSDragOperation)handleDrag:(QEvent::Type)dragType sender:(id<NSDraggingInfo>)sender { if (!m_platformWindow) return NSDragOperationNone; - NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; - QPoint qt_windowPoint(windowPoint.x, windowPoint.y); - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); + QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint(); + + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(sender.draggingSourceOperationMask); QWindow *target = findEventTargetWindow(m_platformWindow->window()); if (!target) @@ -209,7 +202,12 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin const auto modifiers = [QNSView convertKeyModifiers:NSApp.currentEvent.modifierFlags]; const auto buttons = currentlyPressedMouseButtons(); - const auto point = mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint); + const auto point = mapWindowCoordinates(m_platformWindow->window(), target, windowPoint); + + if (dragType == QEvent::DragEnter) + qCDebug(lcQpaMouse) << dragType << self << "at" << windowPoint; + else + qCDebug(lcQpaMouse) << dragType << "at" << windowPoint << "with" << buttons; QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect()); QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); @@ -219,7 +217,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin point, qtAllowed, buttons, modifiers); [self updateCursorFromDragResponse:response drag:nativeDrag]; } else { - QCocoaDropData mimeData([sender draggingPasteboard]); + QCocoaDropData mimeData(sender.draggingPasteboard); response = QWindowSystemInterface::handleDrag(target, &mimeData, point, qtAllowed, buttons, modifiers); } @@ -227,7 +225,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin return qt_mac_mapDropAction(response.acceptedAction()); } -- (void)draggingExited:(id <NSDraggingInfo>)sender +- (void)draggingExited:(id<NSDraggingInfo>)sender { if (!m_platformWindow) return; @@ -236,17 +234,18 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin if (!target) return; - NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; - QPoint qt_windowPoint(windowPoint.x, windowPoint.y); + QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint(); + + qCDebug(lcQpaMouse) << QEvent::DragLeave << self << "at" << windowPoint; // Send 0 mime data to indicate drag exit QWindowSystemInterface::handleDrag(target, nullptr, - mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), + mapWindowCoordinates(m_platformWindow->window(), target, windowPoint), Qt::IgnoreAction, Qt::NoButton, Qt::NoModifier); } -// called on drop, send the drop to Qt and return if it was accepted. -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender +// Called on drop, send the drop to Qt and return if it was accepted +- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { if (!m_platformWindow) return false; @@ -255,31 +254,31 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin if (!target) return false; - NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; - QPoint qt_windowPoint(windowPoint.x, windowPoint.y); - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); + QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint(); + + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(sender.draggingSourceOperationMask); QPlatformDropQtResponse response(false, Qt::IgnoreAction); QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); const auto modifiers = [QNSView convertKeyModifiers:NSApp.currentEvent.modifierFlags]; const auto buttons = currentlyPressedMouseButtons(); - const auto point = mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint); + const auto point = mapWindowCoordinates(m_platformWindow->window(), target, windowPoint); + + qCDebug(lcQpaMouse) << QEvent::Drop << "at" << windowPoint << "with" << buttons; if (nativeDrag->currentDrag()) { // The drag was started from within the application response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(), point, qtAllowed, buttons, modifiers); } else { - QCocoaDropData mimeData([sender draggingPasteboard]); + QCocoaDropData mimeData(sender.draggingPasteboard); response = QWindowSystemInterface::handleDrop(target, &mimeData, point, qtAllowed, buttons, modifiers); } return response.isAccepted(); } -- (void)draggingSession:(NSDraggingSession *)session - endedAtPoint:(NSPoint)screenPoint - operation:(NSDragOperation)operation +- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation { Q_UNUSED(session); Q_UNUSED(screenPoint); @@ -295,6 +294,8 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation)); m_buttons = currentlyPressedMouseButtons(); + + qCDebug(lcQpaMouse) << "Drag session" << session << "ended, with" << m_buttons; } @end diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm index 49c94f18db..6e3cff2b48 100644 --- a/src/plugins/platforms/cocoa/qnsview_mouse.mm +++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm @@ -93,6 +93,7 @@ - (void)resetMouseButtons { + qCDebug(lcQpaMouse) << "Reseting mouse buttons"; m_buttons = Qt::NoButton; m_frameStrutButtons = Qt::NoButton; } @@ -145,6 +146,9 @@ QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint(); ulong timestamp = [theEvent timestamp] * 1000; + + auto eventType = cocoaEvent2QtMouseEvent(theEvent); + qCInfo(lcQpaMouse) << "Frame-strut" << eventType << "at" << qtWindowPoint << "with" << m_frameStrutButtons << "in" << self.window; QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons); } @end @@ -153,6 +157,19 @@ - (void)initMouse { + m_buttons = Qt::NoButton; + m_acceptedMouseDowns = Qt::NoButton; + m_frameStrutButtons = Qt::NoButton; + + m_scrolling = false; + self.cursor = nil; + + m_sendUpAsRightButton = false; + m_dontOverrideCtrlLMB = qt_mac_resolveOption(false, m_platformWindow->window(), + "_q_platform_MacDontOverrideCtrlLMB", "QT_MAC_DONT_OVERRIDE_CTRL_LMB"); + + m_mouseMoveHelper = [[QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) alloc] initWithView:self]; + NSUInteger trackingOptions = NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited | NSTrackingCursorUpdate; @@ -241,6 +258,11 @@ button = Qt::RightButton; const auto eventType = cocoaEvent2QtMouseEvent(theEvent); + if (eventType == QEvent::MouseMove) + qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << buttons; + else + qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << buttons; + QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, buttons, button, eventType, modifiers); @@ -446,16 +468,16 @@ - (void)cursorUpdate:(NSEvent *)theEvent { - qCDebug(lcQpaMouse) << "Updating cursor for" << self << "to" << self.cursor; - // Note: We do not get this callback when moving from a subview that // uses the legacy cursorRect API, so the cursor is reset to the arrow // cursor. See rdar://34183708 - if (self.cursor) + if (self.cursor && self.cursor != NSCursor.currentCursor) { + qCInfo(lcQpaMouse) << "Updating cursor for" << self << "to" << self.cursor; [self.cursor set]; - else + } else { [super cursorUpdate:theEvent]; + } } - (void)mouseMovedImpl:(NSEvent *)theEvent @@ -510,6 +532,8 @@ QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; m_platformWindow->m_enterLeaveTargetWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); + + qCInfo(lcQpaMouse) << QEvent::Enter << self << "at" << windowPoint << "with" << currentlyPressedMouseButtons(); QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); } @@ -528,6 +552,7 @@ if (!m_platformWindow->isContentView()) return; + qCInfo(lcQpaMouse) << QEvent::Leave << self; QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow); m_platformWindow->m_enterLeaveTargetWindow = 0; } @@ -626,8 +651,10 @@ // "isInverted": natural OS X scrolling, inverted from the Qt/other platform/Jens perspective. bool isInverted = [theEvent isDirectionInvertedFromDevice]; - qCDebug(lcQpaMouse) << "scroll wheel @ window pos" << qt_windowPoint << "delta px" << pixelDelta - << "angle" << angleDelta << "phase" << phase << (isInverted ? "inverted" : ""); + qCInfo(lcQpaMouse).nospace() << phase << " at " << qt_windowPoint + << " pixelDelta=" << pixelDelta << " angleDelta=" << angleDelta + << (isInverted ? " inverted=true" : ""); + QWindowSystemInterface::handleWheelEvent(m_platformWindow->window(), qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, m_currentWheelModifiers, phase, source, isInverted); } diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js index 84694c7b9f..049eb1c35e 100644 --- a/src/plugins/platforms/wasm/qtloader.js +++ b/src/plugins/platforms/wasm/qtloader.js @@ -50,6 +50,7 @@ // External mode.usage: // // var config = { +// canvasElements : [$("canvas-id")], // showLoader: function() { // loader.style.display = 'block' // canvas.style.display = 'hidden' @@ -69,6 +70,8 @@ // One or more HTML elements. QtLoader will display loader elements // on these while loading the applicaton, and replace the loader with a // canvas on load complete. +// canvasElements : [canvas-element, ...] +// One or more canvas elements. // showLoader : function(status, containerElement) // Optional loading element constructor function. Implement to create // a custom loading screen. This function may be called multiple times, @@ -146,8 +149,25 @@ function QtLoader(config) while (element.firstChild) element.removeChild(element.firstChild); } - // Set default state handler functions if needed + function createCanvas() { + var canvas = document.createElement("canvas"); + canvas.className = "QtCanvas"; + canvas.style.height = "100%"; + canvas.style.width = "100%"; + + // Set contentEditable in order to enable clipboard events; hide the resulting focus frame. + canvas.contentEditable = true; + canvas.style.outline = "0px solid transparent"; + canvas.style.cursor = "default"; + + return canvas; + } + + // Set default state handler functions and create canvases if needed if (config.containerElements !== undefined) { + + config.canvasElements = config.containerElements.map(createCanvas); + config.showError = config.showError || function(errorText, container) { removeChildren(container); var errorTextElement = document.createElement("text"); @@ -164,12 +184,8 @@ function QtLoader(config) return loadingText; }; - config.showCanvas = config.showCanvas || function(container) { + config.showCanvas = config.showCanvas || function(canvas, container) { removeChildren(container); - var canvas = document.createElement("canvas"); - canvas.className = "QtCanvas" - canvas.style = "height: 100%; width: 100%;" - return canvas; } config.showExit = config.showExit || function(crashed, exitCode, container) { @@ -384,6 +400,8 @@ function QtLoader(config) Module.mainScriptUrlOrBlob = new Blob([emscriptenModuleSource], {type: 'text/javascript'}); + Module.qtCanvasElements = config.canvasElements; + config.restart = function() { // Restart by reloading the page. This will wipe all state which means @@ -438,19 +456,17 @@ function QtLoader(config) } function setCanvasContent() { - var firstCanvas; if (config.containerElements === undefined) { - firstCanvas = config.showCanvas(); - } else { - for (container of config.containerElements) { - var canvasElement = config.showCanvas(container); - container.appendChild(canvasElement); - } - firstCanvas = config.containerElements[0].firstChild; + if (config.showCanvas !== undefined) + config.showCanvas(); + return; } - if (Module.canvas === undefined) { - Module.canvas = firstCanvas; + for (var i = 0; i < config.containerElements.length; ++i) { + var container = config.containerElements[i]; + var canvas = config.canvasElements[i]; + config.showCanvas(canvas, container); + container.appendChild(canvas); } } diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp index ec058f05dd..7a7b253b19 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.cpp +++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp @@ -67,23 +67,42 @@ static void qClipboardPromiseResolve(emscripten::val something) pasteClipboardData(emscripten::val("text/plain"), something); } -static void qClipboardCopyTo(val event) +static void qClipboardCutTo(val event) { - val target = event["target"]; - val clipboard = event["clipboardData"]; + if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) { + // Send synthetic Ctrl+X to make the app cut data to Qt's clipboard + QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>( + 0, QEvent::KeyPress, Qt::Key_X, Qt::ControlModifier, "X"); + } val module = val::global("Module"); val clipdata = module.call<val>("getClipboardData"); val clipFormat = module.call<val>("getClipboardFormat"); - clipboard.call<void>("setData", clipFormat, clipdata); - target.call<void>("preventDefault"); + event["clipboardData"].call<void>("setData", clipFormat, clipdata); + event.call<void>("preventDefault"); } -static void qClipboardPasteTo(val event) +static void qClipboardCopyTo(val event) { - val target = event["clipboardData"]; + if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) { + // Send synthetic Ctrl+C to make the app copy data to Qt's clipboard + QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>( + 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C"); + } + val module = val::global("Module"); val clipdata = module.call<val>("getClipboardData"); + val clipFormat = module.call<val>("getClipboardFormat"); + event["clipboardData"].call<void>("setData", clipFormat, clipdata); + event.call<void>("preventDefault"); +} + +static void qClipboardPasteTo(val event) +{ + bool hasClipboardApi = QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi; + val clipdata = hasClipboardApi ? + val::global("Module").call<val>("getClipboardData") : + event["clipboardData"].call<val>("getData", std::string("text")); const std::string data = clipdata.as<std::string>(); if (data.length() > 0) { @@ -99,13 +118,16 @@ EMSCRIPTEN_BINDINGS(clipboard_module) { function("getClipboardFormat", &getClipboardFormat); function("pasteClipboardData", &pasteClipboardData); function("qClipboardPromiseResolve", &qClipboardPromiseResolve); + function("qClipboardCutTo", &qClipboardCutTo); function("qClipboardCopyTo", &qClipboardCopyTo); function("qClipboardPasteTo", &qClipboardPasteTo); } -QWasmClipboard::QWasmClipboard() : - hasClipboardApi(false) +QWasmClipboard::QWasmClipboard() { + val clipboard = val::global("navigator")["clipboard"]; + hasClipboardApi = (!clipboard.isUndefined() && !clipboard["readText"].isUndefined()); + initClipboardEvents(); } @@ -157,29 +179,32 @@ void QWasmClipboard::qWasmClipboardPaste(QMimeData *mData) void QWasmClipboard::initClipboardEvents() { - val navigator = val::global("navigator"); - val permissions = navigator["permissions"]; - val clipboard = navigator["clipboard"]; + if (!hasClipboardApi) + return; - hasClipboardApi = (!clipboard.isUndefined()); - if (hasClipboardApi) { - val readPermissionsMap = val::object(); - readPermissionsMap.set("name", val("clipboard-read")); - permissions.call<val>("query", readPermissionsMap); + val permissions = val::global("navigator")["permissions"]; + val readPermissionsMap = val::object(); + readPermissionsMap.set("name", val("clipboard-read")); + permissions.call<val>("query", readPermissionsMap); - val writePermissionsMap = val::object(); - writePermissionsMap.set("name", val("clipboard-write")); - permissions.call<val>("query", writePermissionsMap); - - } else { + val writePermissionsMap = val::object(); + writePermissionsMap.set("name", val("clipboard-write")); + permissions.call<val>("query", writePermissionsMap); +} - val window = val::global("window"); - window.call<void>("addEventListener", std::string("paste"), - val::module_property("qClipboardPasteTo")); +void QWasmClipboard::installEventHandlers(const QString &canvasId) +{ + if (hasClipboardApi) + return; - window.call<void>("addEventListener", std::string("copy"), - val::module_property("qClipboardCopyTo")); - } + // Fallback path for browsers which do not support direct clipboard access + val canvas = val::global(canvasId.toUtf8().constData()); + canvas.call<void>("addEventListener", std::string("cut"), + val::module_property("qClipboardCutTo")); + canvas.call<void>("addEventListener", std::string("copy"), + val::module_property("qClipboardCopyTo")); + canvas.call<void>("addEventListener", std::string("paste"), + val::module_property("qClipboardPasteTo")); } void QWasmClipboard::readTextFromClipboard() diff --git a/src/plugins/platforms/wasm/qwasmclipboard.h b/src/plugins/platforms/wasm/qwasmclipboard.h index e64b2e5007..00aae8fead 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.h +++ b/src/plugins/platforms/wasm/qwasmclipboard.h @@ -51,6 +51,7 @@ public: static void qWasmClipboardPaste(QMimeData *mData); void initClipboardEvents(); + void installEventHandlers(const QString &canvasId); bool hasClipboardApi; void readTextFromClipboard(); void writeTextToClipboard(); diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index 3dc6b7d2f3..90cc20789d 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -56,8 +56,9 @@ QWasmCompositedWindow::QWasmCompositedWindow() { } -QWasmCompositor::QWasmCompositor() - : m_frameBuffer(nullptr) +QWasmCompositor::QWasmCompositor(QWasmScreen *screen) + :QObject(screen) + , m_frameBuffer(nullptr) , m_blitter(new QOpenGLTextureBlitter) , m_needComposit(false) , m_inFlush(false) @@ -107,11 +108,6 @@ void QWasmCompositor::removeWindow(QWasmWindow *window) notifyTopWindowChanged(window); } -void QWasmCompositor::setScreen(QWasmScreen *screen) -{ - m_screen = screen; -} - void QWasmCompositor::setVisible(QWasmWindow *window, bool visible) { QWasmCompositedWindow &compositedWindow = m_compositedWindows[window]; @@ -654,7 +650,7 @@ void QWasmCompositor::frame() m_needComposit = false; - if (m_windowStack.empty() || !m_screen) + if (m_windowStack.empty() || !screen()) return; QWasmWindow *someWindow = nullptr; @@ -673,7 +669,7 @@ void QWasmCompositor::frame() if (m_context.isNull()) { m_context.reset(new QOpenGLContext()); //mContext->setFormat(mScreen->format()); - m_context->setScreen(m_screen->screen()); + m_context->setScreen(screen()->screen()); m_context->create(); } @@ -682,8 +678,8 @@ void QWasmCompositor::frame() if (!m_blitter->isCreated()) m_blitter->create(); - qreal dpr = m_screen->devicePixelRatio(); - glViewport(0, 0, m_screen->geometry().width() * dpr, m_screen->geometry().height() * dpr); + qreal dpr = screen()->devicePixelRatio(); + glViewport(0, 0, screen()->geometry().width() * dpr, screen()->geometry().height() * dpr); m_context->functions()->glClearColor(0.2, 0.2, 0.2, 1.0); m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -697,7 +693,7 @@ void QWasmCompositor::frame() if (!compositedWindow.visible) continue; - drawWindow(m_blitter.data(), m_screen, window); + drawWindow(m_blitter.data(), screen(), window); } m_blitter->release(); @@ -719,3 +715,8 @@ void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window) requestRedraw(); QWindowSystemInterface::handleWindowActivated(window->window()); } + +QWasmScreen *QWasmCompositor::screen() +{ + return static_cast<QWasmScreen *>(parent()); +} diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index 4e5ed46cec..ed6facdcc3 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -62,7 +62,7 @@ class QWasmCompositor : public QObject { Q_OBJECT public: - QWasmCompositor(); + QWasmCompositor(QWasmScreen *screen); ~QWasmCompositor(); enum QWasmSubControl { @@ -103,7 +103,6 @@ public: void addWindow(QWasmWindow *window, QWasmWindow *parentWindow = nullptr); void removeWindow(QWasmWindow *window); - void setScreen(QWasmScreen *screen); void setVisible(QWasmWindow *window, bool visible); void raise(QWasmWindow *window); @@ -129,8 +128,7 @@ private slots: void frame(); private: - void createFrameBuffer(); - void flushCompletedCallback(int32_t); + QWasmScreen *screen(); void notifyTopWindowChanged(QWasmWindow *window); void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); @@ -142,7 +140,6 @@ private: QImage *m_frameBuffer; QScopedPointer<QOpenGLContext> m_context; QScopedPointer<QOpenGLTextureBlitter> m_blitter; - QWasmScreen *m_screen; QHash<QWasmWindow *, QWasmCompositedWindow> m_compositedWindows; QList<QWasmWindow *> m_windowStack; diff --git a/src/plugins/platforms/wasm/qwasmcursor.cpp b/src/plugins/platforms/wasm/qwasmcursor.cpp index 744b160dd1..2b3f37300d 100644 --- a/src/plugins/platforms/wasm/qwasmcursor.cpp +++ b/src/plugins/platforms/wasm/qwasmcursor.cpp @@ -28,20 +28,21 @@ ****************************************************************************/ #include "qwasmcursor.h" +#include "qwasmscreen.h" #include <QtCore/qdebug.h> +#include <QtGui/qwindow.h> #include <emscripten/emscripten.h> #include <emscripten/bind.h> void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window) { - if (windowCursor == nullptr) + if (!windowCursor || !window) + return; + QScreen *screen = window->screen(); + if (!screen) return; - - // FIXME: The HTML5 plugin sets the cursor on the native canvas; when using multiple windows - // multiple cursors need to be managed taking mouse postion and stacking into account. - Q_UNUSED(window); // Bitmap and custom cursors are not implemented (will fall back to "auto") if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor) @@ -52,8 +53,9 @@ void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window) if (htmlCursorName.isEmpty()) htmlCursorName = "auto"; - // Set cursor on the main canvas - emscripten::val canvasStyle = emscripten::val::module_property("canvas")["style"]; + // Set cursor on the canvas + QString canvasId = QWasmScreen::get(screen)->canvasId(); + emscripten::val canvasStyle = emscripten::val::global(canvasId.toUtf8().constData())["style"]; canvasStyle.set("cursor", emscripten::val(htmlCursorName.constData())); } diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp index 3fc8f600a1..14222da807 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -320,41 +320,35 @@ EMSCRIPTEN_BINDINGS(mouse_module) { function("mouseWheelEvent", &mouseWheelEvent); } -QWasmEventTranslator::QWasmEventTranslator(QObject *parent) - : QObject(parent) +QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen) + : QObject(screen) , draggedWindow(nullptr) , lastWindow(nullptr) , pressedButtons(Qt::NoButton) , resizeMode(QWasmWindow::ResizeNone) { - emscripten_set_keydown_callback(0, (void *)this, 1, &keyboard_cb); - emscripten_set_keyup_callback(0, (void *)this, 1, &keyboard_cb); - - emscripten_set_mousedown_callback(0, (void *)this, 1, &mouse_cb); - emscripten_set_mouseup_callback(0, (void *)this, 1, &mouse_cb); - emscripten_set_mousemove_callback(0, (void *)this, 1, &mouse_cb); - - emscripten_set_focus_callback(0, (void *)this, 1, &focus_cb); - - emscripten_set_wheel_callback(0, (void *)this, 1, &wheel_cb); - touchDevice = new QTouchDevice; touchDevice->setType(QTouchDevice::TouchScreen); touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition); QWindowSystemInterface::registerTouchDevice(touchDevice); - emscripten_set_touchstart_callback("#canvas", (void *)this, 1, &touchCallback); - emscripten_set_touchend_callback("#canvas", (void *)this, 1, &touchCallback); - emscripten_set_touchmove_callback("#canvas", (void *)this, 1, &touchCallback); - emscripten_set_touchcancel_callback("#canvas", (void *)this, 1, &touchCallback); + initEventHandlers(); +} + +void QWasmEventTranslator::initEventHandlers() +{ + qDebug() << "QWasmEventTranslator::initEventHandlers"; + + QByteArray _canvasId = screen()->canvasId().toUtf8(); + const char *canvasId = _canvasId.constData(); // The Platform Detect: expand coverage and move as needed enum Platform { GenericPlatform, MacOSPlatform }; - Platform platform = Platform(emscripten::val::global("navigator")["platform"] - .call<bool>("includes", emscripten::val("Mac"))); + Platform platform = Platform(emscripten::val::global("navigator")["platform"] + .call<bool>("includes", emscripten::val("Mac"))); g_usePlatformMacCtrlMetaSwitching = (platform == MacOSPlatform); if (platform == MacOSPlatform) { @@ -362,11 +356,30 @@ QWasmEventTranslator::QWasmEventTranslator(QObject *parent) if (emscripten::val::global("window")["safari"].isUndefined()) { - emscripten::val::global("canvas").call<void>("addEventListener", + emscripten::val::global(canvasId).call<void>("addEventListener", std::string("wheel"), val::module_property("mouseWheelEvent")); } } + + emscripten_set_keydown_callback(canvasId, (void *)this, 1, &keyboard_cb); + emscripten_set_keyup_callback(canvasId, (void *)this, 1, &keyboard_cb); + + emscripten_set_mousedown_callback(canvasId, (void *)this, 1, &mouse_cb); + emscripten_set_mouseup_callback(canvasId, (void *)this, 1, &mouse_cb); + emscripten_set_mousemove_callback(canvasId, (void *)this, 1, &mouse_cb); + + emscripten_set_focus_callback(canvasId, (void *)this, 1, &focus_cb); + + emscripten_set_wheel_callback(canvasId, (void *)this, 1, &wheel_cb); + + emscripten_set_touchstart_callback(canvasId, (void *)this, 1, &touchCallback); + emscripten_set_touchend_callback(canvasId, (void *)this, 1, &touchCallback); + emscripten_set_touchmove_callback(canvasId, (void *)this, 1, &touchCallback); + emscripten_set_touchcancel_callback(canvasId, (void *)this, 1, &touchCallback); + + emscripten_set_resize_callback(nullptr, (void *)this, 1, uiEvent_cb); // Note: handles browser window resize + } template <typename Event> @@ -415,6 +428,11 @@ int QWasmEventTranslator::keyboard_cb(int eventType, const EmscriptenKeyboardEve return accepted ? 1 : 0; } +QWasmScreen *QWasmEventTranslator::screen() +{ + return static_cast<QWasmScreen *>(parent()); +} + Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey) { Qt::Key qtKey = Qt::Key_unknown; @@ -531,14 +549,14 @@ void resizeWindow(QWindow *window, QWasmWindow::ResizeMode mode, void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent) { auto timestamp = mouseEvent->timestamp; - QPoint point(mouseEvent->canvasX, mouseEvent->canvasY); + QPoint point(mouseEvent->targetX, mouseEvent->targetY); QEvent::Type buttonEventType = QEvent::None; Qt::MouseButton button = translateMouseButton(mouseEvent->button); Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent); - QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5); + QWindow *window2 = screen()->compositor()->windowAt(point, 5); if (window2 != nullptr) lastWindow = window2; @@ -635,6 +653,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh { Q_UNUSED(eventType) + QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData); EmscriptenMouseEvent mouseEvent = wheelEvent->mouse; int scrollFactor = 0; @@ -658,7 +677,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh auto timestamp = mouseEvent.timestamp; QPoint globalPoint(mouseEvent.canvasX, mouseEvent.canvasY); - QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(globalPoint, 5); + QWindow *window2 = eventTranslator->screen()->compositor()->windowAt(globalPoint, 5); QPoint localPoint(globalPoint.x() - window2->geometry().x(), globalPoint.y() - window2->geometry().y()); @@ -676,6 +695,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) { + QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData); QList<QWindowSystemInterface::TouchPoint> touchPointList; touchPointList.reserve(touchEvent->numTouches); QWindow *window2; @@ -685,7 +705,7 @@ int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEven const EmscriptenTouchPoint *touches = &touchEvent->touches[i]; QPoint point(touches->canvasX, touches->canvasY); - window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5); + window2 = eventTranslator->screen()->compositor()->windowAt(point, 5); QWindowSystemInterface::TouchPoint touchPoint; @@ -832,6 +852,14 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa return 0; QFlags<Qt::KeyboardModifier> mods = translateKeyboardEventModifier(keyEvent); + + // Clipboard fallback path: cut/copy/paste are handled by clipboard event + // handlers if direct clipboard access is not available. + if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi && modifiers & Qt::ControlModifier && + (qtKey == Qt::Key_X || qtKey == Qt::Key_C || qtKey == Qt::Key_V)) { + return 0; + } + bool accepted = false; if (keyType == QEvent::KeyPress && @@ -857,4 +885,19 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa return accepted; } +int QWasmEventTranslator::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData) +{ + Q_UNUSED(e) + QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData); + + if (eventType == EMSCRIPTEN_EVENT_RESIZE) { + // This resize event is called when the HTML window is resized. Depending + // on the page layout the the canvas might also have been resized, so we + // update the Qt screen size (and canvas render size). + eventTranslator->screen()->updateQScreenAndCanvasRenderSize(); + } + + return 0; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h index 25e2a630b6..ba08dce946 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.h +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h @@ -48,7 +48,7 @@ class QWasmEventTranslator : public QObject public: - explicit QWasmEventTranslator(QObject *parent = 0); + explicit QWasmEventTranslator(QWasmScreen *screen); static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); @@ -57,12 +57,15 @@ public: static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); + static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData); + void processEvents(); + void initEventHandlers(); Q_SIGNALS: void getWindowAt(const QPoint &point, QWindow **window); private: - + QWasmScreen *screen(); Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey); template <typename Event> QFlags<Qt::KeyboardModifier> translatKeyModifier(const Event *event); diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 6f96ec69da..1964cefdad 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -70,29 +70,35 @@ QWasmIntegration *QWasmIntegration::s_instance; QWasmIntegration::QWasmIntegration() : m_fontDb(nullptr), - m_compositor(new QWasmCompositor), - m_screen(new QWasmScreen(m_compositor)), m_eventDispatcher(nullptr), m_clipboard(new QWasmClipboard) { s_instance = this; - updateQScreenAndCanvasRenderSize(); - screenAdded(m_screen); - emscripten_set_resize_callback(0, (void *)this, 1, uiEvent_cb); - - m_eventTranslator = new QWasmEventTranslator; + // We expect that qtloader.js has populated Module.qtCanvasElements with one or more canvases. + // Also check Module.canvas, which may be set if the emscripen or a custom loader is used. + emscripten::val qtCanvaseElements = val::module_property("qtCanvasElements"); + emscripten::val canvas = val::module_property("canvas"); + + if (!qtCanvaseElements.isUndefined()) { + int screenCount = qtCanvaseElements["length"].as<int>(); + for (int i = 0; i < screenCount; ++i) { + emscripten::val canvas = qtCanvaseElements[i].as<emscripten::val>(); + QString canvasId = QString::fromStdString(canvas["id"].as<std::string>()); + addScreen(canvasId); + } + } else if (!canvas.isUndefined()){ + QString canvasId = QString::fromStdString(canvas["id"].as<std::string>()); + addScreen(canvasId); + } emscripten::val::global("window").set("onbeforeunload", val::module_property("browserBeforeUnload")); - } QWasmIntegration::~QWasmIntegration() { - delete m_compositor; - destroyScreen(m_screen); delete m_fontDb; - delete m_eventTranslator; + qDeleteAll(m_screens); s_instance = nullptr; } @@ -117,13 +123,15 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const { - return new QWasmWindow(window, m_compositor, m_backingStores.value(window)); + QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor(); + return new QWasmWindow(window, compositor, m_backingStores.value(window)); } QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const { #ifndef QT_NO_OPENGL - QWasmBackingStore *backingStore = new QWasmBackingStore(m_compositor, window); + QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor(); + QWasmBackingStore *backingStore = new QWasmBackingStore(compositor, window); m_backingStores.insert(window, backingStore); return backingStore; #else @@ -168,55 +176,22 @@ QPlatformTheme *QWasmIntegration::createPlatformTheme(const QString &name) const return QPlatformIntegration::createPlatformTheme(name); } -int QWasmIntegration::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData) -{ - Q_UNUSED(e) - Q_UNUSED(userData) - - if (eventType == EMSCRIPTEN_EVENT_RESIZE) { - // This resize event is called when the HTML window is resized. Depending - // on the page layout the the canvas might also have been resized, so we - // update the Qt screen size (and canvas render size). - updateQScreenAndCanvasRenderSize(); - } - - return 0; -} - -static void set_canvas_size(double width, double height) +QPlatformClipboard* QWasmIntegration::clipboard() const { - emscripten::val canvas = emscripten::val::global("canvas"); - canvas.set("width", width); - canvas.set("height", height); + return m_clipboard; } -void QWasmIntegration::updateQScreenAndCanvasRenderSize() +QVector<QWasmScreen *> QWasmIntegration::screens() { - // The HTML canvas has two sizes: the CSS size and the canvas render size. - // The CSS size is determined according to standard CSS rules, while the - // render size is set using the "width" and "height" attributes. The render - // size must be set manually and is not auto-updated on CSS size change. - // Setting the render size to a value larger than the CSS size enables high-dpi - // rendering. - - double css_width; - double css_height; - emscripten_get_element_css_size(0, &css_width, &css_height); - QSizeF cssSize(css_width, css_height); - - QWasmScreen *screen = QWasmIntegration::get()->m_screen; - QSizeF canvasSize = cssSize * screen->devicePixelRatio(); - - set_canvas_size(canvasSize.width(), canvasSize.height()); - screen->setGeometry(QRect(QPoint(0, 0), cssSize.toSize())); - QWasmIntegration::get()->m_compositor->redrawWindowContent(); + return m_screens; } -QPlatformClipboard* QWasmIntegration::clipboard() const +void QWasmIntegration::addScreen(const QString &canvasId) { - if (!m_clipboard) - m_clipboard = new QWasmClipboard; - return m_clipboard; + QWasmScreen *screen = new QWasmScreen(canvasId); + m_clipboard->installEventHandlers(canvasId); + m_screens.append(screen); + screenAdded(screen); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index 4c5aeb4ebc..5c0ac0b297 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -71,24 +71,20 @@ public: QPlatformTheme *createPlatformTheme(const QString &name) const override; QPlatformClipboard *clipboard() const override; - QWasmScreen *screen() { return m_screen; } - QWasmCompositor *compositor() { return m_compositor; } - QWasmEventTranslator *eventTranslator() { return m_eventTranslator; } + QVector<QWasmScreen *>screens(); QWasmClipboard *getWasmClipboard() { return m_clipboard; } static QWasmIntegration *get() { return s_instance; } static void QWasmBrowserExit(); - static void updateQScreenAndCanvasRenderSize(); private: + void addScreen(const QString &canvasId); + mutable QWasmFontDatabase *m_fontDb; - QWasmCompositor *m_compositor; - mutable QWasmScreen *m_screen; - mutable QWasmEventTranslator *m_eventTranslator; mutable QWasmEventDispatcher *m_eventDispatcher; - static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData); mutable QHash<QWindow *, QWasmBackingStore *> m_backingStores; + QVector<QWasmScreen *> m_screens; mutable QWasmClipboard *m_clipboard; static QWasmIntegration *s_instance; }; diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index 73af3d1878..ae43e2ebf0 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -28,7 +28,7 @@ ****************************************************************************/ #include "qwasmopenglcontext.h" - +#include "qwasmintegration.h" #include <EGL/egl.h> QT_BEGIN_NAMESPACE @@ -57,7 +57,7 @@ void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surfac emscripten_webgl_destroy_context(m_context); // Create new context - const char *canvasId = 0; // (use default canvas) FIXME: get the actual canvas from the surface. + const QString canvasId = QWasmScreen::get(surface->screen())->canvasId(); m_context = createEmscriptenContext(canvasId, m_requestedFormat); // Register context-lost callback. @@ -73,11 +73,11 @@ void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surfac return true; }; bool capture = true; - emscripten_set_webglcontextlost_callback(canvasId, this, capture, callback); + emscripten_set_webglcontextlost_callback(canvasId.toLocal8Bit().constData(), this, capture, callback); } } -EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const char *canvasId, QSurfaceFormat format) +EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const QString &canvasId, QSurfaceFormat format) { EmscriptenWebGLContextAttributes attributes; emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes @@ -96,7 +96,7 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(cons attributes.depth = format.depthBufferSize() > 0; attributes.stencil = format.stencilBufferSize() > 0; - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId, &attributes); + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId.toLocal8Bit().constData(), &attributes); return context; } diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h index 9123100479..126b596a7e 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.h +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h @@ -51,7 +51,7 @@ public: private: void maybeRecreateEmscriptenContext(QPlatformSurface *surface); - static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const char *canvasId, QSurfaceFormat format); + static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const QString &canvasId, QSurfaceFormat format); bool m_contextLost = false; QSurfaceFormat m_requestedFormat; diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index 37f1efadc6..a26cafa900 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -29,8 +29,10 @@ #include "qwasmscreen.h" #include "qwasmwindow.h" +#include "qwasmeventtranslator.h" #include "qwasmcompositor.h" #include <emscripten/bind.h> +#include <emscripten/val.h> #include <QtEglSupport/private/qeglconvenience_p.h> #ifndef QT_NO_OPENGL @@ -44,12 +46,13 @@ QT_BEGIN_NAMESPACE -QWasmScreen::QWasmScreen(QWasmCompositor *compositor) - : m_compositor(compositor) - , m_depth(32) - , m_format(QImage::Format_RGB32) +QWasmScreen::QWasmScreen(const QString &canvasId) + : m_canvasId(canvasId) + { - m_compositor->setScreen(this); + m_compositor = new QWasmCompositor(this); + m_eventTranslator = new QWasmEventTranslator(this); + updateQScreenAndCanvasRenderSize(); } QWasmScreen::~QWasmScreen() @@ -57,6 +60,31 @@ QWasmScreen::~QWasmScreen() } +QWasmScreen *QWasmScreen::get(QPlatformScreen *screen) +{ + return static_cast<QWasmScreen *>(screen); +} + +QWasmScreen *QWasmScreen::get(QScreen *screen) +{ + return get(screen->handle()); +} + +QWasmCompositor *QWasmScreen::compositor() +{ + return m_compositor; +} + +QWasmEventTranslator *QWasmScreen::eventTranslator() +{ + return m_eventTranslator; +} + +QString QWasmScreen::canvasId() const +{ + return m_canvasId; +} + QRect QWasmScreen::geometry() const { return m_geometry; @@ -82,6 +110,11 @@ qreal QWasmScreen::devicePixelRatio() const return qreal(htmlWindowDpr); } +QString QWasmScreen::name() const +{ + return m_canvasId; +} + QPlatformCursor *QWasmScreen::cursor() const { return const_cast<QWasmCursor *>(&m_cursor); @@ -114,4 +147,31 @@ void QWasmScreen::setGeometry(const QRect &rect) resizeMaximizedWindows(); } +void QWasmScreen::updateQScreenAndCanvasRenderSize() +{ + // The HTML canvas has two sizes: the CSS size and the canvas render size. + // The CSS size is determined according to standard CSS rules, while the + // render size is set using the "width" and "height" attributes. The render + // size must be set manually and is not auto-updated on CSS size change. + // Setting the render size to a value larger than the CSS size enables high-dpi + // rendering. + + QByteArray canvasId = m_canvasId.toUtf8(); + double css_width; + double css_height; + emscripten_get_element_css_size(canvasId.constData(), &css_width, &css_height); + QSizeF cssSize(css_width, css_height); + + QSizeF canvasSize = cssSize * devicePixelRatio(); + emscripten::val canvas = emscripten::val::global(canvasId.constData()); + canvas.set("width", canvasSize.width()); + canvas.set("height", canvasSize.height()); + + emscripten::val rect = canvas.call<emscripten::val>("getBoundingClientRect"); + QPoint position(rect["left"].as<int>(), rect["top"].as<int>()); + + setGeometry(QRect(position, cssSize.toSize())); + m_compositor->redrawWindowContent(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h index 3891db77bb..82d2a83edb 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.h +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -43,20 +43,28 @@ class QPlatformOpenGLContext; class QWasmWindow; class QWasmBackingStore; class QWasmCompositor; +class QWasmEventTranslator; class QOpenGLContext; class QWasmScreen : public QObject, public QPlatformScreen { Q_OBJECT public: - - QWasmScreen(QWasmCompositor *compositor); + QWasmScreen(const QString &canvasId); ~QWasmScreen(); + static QWasmScreen *get(QPlatformScreen *screen); + static QWasmScreen *get(QScreen *screen); + QString canvasId() const; + + QWasmCompositor *compositor(); + QWasmEventTranslator *eventTranslator(); + QRect geometry() const override; int depth() const override; QImage::Format format() const override; qreal devicePixelRatio() const override; + QString name() const override; QPlatformCursor *cursor() const override; void resizeMaximizedWindows(); @@ -64,17 +72,18 @@ public: QWindow *topLevelAt(const QPoint &p) const override; void invalidateSize(); + void updateQScreenAndCanvasRenderSize(); public slots: void setGeometry(const QRect &rect); -protected: private: - QWasmCompositor *m_compositor; - + QString m_canvasId; + QWasmCompositor *m_compositor = nullptr; + QWasmEventTranslator *m_eventTranslator = nullptr; QRect m_geometry = QRect(0, 0, 100, 100); - int m_depth; - QImage::Format m_format; + int m_depth = 32; + QImage::Format m_format = QImage::Format_RGB32; QWasmCursor m_cursor; }; diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html index 67bfcdfbdc..39bb711b6b 100644 --- a/src/plugins/platforms/wasm/wasm_shell.html +++ b/src/plugins/platforms/wasm/wasm_shell.html @@ -7,27 +7,32 @@ <style> html, body { padding: 0; margin : 0; overflow:hidden; height: 100% } /* the canvas *must not* have any border or padding, or mouse coords will be wrong */ - canvas { border: 0px none; background-color: white; height:100%; width:100%; } + canvas { border: 0px none; background-color: white; height:100%; width:100%; } + /* The contenteditable property is set to true for the canvas in order to support + clipboard events. Hide the resulting focus frame and set the cursor back to + the default cursor. */ + canvas { outline: 0px solid transparent; cursor:default } </style> </head> <body onload="init()"> - <figure style="overflow:visible;" id="spinner"> + <figure style="overflow:visible;" id="qtspinner"> <center style="margin-top:1.5em; line-height:150%"> <img src="qtlogo.svg"; width=320; height=200; style="display:block"> </img> <strong>Qt for WebAssembly: APPNAME</strong> - <div id="status"></div> + <div id="qtstatus"></div> <noscript>JavaScript is disabled. Please enable JavaScript to use this application.</noscript> </center> </figure> - <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas> + <canvas id="qtcanvas" oncontextmenu="event.preventDefault()" contenteditable="true"></canvas> <script type='text/javascript'> function init() { - var spinner = document.getElementById('spinner'); - var canvas = document.getElementById('canvas'); - var status = document.getElementById('status') + var spinner = document.getElementById('qtspinner'); + var canvas = document.getElementById('qtcanvas'); + var status = document.getElementById('qtstatus') var qtLoader = QtLoader({ + canvasElements : [canvas], showLoader: function(loaderStatus) { spinner.style.display = 'block'; canvas.style.display = 'none'; @@ -50,7 +55,6 @@ showCanvas: function() { spinner.style.display = 'none'; canvas.style.display = 'block'; - return canvas; }, }); qtLoader.loadEmscriptenModule("APPNAME"); |