summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.cpp4
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.h2
-rw-r--r--src/plugins/platforms/android/androidjniinput.cpp52
-rw-r--r--src/plugins/platforms/android/androidjniinput.h4
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp79
-rw-r--r--src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp16
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp52
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.h2
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp11
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.h2
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.cpp18
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.h12
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.cpp59
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.h11
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm3
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h11
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm219
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm9
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm6
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.mm27
-rw-r--r--src/plugins/platforms/cocoa/qcocoaresources.qrc7
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm9
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindowmanager.mm3
-rw-r--r--src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h1
-rw-r--r--src/plugins/platforms/cocoa/qnsview_complextext.mm4
-rw-r--r--src/plugins/platforms/cocoa/qnsview_keys.mm9
-rw-r--r--src/plugins/platforms/cocoa/qnsview_mouse.mm24
-rw-r--r--src/plugins/platforms/cocoa/qnsview_tablet.mm2
-rw-r--r--src/plugins/platforms/direct2d/CMakeLists.txt4
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp2
-rw-r--r--src/plugins/platforms/directfb/qdirectfbinput.cpp2
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp34
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h4
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfswindow.cpp4
-rw-r--r--src/plugins/platforms/eglfs/cursor.qrc7
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp7
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp9
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h1
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp24
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.h2
-rw-r--r--src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h3
-rw-r--r--src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm10
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.h4
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm22
-rw-r--r--src/plugins/platforms/ios/qiostextinputoverlay.mm32
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.h13
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.mm333
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.mm10
-rw-r--r--src/plugins/platforms/ios/quiview.mm2
-rw-r--r--src/plugins/platforms/ios/quiview_accessibility.mm5
-rw-r--r--src/plugins/platforms/offscreen/qoffscreencommon.cpp28
-rw-r--r--src/plugins/platforms/offscreen/qoffscreencommon.h1
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenwindow.cpp4
-rw-r--r--src/plugins/platforms/qnx/.prev_CMakeLists.txt91
-rw-r--r--src/plugins/platforms/qnx/qqnxeglwindow.cpp2
-rw-r--r--src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp4
-rw-r--r--src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp2
-rw-r--r--src/plugins/platforms/wasm/CMakeLists.txt3
-rw-r--r--src/plugins/platforms/wasm/qtloader.js6
-rw-r--r--src/plugins/platforms/wasm/qwasmbackingstore.cpp2
-rw-r--r--src/plugins/platforms/wasm/qwasmclipboard.cpp394
-rw-r--r--src/plugins/platforms/wasm/qwasmclipboard.h10
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.cpp666
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.h70
-rw-r--r--src/plugins/platforms/wasm/qwasmcursor.cpp40
-rw-r--r--src/plugins/platforms/wasm/qwasmcursor.h5
-rw-r--r--src/plugins/platforms/wasm/qwasmeventdispatcher.cpp194
-rw-r--r--src/plugins/platforms/wasm/qwasmeventdispatcher.h27
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.cpp595
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.h52
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.cpp5
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp4
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp77
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h20
-rw-r--r--src/plugins/platforms/windows/CMakeLists.txt4
-rw-r--r--src/plugins/platforms/windows/cursors.qrc25
-rw-r--r--src/plugins/platforms/windows/openglblacklists.qrc5
-rw-r--r--src/plugins/platforms/windows/qwin10helpers.cpp43
-rw-r--r--src/plugins/platforms/windows/qwindowsclipboard.cpp11
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp136
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h85
-rw-r--r--src/plugins/platforms/windows/qwindowscursor.cpp5
-rw-r--r--src/plugins/platforms/windows/qwindowsdialoghelpers.cpp43
-rw-r--r--src/plugins/platforms/windows/qwindowsinputcontext.cpp5
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp14
-rw-r--r--src/plugins/platforms/windows/qwindowsmime.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp4
-rw-r--r--src/plugins/platforms/windows/qwindowsopengltester.cpp12
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp75
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.h3
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.cpp111
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.cpp14
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp140
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h5
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp17
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h2
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp7
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp9
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.cpp33
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h6
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_screens.cpp9
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp216
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp24
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbmime.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp5
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h4
116 files changed, 2544 insertions, 2058 deletions
diff --git a/src/plugins/platforms/android/androidcontentfileengine.cpp b/src/plugins/platforms/android/androidcontentfileengine.cpp
index 2c47b71c88..b18c81e896 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.cpp
+++ b/src/plugins/platforms/android/androidcontentfileengine.cpp
@@ -54,8 +54,10 @@ AndroidContentFileEngine::AndroidContentFileEngine(const QString &f)
setFileName(f);
}
-bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
+bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode,
+ std::optional<QFile::Permissions> permissions)
{
+ Q_UNUSED(permissions);
QString openModeStr;
if (openMode & QFileDevice::ReadOnly) {
openModeStr += QLatin1Char('r');
diff --git a/src/plugins/platforms/android/androidcontentfileengine.h b/src/plugins/platforms/android/androidcontentfileengine.h
index 31eaf9b0ab..531d0f80ff 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.h
+++ b/src/plugins/platforms/android/androidcontentfileengine.h
@@ -46,7 +46,7 @@ class AndroidContentFileEngine : public QFSFileEngine
{
public:
AndroidContentFileEngine(const QString &fileName);
- bool open(QIODevice::OpenMode openMode) override;
+ bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override;
qint64 size() const override;
FileFlags fileFlags(FileFlags type = FileInfoAll) const override;
QString fileName(FileName file = DefaultName) const override;
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp
index ebdc18cc07..d4b51d38ba 100644
--- a/src/plugins/platforms/android/androidjniinput.cpp
+++ b/src/plugins/platforms/android/androidjniinput.cpp
@@ -78,16 +78,15 @@ namespace QtAndroidInput
candidatesEnd);
}
- void showSoftwareKeyboard(int left, int top, int width, int height, int editorHeight, int inputHints, int enterKeyType)
+ void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints, int enterKeyType)
{
QJniObject::callStaticMethod<void>(applicationClass(),
"showSoftwareKeyboard",
- "(IIIIIII)V",
+ "(IIIIII)V",
left,
top,
width,
height,
- editorHeight,
inputHints,
enterKeyType);
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
@@ -134,17 +133,6 @@ namespace QtAndroidInput
anchor.x(), anchor.y(), rtl);
}
- void updateInputItemRectangle(int left, int top, int width, int height)
- {
- QJniObject::callStaticMethod<void>(applicationClass(),
- "updateInputItemRectangle",
- "(IIII)V",
- left,
- top,
- width,
- height);
- }
-
static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y)
{
if (m_ignoreMouseEvents)
@@ -279,15 +267,11 @@ namespace QtAndroidInput
}
}
- static void touchEnd(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint /*action*/)
+ static QPointingDevice *getTouchDevice()
{
- if (m_touchPoints.isEmpty())
- return;
-
- QMutexLocker lock(QtAndroid::platformInterfaceMutex());
QAndroidPlatformIntegration *platformIntegration = QtAndroid::androidPlatformIntegration();
if (!platformIntegration)
- return;
+ return nullptr;
QPointingDevice *touchDevice = platformIntegration->touchDevice();
if (!touchDevice) {
@@ -303,10 +287,37 @@ namespace QtAndroidInput
platformIntegration->setTouchDevice(touchDevice);
}
+ return touchDevice;
+ }
+
+ static void touchEnd(JNIEnv * /*env*/, jobject /*thiz*/, jint /*winId*/, jint /*action*/)
+ {
+ if (m_touchPoints.isEmpty())
+ return;
+
+ QMutexLocker lock(QtAndroid::platformInterfaceMutex());
+ QPointingDevice *touchDevice = getTouchDevice();
+ if (!touchDevice)
+ return;
+
QWindow *window = QtAndroid::topLevelWindowAt(m_touchPoints.at(0).area.center().toPoint());
QWindowSystemInterface::handleTouchEvent(window, touchDevice, m_touchPoints);
}
+ static void touchCancel(JNIEnv * /*env*/, jobject /*thiz*/, jint /*winId*/)
+ {
+ if (m_touchPoints.isEmpty())
+ return;
+
+ QMutexLocker lock(QtAndroid::platformInterfaceMutex());
+ QPointingDevice *touchDevice = getTouchDevice();
+ if (!touchDevice)
+ return;
+
+ QWindow *window = QtAndroid::topLevelWindowAt(m_touchPoints.at(0).area.center().toPoint());
+ QWindowSystemInterface::handleTouchCancelEvent(window, touchDevice);
+ }
+
static bool isTabletEventSupported(JNIEnv */*env*/, jobject /*thiz*/)
{
#if QT_CONFIG(tabletevent)
@@ -858,6 +869,7 @@ namespace QtAndroidInput
{"touchBegin","(I)V",(void*)touchBegin},
{"touchAdd","(IIIZIIFFFF)V",(void*)touchAdd},
{"touchEnd","(II)V",(void*)touchEnd},
+ {"touchCancel", "(I)V", (void *)touchCancel},
{"mouseDown", "(III)V", (void *)mouseDown},
{"mouseUp", "(III)V", (void *)mouseUp},
{"mouseMove", "(III)V", (void *)mouseMove},
diff --git a/src/plugins/platforms/android/androidjniinput.h b/src/plugins/platforms/android/androidjniinput.h
index ceef2a826c..30c083f51c 100644
--- a/src/plugins/platforms/android/androidjniinput.h
+++ b/src/plugins/platforms/android/androidjniinput.h
@@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE
namespace QtAndroidInput
{
// Software keyboard support
- void showSoftwareKeyboard(int top, int left, int width, int editorHeight, int height, int inputHints, int enterKeyType);
+ void showSoftwareKeyboard(int top, int left, int width, int height, int inputHints, int enterKeyType);
void resetSoftwareKeyboard();
void hideSoftwareKeyboard();
bool isSoftwareKeyboardVisible();
@@ -57,8 +57,6 @@ namespace QtAndroidInput
void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd);
// Software keyboard support
- // edit field resize
- void updateInputItemRectangle(int left, int top, int width, int height);
// cursor/selection handles
void updateHandles(int handleCount, QPoint editMenuPos = QPoint(), uint32_t editButtons = 0,
QPoint cursor = QPoint(), QPoint anchor = QPoint(), bool rtl = false);
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 6dff19f3d5..e99dca17a2 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -60,6 +60,7 @@
#include <android/bitmap.h>
#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qbasicatomic.h>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qjniobject.h>
#include <QtCore/qresource.h>
@@ -104,7 +105,6 @@ static sem_t m_exitSemaphore, m_terminateSemaphore;
QHash<int, AndroidSurfaceClient *> m_surfaces;
static QBasicMutex m_surfacesMutex;
-static int m_surfaceId = 1;
static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr;
@@ -123,6 +123,8 @@ static const char m_qtTag[] = "Qt";
static const char m_classErrorMsg[] = "Can't find class \"%s\"";
static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
+static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
+
namespace QtAndroid
{
QBasicMutex *platformInterfaceMutex()
@@ -322,6 +324,11 @@ namespace QtAndroid
return manufacturer + QLatin1Char(' ') + model;
}
+ jint generateViewId()
+ {
+ return QJniObject::callStaticMethod<jint>("android/view/View", "generateViewId", "()I");
+ }
+
int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth)
{
QJniEnvironment env;
@@ -329,7 +336,7 @@ namespace QtAndroid
return -1;
m_surfacesMutex.lock();
- int surfaceId = m_surfaceId++;
+ jint surfaceId = generateViewId();
m_surfaces[surfaceId] = client;
m_surfacesMutex.unlock();
@@ -352,7 +359,7 @@ namespace QtAndroid
int insertNativeView(jobject view, const QRect &geometry)
{
m_surfacesMutex.lock();
- const int surfaceId = m_surfaceId++;
+ jint surfaceId = generateViewId();
m_surfaces[surfaceId] = nullptr; // dummy
m_surfacesMutex.unlock();
@@ -545,6 +552,7 @@ static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
for (int i = 0; i < m_applicationParams.size(); i++)
params[i] = static_cast<const char *>(m_applicationParams[i].constData());
+ startQtAndroidPluginCalled.fetchAndAddRelease(1);
int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
if (m_mainLibraryHnd) {
@@ -592,7 +600,9 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
QAndroidEventDispatcherStopper::instance()->goingToStop(false);
}
- sem_wait(&m_terminateSemaphore);
+ if (startQtAndroidPluginCalled.loadAcquire())
+ sem_wait(&m_terminateSemaphore);
+
sem_destroy(&m_terminateSemaphore);
env->DeleteGlobalRef(m_applicationClass);
@@ -631,12 +641,11 @@ static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface,
surfaceClient->surfaceChanged(env, jSurface, w, h);
}
-static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/,
- jint screenWidthPixels, jint screenHeightPixels,
- jint availableLeftPixels, jint availableTopPixels,
- jint availableWidthPixels, jint availableHeightPixels,
- jdouble xdpi, jdouble ydpi,
- jdouble scaledDensity, jdouble density)
+static void setDisplayMetrics(JNIEnv * /*env*/, jclass /*clazz*/, jint screenWidthPixels,
+ jint screenHeightPixels, jint availableLeftPixels,
+ jint availableTopPixels, jint availableWidthPixels,
+ jint availableHeightPixels, jdouble xdpi, jdouble ydpi,
+ jdouble scaledDensity, jdouble density, jfloat refreshRate)
{
m_availableWidthPixels = availableWidthPixels;
m_availableHeightPixels = availableHeightPixels;
@@ -645,20 +654,18 @@ static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/,
QMutexLocker lock(&m_platformMutex);
if (!m_androidPlatformIntegration) {
- QAndroidPlatformIntegration::setDefaultDisplayMetrics(availableLeftPixels,
- availableTopPixels,
- availableWidthPixels,
- availableHeightPixels,
- qRound(double(screenWidthPixels) / xdpi * 25.4),
- qRound(double(screenHeightPixels) / ydpi * 25.4),
- screenWidthPixels,
- screenHeightPixels);
+ QAndroidPlatformIntegration::setDefaultDisplayMetrics(
+ availableLeftPixels, availableTopPixels, availableWidthPixels,
+ availableHeightPixels, qRound(double(screenWidthPixels) / xdpi * 25.4),
+ qRound(double(screenHeightPixels) / ydpi * 25.4), screenWidthPixels,
+ screenHeightPixels);
} else {
m_androidPlatformIntegration->setPhysicalSize(qRound(double(screenWidthPixels) / xdpi * 25.4),
qRound(double(screenHeightPixels) / ydpi * 25.4));
m_androidPlatformIntegration->setScreenSize(screenWidthPixels, screenHeightPixels);
m_androidPlatformIntegration->setAvailableGeometry(QRect(availableLeftPixels, availableTopPixels,
availableWidthPixels, availableHeightPixels));
+ m_androidPlatformIntegration->setRefreshRate(refreshRate);
}
}
@@ -759,6 +766,12 @@ static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint new
}
}
+static void handleRefreshRateChanged(JNIEnv */*env*/, jclass /*cls*/, jfloat refreshRate)
+{
+ if (m_androidPlatformIntegration)
+ m_androidPlatformIntegration->setRefreshRate(refreshRate);
+}
+
static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/,
jint requestCode,
jint resultCode,
@@ -778,20 +791,22 @@ static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
}
static JNINativeMethod methods[] = {
- {"startQtAndroidPlugin", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)startQtAndroidPlugin},
- {"startQtApplication", "()V", (void *)startQtApplication},
- {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin},
- {"quitQtCoreApplication", "()V", (void *)quitQtCoreApplication},
- {"terminateQt", "()V", (void *)terminateQt},
- {"waitForServiceSetup", "()V", (void *)waitForServiceSetup},
- {"setDisplayMetrics", "(IIIIIIDDDD)V", (void *)setDisplayMetrics},
- {"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface},
- {"updateWindow", "()V", (void *)updateWindow},
- {"updateApplicationState", "(I)V", (void *)updateApplicationState},
- {"handleOrientationChanged", "(II)V", (void *)handleOrientationChanged},
- {"onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult},
- {"onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent},
- {"onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind}
+ { "startQtAndroidPlugin", "(Ljava/lang/String;Ljava/lang/String;)Z",
+ (void *)startQtAndroidPlugin },
+ { "startQtApplication", "()V", (void *)startQtApplication },
+ { "quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin },
+ { "quitQtCoreApplication", "()V", (void *)quitQtCoreApplication },
+ { "terminateQt", "()V", (void *)terminateQt },
+ { "waitForServiceSetup", "()V", (void *)waitForServiceSetup },
+ { "setDisplayMetrics", "(IIIIIIDDDDF)V", (void *)setDisplayMetrics },
+ { "setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface },
+ { "updateWindow", "()V", (void *)updateWindow },
+ { "updateApplicationState", "(I)V", (void *)updateApplicationState },
+ { "handleOrientationChanged", "(II)V", (void *)handleOrientationChanged },
+ { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult },
+ { "onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent },
+ { "onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind },
+ { "handleRefreshRateChanged", "(F)V", (void *)handleRefreshRateChanged }
};
#define FIND_AND_CHECK_CLASS(CLASS_NAME) \
diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
index 15c0aada95..0c6fb92ffb 100644
--- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
+++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
@@ -261,8 +261,10 @@ public:
close();
}
- bool open(QIODevice::OpenMode openMode) override
+ bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override
{
+ Q_UNUSED(permissions);
+
if (m_isFolder || (openMode & QIODevice::WriteOnly))
return false;
close();
@@ -309,21 +311,11 @@ public:
return -1;
}
- bool isSequential() const override
- {
- return false;
- }
-
bool caseSensitive() const override
{
return true;
}
- bool isRelativePath() const override
- {
- return false;
- }
-
FileFlags fileFlags(FileFlags type = FileInfoAll) const override
{
FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
@@ -368,7 +360,7 @@ public:
m_fileName = cleanedAssetPath(file);
switch (FolderIterator::fileType(m_fileName)) {
case AssetItem::Type::File:
- open(QIODevice::ReadOnly);
+ open(QIODevice::ReadOnly, std::nullopt);
break;
case AssetItem::Type::Folder:
m_isFolder = true;
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp
index 37a5191dfc..417f3469a8 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/qandroidinputcontext.cpp
@@ -60,6 +60,7 @@
#include <qthread.h>
#include <qwindow.h>
#include <qpa/qplatformwindow.h>
+
QT_BEGIN_NAMESPACE
namespace {
@@ -492,7 +493,7 @@ QAndroidInputContext::QAndroidInputContext()
m_androidInputContext = this;
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged,
- this, &QAndroidInputContext::updateInputItemRectangle);
+ this, &QAndroidInputContext::updateSelectionHandles);
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::anchorRectangleChanged,
this, &QAndroidInputContext::updateSelectionHandles);
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::inputItemClipRectangleChanged, this, [this]{
@@ -921,49 +922,10 @@ void QAndroidInputContext::showInputPanel()
else
m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition()));
- QRect rect = cursorRect();
- if (!isInputPanelVisible())
- QtAndroidInput::showSoftwareKeyboard(rect.left(), rect.top(), rect.width(), rect.height(),
- screenInputItemRectangle().height(),
- query->value(Qt::ImHints).toUInt(),
- query->value(Qt::ImEnterKeyType).toUInt());
-}
-
-QRect QAndroidInputContext::cursorRect()
-{
- QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
- // if single line, we do not want to mess with the editor's position, as we do not
- // have to follow the cursor in vertical axis
- if (query.isNull()
- || (query->value(Qt::ImHints).toUInt() & Qt::ImhMultiLine) != Qt::ImhMultiLine)
- return {};
-
- auto im = qGuiApp->inputMethod();
- if (!im)
- return {};
-
- const auto cursorRect= im->cursorRectangle().toRect();
- QRect finalRect(inputItemRectangle().toRect());
- const QWindow *window = qGuiApp->focusWindow();
- const double pd = window
- ? QHighDpiScaling::factor(window)
- : QHighDpiScaling::factor(QtAndroid::androidPlatformIntegration()->screen());
- finalRect.setY(cursorRect.y() * pd);
- finalRect.setHeight(cursorRect.height() * pd);
- //fiddle a bit with vert margins, so the tracking rectangle is not too tight.
- finalRect += QMargins(0, cursorRect.height() / 4, 0, cursorRect.height() / 4);
- return finalRect;
-}
-
-void QAndroidInputContext::updateInputItemRectangle()
-{
- QRect rect = cursorRect();
-
- if (!rect.isValid())
- return;
- QtAndroidInput::updateInputItemRectangle(rect.left(), rect.top(),
- rect.width(), rect.height());
- updateSelectionHandles();
+ QRect rect = screenInputItemRectangle();
+ QtAndroidInput::showSoftwareKeyboard(rect.left(), rect.top(), rect.width(), rect.height(),
+ query->value(Qt::ImHints).toUInt(),
+ query->value(Qt::ImEnterKeyType).toUInt());
}
void QAndroidInputContext::showInputPanelLater(Qt::ApplicationState state)
@@ -1067,7 +1029,7 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right
absolutely not what Android's native EditText does. It deletes leftLength characters before
min(selection start, composing region start) and rightLength characters after max(selection
end, composing region end). There are no known keyboards that depend on this behavior, but
- it is better to be consistent with EditText behavior, because there definetly should be no
+ it is better to be consistent with EditText behavior, because there definitely should be no
keyboards that depend on documented behavior.
*/
const int leftEnd =
diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h
index 02a66c367a..e9bfb98e66 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.h
+++ b/src/plugins/platforms/android/qandroidinputcontext.h
@@ -138,7 +138,6 @@ public:
public slots:
void safeCall(const std::function<void()> &func, Qt::ConnectionType conType = Qt::BlockingQueuedConnection);
void updateCursorPosition();
- void updateInputItemRectangle();
void updateSelectionHandles();
void handleLocationChanged(int handleId, int x, int y);
void touchDown(int x, int y);
@@ -155,7 +154,6 @@ private:
bool focusObjectIsComposing() const;
void focusObjectStartComposing();
bool focusObjectStopComposing();
- QRect cursorRect();
private:
ExtractedText m_extractedText;
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
index 7741fc755d..1ec5867a38 100644
--- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
@@ -46,6 +46,7 @@
#include <QMimeDatabase>
#include <QMimeType>
#include <QRegularExpression>
+#include <QUrl>
QT_BEGIN_NAMESPACE
@@ -119,7 +120,7 @@ void QAndroidPlatformFileDialogHelper::takePersistableUriPermission(const QJniOb
uri.object(), modeFlags);
}
-void QAndroidPlatformFileDialogHelper::setIntentTitle(const QString &title)
+void QAndroidPlatformFileDialogHelper::setInitialFileName(const QString &title)
{
const QJniObject extraTitle = QJniObject::getStaticObjectField(
JniIntentClass, "EXTRA_TITLE", "Ljava/lang/String;");
@@ -209,6 +210,12 @@ bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::Win
if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
m_intent = getFileDialogIntent("ACTION_CREATE_DOCUMENT");
+ const QList<QUrl> selectedFiles = options()->initiallySelectedFiles();
+ if (selectedFiles.size() > 0) {
+ // TODO: The initial folder to show at the start should be handled by EXTRA_INITIAL_URI
+ // Take only the file name.
+ setInitialFileName(selectedFiles.first().fileName());
+ }
} else if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) {
switch (options()->fileMode()) {
case QFileDialogOptions::FileMode::DirectoryOnly:
@@ -232,8 +239,6 @@ bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::Win
setMimeTypes();
}
- setIntentTitle(options()->windowTitle());
-
QtAndroidPrivate::registerActivityResultListener(this);
m_activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V",
m_intent.object(), REQUEST_CODE);
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
index 2d3cb49a96..f573e72fef 100644
--- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
@@ -76,7 +76,7 @@ public:
private:
QJniObject getFileDialogIntent(const QString &intentType);
void takePersistableUriPermission(const QJniObject &uri);
- void setIntentTitle(const QString &title);
+ void setInitialFileName(const QString &title);
void setOpenableCategory();
void setAllowMultipleSelections(bool allowMultiple);
void setMimeTypes();
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp
index c9ef977816..8f2b7970f1 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.cpp
+++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp
@@ -472,14 +472,10 @@ QPlatformTheme *QAndroidPlatformIntegration::createPlatformTheme(const QString &
return 0;
}
-void QAndroidPlatformIntegration::setDefaultDisplayMetrics(int availableLeft,
- int availableTop,
- int availableWidth,
- int availableHeight,
- int physicalWidth,
- int physicalHeight,
- int screenWidth,
- int screenHeight)
+void QAndroidPlatformIntegration::setDefaultDisplayMetrics(int availableLeft, int availableTop,
+ int availableWidth, int availableHeight,
+ int physicalWidth, int physicalHeight,
+ int screenWidth, int screenHeight)
{
m_defaultAvailableGeometry = QRect(availableLeft, availableTop,
availableWidth, availableHeight);
@@ -526,6 +522,12 @@ void QAndroidPlatformIntegration::setScreenSize(int width, int height)
QMetaObject::invokeMethod(m_primaryScreen, "setSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height)));
}
+void QAndroidPlatformIntegration::setRefreshRate(qreal refreshRate)
+{
+ if (m_primaryScreen)
+ QMetaObject::invokeMethod(m_primaryScreen, "setRefreshRate", Qt::AutoConnection,
+ Q_ARG(qreal, refreshRate));
+}
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QAndroidPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.h b/src/plugins/platforms/android/qandroidplatformintegration.h
index 52442e87af..4fffed651b 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.h
+++ b/src/plugins/platforms/android/qandroidplatformintegration.h
@@ -99,6 +99,7 @@ public:
void setAvailableGeometry(const QRect &availableGeometry);
void setPhysicalSize(int width, int height);
void setScreenSize(int width, int height);
+ void setRefreshRate(qreal refreshRate);
bool isVirtualDesktop() { return true; }
QPlatformFontDatabase *fontDatabase() const override;
@@ -121,14 +122,9 @@ public:
QStringList themeNames() const override;
QPlatformTheme *createPlatformTheme(const QString &name) const override;
- static void setDefaultDisplayMetrics(int availableLeft,
- int availableTop,
- int availableWidth,
- int availableHeight,
- int physicalWidth,
- int physicalHeight,
- int screenWidth,
- int screenHeight);
+ static void setDefaultDisplayMetrics(int availableLeft, int availableTop, int availableWidth,
+ int availableHeight, int physicalWidth, int physicalHeight,
+ int screenWidth, int screenHeight);
static void setScreenOrientation(Qt::ScreenOrientation currentOrientation,
Qt::ScreenOrientation nativeOrientation);
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp
index 6a83827262..59c8f396af 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.cpp
+++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp
@@ -55,10 +55,11 @@
#include <android/native_window_jni.h>
#include <qguiapplication.h>
+#include <QtCore/QJniObject>
+#include <QtCore/QJniEnvironment>
#include <QtGui/QGuiApplication>
#include <QtGui/QWindow>
#include <QtGui/private/qwindow_p.h>
-
#include <vector>
QT_BEGIN_NAMESPACE
@@ -103,6 +104,46 @@ QAndroidPlatformScreen::QAndroidPlatformScreen()
}
m_physicalSize = QAndroidPlatformIntegration::m_defaultPhysicalSize;
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidPlatformScreen::applicationStateChanged);
+
+ QJniObject activity(QtAndroid::activity());
+ if (!activity.isValid())
+ return;
+ QJniObject display;
+ if (QNativeInterface::QAndroidApplication::sdkVersion() < 30) {
+ display = activity.callObjectMethod("getWindowManager", "()Landroid/view/WindowManager;")
+ .callObjectMethod("getDefaultDisplay", "()Landroid/view/Display;");
+ } else {
+ display = activity.callObjectMethod("getDisplay", "()Landroid/view/Display;");
+ }
+ if (!display.isValid())
+ return;
+
+ m_name = display.callObjectMethod("getName", "()Ljava/lang/String;").toString();
+ m_refreshRate = display.callMethod<jfloat>("getRefreshRate");
+
+ if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) {
+ m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
+ return;
+ }
+
+ QJniEnvironment env;
+ const jint currentMode = display.callObjectMethod("getMode", "()Landroid/view/Display$Mode;")
+ .callMethod<jint>("getModeId");
+ const auto modes = display.callObjectMethod("getSupportedModes",
+ "()[Landroid/view/Display$Mode;");
+ const auto modesArray = jobjectArray(modes.object());
+ const auto sz = env->GetArrayLength(modesArray);
+ for (jsize i = 0; i < sz; ++i) {
+ auto mode = QJniObject::fromLocalRef(env->GetObjectArrayElement(modesArray, i));
+ if (currentMode == mode.callMethod<jint>("getModeId"))
+ m_currentMode = m_modes.size();
+ m_modes << Mode { .size = QSize { mode.callMethod<jint>("getPhysicalHeight"),
+ mode.callMethod<jint>("getPhysicalWidth") },
+ .refreshRate = mode.callMethod<jfloat>("getRefreshRate") };
+ }
+
+ if (m_modes.isEmpty())
+ m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
}
QAndroidPlatformScreen::~QAndroidPlatformScreen()
@@ -158,7 +199,7 @@ void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window)
}
QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
+ QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
}
@@ -178,7 +219,7 @@ void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window)
}
QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
+ QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
}
@@ -195,7 +236,7 @@ void QAndroidPlatformScreen::raise(QAndroidPlatformWindow *window)
setDirty(window->geometry());
}
QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
+ QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
}
@@ -212,7 +253,7 @@ void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window)
setDirty(window->geometry());
}
QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
+ QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
}
@@ -242,6 +283,14 @@ void QAndroidPlatformScreen::setSize(const QSize &size)
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
}
+void QAndroidPlatformScreen::setRefreshRate(qreal refreshRate)
+{
+ if (refreshRate == m_refreshRate)
+ return;
+ m_refreshRate = refreshRate;
+ QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), refreshRate);
+}
+
void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
{
QMutexLocker lock(&m_surfaceMutex);
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h
index edddc9c557..e0de85277a 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.h
+++ b/src/plugins/platforms/android/qandroidplatformscreen.h
@@ -69,6 +69,12 @@ public:
QImage::Format format() const override { return m_format; }
QSizeF physicalSize() const override { return m_physicalSize; }
+ QString name() const override { return m_name; }
+ QList<Mode> modes() const override { return m_modes; }
+ int currentMode() const override { return m_currentMode; }
+ int preferredMode() const override { return m_currentMode; }
+ qreal refreshRate() const override { return m_refreshRate; }
+
inline QWindow *topWindow() const;
QWindow *topLevelAt(const QPoint & p) const override;
@@ -87,6 +93,7 @@ public slots:
void setPhysicalSize(const QSize &size);
void setAvailableGeometry(const QRect &rect);
void setSize(const QSize &size);
+ void setRefreshRate(qreal refreshRate);
protected:
bool event(QEvent *event) override;
@@ -100,6 +107,10 @@ protected:
int m_depth;
QImage::Format m_format;
QSizeF m_physicalSize;
+ qreal m_refreshRate;
+ QString m_name;
+ QList<Mode> m_modes;
+ int m_currentMode = 0;
private:
QDpi logicalDpi() const override;
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index 8b346fa89f..8e5ecd0f69 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -75,6 +75,7 @@
#include "qcocoaapplicationdelegate.h"
#include "qcocoaintegration.h"
+#include "qcocoamenubar.h"
#include "qcocoamenu.h"
#include "qcocoamenuloader.h"
#include "qcocoamenuitem.h"
@@ -230,6 +231,8 @@ QT_USE_NAMESPACE
// (See the activateIgnoringOtherApps docs.)
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}
+
+ QCocoaMenuBar::insertWindowMenu();
}
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index 374dc97f7d..a91407da5d 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -71,6 +71,8 @@ public:
QPaintDevice *paintDevice() override;
void endPaint() override;
+ bool scroll(const QRegion &region, int dx, int dy) override;
+
void flush(QWindow *, const QRegion &, const QPoint &) override;
#ifndef QT_NO_OPENGL
void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
@@ -85,7 +87,6 @@ private:
bool eventFilter(QObject *watched, QEvent *event) override;
QSize m_requestedSize;
- QRegion m_paintedRegion;
class GraphicsBuffer : public QIOSurfaceGraphicsBuffer
{
@@ -96,15 +97,21 @@ private:
QRegion dirtyRegion; // In unscaled coordinates
QImage *asImage();
qreal devicePixelRatio() const { return m_devicePixelRatio; }
+ bool isDirty() const { return !dirtyRegion.isEmpty(); }
+ QRegion validRegion() const;
private:
qreal m_devicePixelRatio;
QImage m_image;
};
+ void updateDirtyStates(const QRegion &paintedRegion);
+
void ensureBackBuffer();
bool recreateBackBufferIfNeeded();
- bool prepareForFlush();
+ void finalizeBackBuffer();
+
+ void preserveFromFrontBuffer(const QRegion &region, const QPoint &offset = QPoint());
void backingPropertiesChanged();
QMacNotificationObserver m_backingPropertiesObserver;
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index 994ab3036c..a09176cfaa 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -67,7 +67,9 @@ QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const
QCALayerBackingStore::QCALayerBackingStore(QWindow *window)
: QCocoaBackingStore(window)
{
- qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window;
+ qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window
+ << "with" << window->format();
+
m_buffers.resize(1);
observeBackingPropertiesChanges();
@@ -138,7 +140,8 @@ void QCALayerBackingStore::beginPaint(const QRegion &region)
painter.fillRect(rect, Qt::transparent);
}
- m_paintedRegion += region;
+ // We assume the client is going to paint the entire region
+ updateDirtyStates(region);
}
void QCALayerBackingStore::ensureBackBuffer()
@@ -146,13 +149,6 @@ void QCALayerBackingStore::ensureBackBuffer()
if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer)
return;
- // The current back buffer may have been assigned to a layer in a previous flush,
- // but we deferred the swap. Do it now if the surface has been picked up by CA.
- if (m_buffers.back() && m_buffers.back()->isInUse() && m_buffers.back() != m_buffers.front()) {
- qCInfo(lcQpaBackingStore) << "Back buffer has been picked up by CA, swapping to front";
- std::swap(m_buffers.back(), m_buffers.front());
- }
-
if (Q_UNLIKELY(lcQpaBackingStore().isDebugEnabled())) {
// ┌───────┬───────┬───────┬─────┬──────┐
// │ front ┊ spare ┊ spare ┊ ... ┊ back │
@@ -247,8 +243,66 @@ QPaintDevice *QCALayerBackingStore::paintDevice()
void QCALayerBackingStore::endPaint()
{
- qCInfo(lcQpaBackingStore) << "Paint ended with painted region" << m_paintedRegion;
+ qCInfo(lcQpaBackingStore) << "Paint ended. Back buffer valid region is now" << m_buffers.back()->validRegion();
+ m_buffers.back()->unlock();
+
+ // Since we can have multiple begin/endPaint rounds before a flush
+ // we defer finalizing the back buffer until its content is needed.
+}
+
+bool QCALayerBackingStore::scroll(const QRegion &region, int dx, int dy)
+{
+ if (!m_buffers.back()) {
+ qCInfo(lcQpaBackingStore) << "Scroll requested with no back buffer. Ignoring.";
+ return false;
+ }
+
+ const QPoint scrollDelta(dx, dy);
+ qCInfo(lcQpaBackingStore) << "Scrolling" << region << "by" << scrollDelta;
+
+ ensureBackBuffer();
+ recreateBackBufferIfNeeded();
+
+ const QRegion inPlaceRegion = region - m_buffers.back()->dirtyRegion;
+ const QRegion frontBufferRegion = region - inPlaceRegion;
+
+ QMacAutoReleasePool pool;
+
+ m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
+
+ if (!inPlaceRegion.isEmpty()) {
+ qCDebug(lcQpaBackingStore) << "Scrolling" << inPlaceRegion << "in place";
+ QImage *backBufferImage = m_buffers.back()->asImage();
+ const qreal devicePixelRatio = backBufferImage->devicePixelRatio();
+ const QPoint devicePixelDelta = scrollDelta * devicePixelRatio;
+
+ extern void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &);
+
+ for (const QRect &rect : inPlaceRegion) {
+ qt_scrollRectInImage(*backBufferImage,
+ QRect(rect.topLeft() * devicePixelRatio,
+ rect.size() * devicePixelRatio),
+ devicePixelDelta);
+ }
+
+ }
+
+ if (!frontBufferRegion.isEmpty()) {
+ qCDebug(lcQpaBackingStore) << "Scrolling" << frontBufferRegion << "by copying from front buffer";
+ preserveFromFrontBuffer(frontBufferRegion, scrollDelta);
+ }
+
m_buffers.back()->unlock();
+
+ // Mark the target region as filled. Note: We do not mark the source region
+ // as dirty, even though the content has conceptually been "moved", as that
+ // would complicate things when preserving from the front buffer. This matches
+ // the behavior of other backingstore implementations using qt_scrollRectInImage.
+ updateDirtyStates(region.translated(scrollDelta));
+
+ qCInfo(lcQpaBackingStore) << "Scroll ended. Back buffer valid region is now" << m_buffers.back()->validRegion();
+
+ return true;
}
void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region, const QPoint &offset)
@@ -256,14 +310,23 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
Q_UNUSED(region);
Q_UNUSED(offset);
- if (!prepareForFlush())
+ if (!m_buffers.back()) {
+ qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first";
return;
+ }
+
+ finalizeBackBuffer();
if (flushedWindow != window()) {
flushSubWindow(flushedWindow);
return;
}
+ if (m_buffers.front()->isInUse() && !m_buffers.front()->isDirty()) {
+ qCInfo(lcQpaBackingStore) << "Asked to flush, but front buffer is up to date. Ignoring.";
+ return;
+ }
+
QMacAutoReleasePool pool;
NSView *flushedView = static_cast<QCocoaWindow *>(flushedWindow->handle())->view();
@@ -287,16 +350,6 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
const bool isSingleBuffered = window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer;
id backBufferSurface = (__bridge id)m_buffers.back()->surface();
- if (!isSingleBuffered && flushedView.layer.contents == backBufferSurface) {
- // We've managed to paint to the back buffer again before Core Animation had time
- // to flush the transaction and persist the layer changes to the window server, or
- // we've been asked to flush without painting anything. The layer already knows about
- // the back buffer, and we don't need to re-apply it to pick up any possible surface
- // changes, so bail out early.
- qCInfo(lcQpaBackingStore).nospace() << "Skipping flush of " << flushedView
- << ", layer already reflects back buffer";
- return;
- }
// Trigger a new display cycle if there isn't one. This ensures that our layer updates
// are committed as part of a display-cycle instead of on the next runloop pass. This
@@ -315,14 +368,17 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
flushedView.layer.contents = backBufferSurface;
- // Since we may receive multiple flushes before a new frame is started, we do not
- // swap any buffers just yet. Instead we check in the next beginPaint if the layer's
- // surface is in use, and if so swap to an unused surface as the new back buffer.
+ if (!isSingleBuffered) {
+ // Mark the surface as in use, so that we don't end up rendering
+ // to it while it's assigned to a layer.
+ IOSurfaceIncrementUseCount(m_buffers.back()->surface());
- // Note: Ideally CoreAnimation would mark a surface as in use the moment we assign
- // it to a layer, but as that's not the case we may end up painting to the same back
- // buffer once more if we are painting faster than CA can ship the surfaces over to
- // the window server.
+ if (m_buffers.back() != m_buffers.front()) {
+ qCInfo(lcQpaBackingStore) << "Swapping back buffer to front";
+ std::swap(m_buffers.back(), m_buffers.front());
+ IOSurfaceDecrementUseCount(m_buffers.back()->surface());
+ }
+ }
}
void QCALayerBackingStore::flushSubWindow(QWindow *subWindow)
@@ -376,8 +432,12 @@ void QCALayerBackingStore::windowDestroyed(QObject *object)
void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, bool translucentBackground)
{
- if (!prepareForFlush())
+ if (!m_buffers.back()) {
+ qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first";
return;
+ }
+
+ finalizeBackBuffer();
QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground);
}
@@ -385,9 +445,11 @@ void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion &regio
QImage QCALayerBackingStore::toImage() const
{
- if (!const_cast<QCALayerBackingStore*>(this)->prepareForFlush())
+ if (!m_buffers.back())
return QImage();
+ const_cast<QCALayerBackingStore*>(this)->finalizeBackBuffer();
+
// We need to make a copy here, as the returned image could be used just
// for reading, in which case it won't detach, and then the underlying
// image data might change under the feet of the client when we re-use
@@ -421,69 +483,77 @@ QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const
return m_buffers.back().get();
}
-bool QCALayerBackingStore::prepareForFlush()
+void QCALayerBackingStore::updateDirtyStates(const QRegion &paintedRegion)
{
- if (!m_buffers.back()) {
- qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first";
- return false;
- }
-
// Update dirty state of buffers based on what was painted. The back buffer will be
// less dirty, since we painted to it, while other buffers will become more dirty.
// This allows us to minimize copies between front and back buffers on swap in the
// cases where the painted region overlaps with the previous frame (front buffer).
for (const auto &buffer : m_buffers) {
if (buffer == m_buffers.back())
- buffer->dirtyRegion -= m_paintedRegion;
+ buffer->dirtyRegion -= paintedRegion;
else
- buffer->dirtyRegion += m_paintedRegion;
+ buffer->dirtyRegion += paintedRegion;
}
+}
+void QCALayerBackingStore::finalizeBackBuffer()
+{
// After painting, the back buffer is only guaranteed to have content for the painted
// region, and may still have dirty areas that need to be synced up with the front buffer,
// if we have one. We know that the front buffer is always up to date.
- if (!m_buffers.back()->dirtyRegion.isEmpty() && m_buffers.front() != m_buffers.back()) {
- QRegion preserveRegion = m_buffers.back()->dirtyRegion;
- qCDebug(lcQpaBackingStore) << "Preserving" << preserveRegion << "from front to back buffer";
- m_buffers.front()->lock(QPlatformGraphicsBuffer::SWReadAccess);
- const QImage *frontBuffer = m_buffers.front()->asImage();
+ if (!m_buffers.back()->isDirty())
+ return;
- const QRect frontSurfaceBounds(QPoint(0, 0), m_buffers.front()->size());
- const qreal sourceDevicePixelRatio = frontBuffer->devicePixelRatio();
+ m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
+ preserveFromFrontBuffer(m_buffers.back()->dirtyRegion);
+ m_buffers.back()->unlock();
- m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
- QPainter painter(m_buffers.back()->asImage());
- painter.setCompositionMode(QPainter::CompositionMode_Source);
+ // The back buffer is now completely in sync, ready to be presented
+ m_buffers.back()->dirtyRegion = QRegion();
+}
- // Let painter operate in device pixels, to make it easier to compare coordinates
- const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio();
- painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio);
+void QCALayerBackingStore::preserveFromFrontBuffer(const QRegion &region, const QPoint &offset)
+{
- for (const QRect &rect : preserveRegion) {
- QRect sourceRect(rect.topLeft() * sourceDevicePixelRatio, rect.size() * sourceDevicePixelRatio);
- QRect targetRect(rect.topLeft() * targetDevicePixelRatio, rect.size() * targetDevicePixelRatio);
+ if (m_buffers.front() == m_buffers.back())
+ return; // Nothing to preserve from
-#ifdef QT_DEBUG
- if (Q_UNLIKELY(!frontSurfaceBounds.contains(sourceRect.bottomRight()))) {
- qCWarning(lcQpaBackingStore) << "Front buffer too small to preserve"
- << QRegion(sourceRect).subtracted(frontSurfaceBounds);
- }
-#endif
- painter.drawImage(targetRect, *frontBuffer, sourceRect);
- }
+ qCDebug(lcQpaBackingStore) << "Preserving" << region << "of front buffer to"
+ << region.translated(offset) << "of back buffer";
- m_buffers.back()->unlock();
- m_buffers.front()->unlock();
+ Q_ASSERT(m_buffers.back()->isLocked() == QPlatformGraphicsBuffer::SWWriteAccess);
- // The back buffer is now completely in sync, ready to be presented
- m_buffers.back()->dirtyRegion = QRegion();
- }
+ m_buffers.front()->lock(QPlatformGraphicsBuffer::SWReadAccess);
+ const QImage *frontBuffer = m_buffers.front()->asImage();
- // Prepare for another round of painting
- m_paintedRegion = QRegion();
+ const QRect frontSurfaceBounds(QPoint(0, 0), m_buffers.front()->size());
+ const qreal sourceDevicePixelRatio = frontBuffer->devicePixelRatio();
- return true;
+ QPainter painter(m_buffers.back()->asImage());
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+
+ // Let painter operate in device pixels, to make it easier to compare coordinates
+ const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio();
+ painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio);
+
+ for (const QRect &rect : region) {
+ QRect sourceRect(rect.topLeft() * sourceDevicePixelRatio,
+ rect.size() * sourceDevicePixelRatio);
+ QRect targetRect((rect.topLeft() + offset) * targetDevicePixelRatio,
+ rect.size() * targetDevicePixelRatio);
+
+#ifdef QT_DEBUG
+ if (Q_UNLIKELY(!frontSurfaceBounds.contains(sourceRect.bottomRight()))) {
+ qCWarning(lcQpaBackingStore) << "Front buffer too small to preserve"
+ << QRegion(sourceRect).subtracted(frontSurfaceBounds);
+ }
+#endif
+ painter.drawImage(targetRect, *frontBuffer, sourceRect);
+ }
+
+ m_buffers.front()->unlock();
}
// ----------------------------------------------------------------------------
@@ -491,12 +561,19 @@ bool QCALayerBackingStore::prepareForFlush()
QCALayerBackingStore::GraphicsBuffer::GraphicsBuffer(const QSize &size, qreal devicePixelRatio,
const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace)
: QIOSurfaceGraphicsBuffer(size, format)
- , dirtyRegion(0, 0, size.width() / devicePixelRatio, size.height() / devicePixelRatio)
+ , dirtyRegion(QRect(QPoint(0, 0), size / devicePixelRatio))
, m_devicePixelRatio(devicePixelRatio)
{
setColorSpace(colorSpace);
}
+QRegion QCALayerBackingStore::GraphicsBuffer::validRegion() const
+{
+
+ QRegion fullRegion = QRect(QPoint(0, 0), size() / m_devicePixelRatio);
+ return fullRegion - dirtyRegion;
+}
+
QImage *QCALayerBackingStore::GraphicsBuffer::asImage()
{
if (m_image.isNull()) {
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index 40a7a6ef5e..026a99a152 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -422,6 +422,15 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication],
NSApplicationDidChangeScreenParametersNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view,
+ QCocoaWindowWillReleaseQNSViewNotification, [this, view] {
+ if (QT_IGNORE_DEPRECATIONS(m_context.view) != view)
+ return;
+ qCDebug(lcQpaOpenGLContext) << view << "about to be released."
+ << "Clearing current drawable for" << m_context;
+ [m_context clearDrawable];
+ }));
+
// If any of the observers fire at this point it's fine. We check the
// view association (atomically) in the update callback, and skip the
// update if we haven't associated yet. Setting the drawable below will
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index b8954509b9..92edeac558 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -57,6 +57,7 @@
#if QT_CONFIG(sessionmanager)
# include "qcocoasessionmanager.h"
#endif
+#include "qcocoawindowmanager.h"
#include <qpa/qplatforminputcontextfactory_p.h>
#include <qpa/qplatformaccessibility.h>
@@ -81,6 +82,11 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpa, "qt.qpa", QtWarningMsg);
+// Lives here so that the linker is forced to include the QCocoaWindowManager
+// object file also in static builds.
+static void initializeWindowManager() { Q_UNUSED(QCocoaWindowManager::instance()); }
+Q_CONSTRUCTOR_FUNCTION(initializeWindowManager)
+
static void logVersionInformation()
{
if (!lcQpa().isInfoEnabled())
diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h
index 0054cdf6ad..6f8409434c 100644
--- a/src/plugins/platforms/cocoa/qcocoamenubar.h
+++ b/src/plugins/platforms/cocoa/qcocoamenubar.h
@@ -67,6 +67,7 @@ public:
NSMenu *nsMenu() const override { return m_nativeMenu; }
static void updateMenuBarImmediately();
+ static void insertWindowMenu();
QList<QCocoaMenuItem*> merged() const;
NSMenuItem *itemForRole(QPlatformMenuItem::MenuRole role);
diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm
index 524746f952..365346ac8c 100644
--- a/src/plugins/platforms/cocoa/qcocoamenubar.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm
@@ -333,9 +333,36 @@ void QCocoaMenuBar::updateMenuBarImmediately()
[mergedItems release];
[NSApp setMainMenu:mb->nsMenu()];
+ insertWindowMenu();
[loader qtTranslateApplicationMenu];
}
+void QCocoaMenuBar::insertWindowMenu()
+{
+ // For such an item/menu we get for 'free' an additional feature -
+ // a list of windows the application has created in the Dock's menu.
+
+ NSApplication *app = NSApplication.sharedApplication;
+ if (app.windowsMenu)
+ return;
+
+ NSMenu *mainMenu = app.mainMenu;
+ NSMenuItem *winMenuItem = [[[NSMenuItem alloc] initWithTitle:@"QtWindowMenu"
+ action:nil keyEquivalent:@""] autorelease];
+ // We don't want to show this menu, nobody asked us to do so:
+ winMenuItem.hidden = YES;
+
+ winMenuItem.submenu = [[[NSMenu alloc] initWithTitle:@"QtWindowMenu"] autorelease];
+ [mainMenu insertItem:winMenuItem atIndex:mainMenu.itemArray.count];
+ app.windowsMenu = winMenuItem.submenu;
+
+ // Windows, created and 'ordered front' before, will not be in this menu:
+ for (NSWindow *win in app.windows) {
+ if (win.title && ![win.title isEqualToString:@""])
+ [app addWindowsItem:win title:win.title filename:NO];
+ }
+}
+
QList<QCocoaMenuItem*> QCocoaMenuBar::merged() const
{
QList<QCocoaMenuItem*> r;
diff --git a/src/plugins/platforms/cocoa/qcocoaresources.qrc b/src/plugins/platforms/cocoa/qcocoaresources.qrc
deleted file mode 100644
index 1c4b941b9b..0000000000
--- a/src/plugins/platforms/cocoa/qcocoaresources.qrc
+++ /dev/null
@@ -1,7 +0,0 @@
-<RCC>
- <qresource prefix="/qt-project.org/mac/cursors">
- <file>images/spincursor.png</file>
- <file>images/waitcursor.png</file>
- <file>images/sizeallcursor.png</file>
- </qresource>
-</RCC>
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index 6b2dfafd92..dfc1c8197b 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -527,6 +527,8 @@ QVariant QCocoaTheme::themeHint(ThemeHint hint) const
return QVariant(bool([[NSApplication sharedApplication] presentationOptions] & NSApplicationPresentationFullScreen));
case QPlatformTheme::InteractiveResizeAcrossScreens:
return !NSScreen.screensHaveSeparateSpaces;
+ case QPlatformTheme::ShowDirectoriesFirst:
+ return false;
default:
break;
}
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h
index e3152f153c..c0684504aa 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.h
+++ b/src/plugins/platforms/cocoa/qcocoawindow.h
@@ -295,6 +295,8 @@ public: // for QNSView
#endif
};
+extern const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification;
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QCocoaWindow *window);
#endif
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 1bc53bde5c..70cfa42974 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -186,6 +186,8 @@ void QCocoaWindow::initialize()
m_initialized = true;
}
+const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification = @"QCocoaWindowWillReleaseQNSViewNotification";
+
QCocoaWindow::~QCocoaWindow()
{
qCDebug(lcQpaWindow) << "QCocoaWindow::~QCocoaWindow" << window();
@@ -209,6 +211,13 @@ QCocoaWindow::~QCocoaWindow()
}
#endif
+ // Must send notification before calling release, as doing it from
+ // [QNSView dealloc] would mean that any weak references to the view
+ // would already return nil.
+ [NSNotificationCenter.defaultCenter
+ postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
+ object:m_view];
+
[m_view release];
[m_nsWindow close];
[m_nsWindow release];
diff --git a/src/plugins/platforms/cocoa/qcocoawindowmanager.mm b/src/plugins/platforms/cocoa/qcocoawindowmanager.mm
index bb72fea5e6..71bcecdbc6 100644
--- a/src/plugins/platforms/cocoa/qcocoawindowmanager.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindowmanager.mm
@@ -116,8 +116,5 @@ void QCocoaWindowManager::modalSessionChanged()
}
}
-static void initializeWindowManager() { Q_UNUSED(QCocoaWindowManager::instance()); }
-Q_CONSTRUCTOR_FUNCTION(initializeWindowManager)
-
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h
index 5d4b6d6a71..cc7193d8b7 100644
--- a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h
+++ b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h
@@ -43,6 +43,7 @@
#include <qpa/qplatformgraphicsbuffer.h>
#include <private/qcore_mac_p.h>
+#include <CoreGraphics/CGColorSpace.h>
#include <IOSurface/IOSurface.h>
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qnsview_complextext.mm b/src/plugins/platforms/cocoa/qnsview_complextext.mm
index 39739d9725..c1e1364f32 100644
--- a/src/plugins/platforms/cocoa/qnsview_complextext.mm
+++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm
@@ -145,7 +145,9 @@
// e.g. "~\r". We have already inserted the composition,
// so we need to follow up with a single newline event.
- KeyEvent newlineEvent(NSApp.currentEvent);
+ KeyEvent newlineEvent(m_currentlyInterpretedKeyEvent ?
+ m_currentlyInterpretedKeyEvent : NSApp.currentEvent);
+ newlineEvent.type = QEvent::KeyPress;
newlineEvent.key = Qt::Key_Return;
newlineEvent.text = QLatin1Char(kReturnCharCode);
newlineEvent.nativeVirtualKey = kVK_Return;
diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm
index 96ab7ab168..01c3a4ea0d 100644
--- a/src/plugins/platforms/cocoa/qnsview_keys.mm
+++ b/src/plugins/platforms/cocoa/qnsview_keys.mm
@@ -234,6 +234,8 @@
KeyEvent::KeyEvent(NSEvent *nsevent)
{
timestamp = nsevent.timestamp * 1000;
+ nativeModifiers = nsevent.modifierFlags;
+ modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
switch (nsevent.type) {
case NSEventTypeKeyDown: type = QEvent::KeyPress; break;
@@ -242,6 +244,8 @@ KeyEvent::KeyEvent(NSEvent *nsevent)
}
if (nsevent.type == NSEventTypeKeyDown || nsevent.type == NSEventTypeKeyUp) {
+ nativeVirtualKey = nsevent.keyCode;
+
NSString *charactersIgnoringModifiers = nsevent.charactersIgnoringModifiers;
NSString *characters = nsevent.characters;
@@ -269,11 +273,6 @@ KeyEvent::KeyEvent(NSEvent *nsevent)
isRepeat = nsevent.ARepeat;
}
-
- nativeVirtualKey = nsevent.keyCode;
-
- nativeModifiers = nsevent.modifierFlags;
- modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
}
bool KeyEvent::sendWindowSystemEvent(QWindow *window) const
diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm
index 559257c4df..d977867890 100644
--- a/src/plugins/platforms/cocoa/qnsview_mouse.mm
+++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm
@@ -714,14 +714,18 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
// had time to emit a momentum phase event.
if ([NSApp nextEventMatchingMask:NSEventMaskScrollWheel untilDate:[NSDate distantPast]
inMode:@"QtMomementumEventSearchMode" dequeue:NO].momentumPhase == NSEventPhaseBegan) {
- Q_ASSERT(pixelDelta.isNull() && angleDelta.isNull());
- return; // Ignore this event, as it has a delta of 0,0
+ return; // Ignore, even if it has delta
+ } else {
+ phase = Qt::ScrollEnd;
+ m_scrolling = false;
}
- phase = Qt::ScrollEnd;
- m_scrolling = false;
} else if (theEvent.momentumPhase == NSEventPhaseBegan) {
Q_ASSERT(!pixelDelta.isNull() && !angleDelta.isNull());
- phase = Qt::ScrollUpdate; // Send as update, it has a delta
+ // If we missed finding a momentum NSEventPhaseBegan when the non-momentum
+ // phase ended we need to treat this as a scroll begin, to not confuse client
+ // code. Otherwise we treat it as a continuation of the existing scroll.
+ phase = m_scrolling ? Qt::ScrollUpdate : Qt::ScrollBegin;
+ m_scrolling = true;
} else if (theEvent.momentumPhase == NSEventPhaseChanged) {
phase = Qt::ScrollMomentum;
} else if (theEvent.phase == NSEventPhaseCancelled
@@ -733,6 +737,16 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
Q_ASSERT(theEvent.momentumPhase != NSEventPhaseStationary);
}
+ // Sanitize deltas for events that should not result in scrolling.
+ // On macOS 12.1 this phase has been observed to report deltas.
+ if (theEvent.phase == NSEventPhaseCancelled) {
+ if (!pixelDelta.isNull() || !angleDelta.isNull()) {
+ qCInfo(lcQpaMouse) << "Ignoring unexpected delta for" << theEvent;
+ pixelDelta = QPoint();
+ angleDelta = QPoint();
+ }
+ }
+
// Prevent keyboard modifier state from changing during scroll event streams.
// A two-finger trackpad flick generates a stream of scroll events. We want
// the keyboard modifier state to be the state at the beginning of the
diff --git a/src/plugins/platforms/cocoa/qnsview_tablet.mm b/src/plugins/platforms/cocoa/qnsview_tablet.mm
index 318a32f7cf..81d90a6fbe 100644
--- a/src/plugins/platforms/cocoa/qnsview_tablet.mm
+++ b/src/plugins/platforms/cocoa/qnsview_tablet.mm
@@ -224,7 +224,7 @@ static const QPointingDevice *tabletToolInstance(NSEvent *theEvent)
QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(nullptr, timestamp, device, theEvent.isEnteringProximity);
// The windowSystemId starts at 0, but is "unique" while in proximity
if (theEvent.isEnteringProximity)
- devicesInProximity->insert(windowSystemId, device);
+ devicesInProximity->insert_or_assign(windowSystemId, device);
else
devicesInProximity->remove(windowSystemId);
}
diff --git a/src/plugins/platforms/direct2d/CMakeLists.txt b/src/plugins/platforms/direct2d/CMakeLists.txt
index e87d2886d8..b88ac617aa 100644
--- a/src/plugins/platforms/direct2d/CMakeLists.txt
+++ b/src/plugins/platforms/direct2d/CMakeLists.txt
@@ -72,6 +72,10 @@ qt_internal_add_plugin(QWindowsDirect2DIntegrationPlugin
winmm
winspool
wtsapi32
+ shcore
+ comdlg32
+ d3d9
+ runtimeobject
)
# Resources:
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
index 9ff107e7df..052ddee5e4 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
@@ -1039,7 +1039,7 @@ public:
const QString nameSubstitute = QSettings(QLatin1String(keyC), QSettings::NativeFormat).value(familyName, familyName).toString();
if (nameSubstitute != familyName) {
const int nameSubstituteLength = qMin(nameSubstitute.length(), LF_FACESIZE - 1);
- memcpy(lf.lfFaceName, nameSubstitute.utf16(), size_t(nameSubstituteLength) * sizeof(wchar_t));
+ memcpy(lf.lfFaceName, nameSubstitute.data(), size_t(nameSubstituteLength) * sizeof(wchar_t));
lf.lfFaceName[nameSubstituteLength] = 0;
}
diff --git a/src/plugins/platforms/directfb/qdirectfbinput.cpp b/src/plugins/platforms/directfb/qdirectfbinput.cpp
index 025edb28b5..c2b4adbd16 100644
--- a/src/plugins/platforms/directfb/qdirectfbinput.cpp
+++ b/src/plugins/platforms/directfb/qdirectfbinput.cpp
@@ -209,7 +209,7 @@ void QDirectFbInput::handleEnterLeaveEvents(const DFBEvent &event)
void QDirectFbInput::handleGotFocusEvent(const DFBEvent &event)
{
QWindow *tlw = m_tlwMap.value(event.window.window_id);
- QWindowSystemInterface::handleWindowActivated(tlw);
+ QWindowSystemInterface::handleWindowActivated(tlw, Qt::ActiveWindowFocusReason);
}
void QDirectFbInput::handleCloseEvent(const DFBEvent &event)
diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
index 813de2fc47..09c102b66f 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
@@ -74,47 +74,17 @@ Q_LOGGING_CATEGORY(qLcEglDevDebug, "qt.qpa.egldeviceintegration")
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
(QEglFSDeviceIntegrationFactoryInterface_iid, QLatin1String("/egldeviceintegrations"), Qt::CaseInsensitive))
-#if QT_CONFIG(library)
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader,
- (QEglFSDeviceIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive))
-
-#endif // QT_CONFIG(library)
-
-QStringList QEglFSDeviceIntegrationFactory::keys(const QString &pluginPath)
+QStringList QEglFSDeviceIntegrationFactory::keys()
{
QStringList list;
-#if QT_CONFIG(library)
- if (!pluginPath.isEmpty()) {
- QCoreApplication::addLibraryPath(pluginPath);
- list = directLoader()->keyMap().values();
- if (!list.isEmpty()) {
- const QString postFix = QLatin1String(" (from ")
- + QDir::toNativeSeparators(pluginPath)
- + QLatin1Char(')');
- const QStringList::iterator end = list.end();
- for (QStringList::iterator it = list.begin(); it != end; ++it)
- (*it).append(postFix);
- }
- }
-#else
- Q_UNUSED(pluginPath);
-#endif
list.append(loader()->keyMap().values());
qCDebug(qLcEglDevDebug) << "EGL device integration plugin keys:" << list;
return list;
}
-QEglFSDeviceIntegration *QEglFSDeviceIntegrationFactory::create(const QString &key, const QString &pluginPath)
+QEglFSDeviceIntegration *QEglFSDeviceIntegrationFactory::create(const QString &key)
{
QEglFSDeviceIntegration *integration = nullptr;
-#if QT_CONFIG(library)
- if (!pluginPath.isEmpty()) {
- QCoreApplication::addLibraryPath(pluginPath);
- integration = qLoadPlugin<QEglFSDeviceIntegration, QEglFSDeviceIntegrationPlugin>(directLoader(), key);
- }
-#else
- Q_UNUSED(pluginPath);
-#endif
if (!integration)
integration = qLoadPlugin<QEglFSDeviceIntegration, QEglFSDeviceIntegrationPlugin>(loader(), key);
if (integration)
diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
index f8c0d6c508..4e43a7c37b 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
+++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
@@ -126,8 +126,8 @@ public:
class Q_EGLFS_EXPORT QEglFSDeviceIntegrationFactory
{
public:
- static QStringList keys(const QString &pluginPath = QString());
- static QEglFSDeviceIntegration *create(const QString &name, const QString &platformPluginPath = QString());
+ static QStringList keys();
+ static QEglFSDeviceIntegration *create(const QString &name);
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
index b1fdba5a46..47f6d88e5c 100644
--- a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
@@ -177,6 +177,8 @@ void QEglFSWindow::destroy()
#ifndef QT_NO_OPENGL
QOpenGLCompositor::destroy();
+ if (qt_gl_global_share_context() == m_rasterCompositingContext)
+ qt_gl_set_global_share_context(nullptr);
delete m_rasterCompositingContext;
#endif
}
@@ -265,7 +267,7 @@ void QEglFSWindow::requestActivateWindow()
QOpenGLCompositor::instance()->moveToTop(this);
#endif
QWindow *wnd = window();
- QWindowSystemInterface::handleWindowActivated(wnd);
+ QWindowSystemInterface::handleWindowActivated(wnd, Qt::ActiveWindowFocusReason);
QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
}
diff --git a/src/plugins/platforms/eglfs/cursor.qrc b/src/plugins/platforms/eglfs/cursor.qrc
deleted file mode 100644
index 8ea6e86587..0000000000
--- a/src/plugins/platforms/eglfs/cursor.qrc
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>cursor-atlas.png</file>
- <file>cursor.json</file>
-</qresource>
-</RCC>
-
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp
index 67606f895f..a3ac9205a9 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp
@@ -39,6 +39,7 @@
#include "qeglfskmseventreader_p.h"
#include "qeglfskmsdevice_p.h"
+#include "qeglfskmsscreen_p.h"
#include <QSocketNotifier>
#include <QCoreApplication>
#include <QLoggingCategory>
@@ -50,12 +51,12 @@ Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
static void pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data)
{
Q_UNUSED(fd);
- Q_UNUSED(sequence);
- Q_UNUSED(tv_sec);
- Q_UNUSED(tv_usec);
QEglFSKmsEventReaderThread *t = static_cast<QEglFSKmsEventReaderThread *>(QThread::currentThread());
t->eventHost()->handlePageFlipCompleted(user_data);
+
+ QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(user_data);
+ screen->pageFlipped(sequence, tv_sec, tv_usec);
}
class RegisterWaitFlipEvent : public QEvent
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
index 0a77e3e642..3bbdd55d34 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
@@ -254,4 +254,13 @@ void QEglFSKmsScreen::setPowerState(QPlatformScreen::PowerState state)
m_powerState = state;
}
+/* Informs exact page flip timing which can be used rendering optimization.
+ Consider this is from drm event reader thread. */
+void QEglFSKmsScreen::pageFlipped(unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec)
+{
+ Q_UNUSED(sequence);
+ Q_UNUSED(tv_sec);
+ Q_UNUSED(tv_usec);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h
index db0aca0bd2..16ed6f4c30 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h
@@ -115,6 +115,7 @@ public:
bool isCursorOutOfRange() const { return m_cursorOutOfRange; }
void setCursorOutOfRange(bool b) { m_cursorOutOfRange = b; }
+ virtual void pageFlipped(unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec);
protected:
QEglFSKmsDevice *m_device;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp
index b8f04cf978..edea3a3577 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp
@@ -154,23 +154,23 @@ EGLNativeWindowType QEglFSOpenWFDIntegration::createNativeWindow(QPlatformWindow
if (!ok)
pipelineId = pipelineIds[0];
- WFDPipeline pipeline = wfdCreatePipeline(mDevice, pipelineId, nullptr);
- if (WFD_INVALID_HANDLE == pipeline)
+ mPipeline = wfdCreatePipeline(mDevice, pipelineId, nullptr);
+ if (WFD_INVALID_HANDLE == mPipeline)
qFatal("Failed to create wfd pipeline");
- wfdSetPipelineAttribi(mDevice, pipeline, WFD_PIPELINE_TRANSPARENCY_ENABLE,
+ wfdSetPipelineAttribi(mDevice, mPipeline, WFD_PIPELINE_TRANSPARENCY_ENABLE,
(WFD_TRANSPARENCY_SOURCE_ALPHA|WFD_TRANSPARENCY_GLOBAL_ALPHA));
WFDErrorCode eError = wfdGetError(mDevice);
if (WFD_ERROR_NONE != eError)
qFatal("Failed to set WFD_PIPELINE_TRANSPARENCY_ENABLE");
- wfdSetPipelineAttribi(mDevice, pipeline, WFD_PIPELINE_GLOBAL_ALPHA, 255);
+ wfdSetPipelineAttribi(mDevice, mPipeline, WFD_PIPELINE_GLOBAL_ALPHA, 255);
eError = wfdGetError(mDevice);
if (WFD_ERROR_NONE != eError)
qFatal("Failed to set WFD_PIPELINE_GLOBAL_ALPHA");
- wfdBindPipelineToPort(mDevice, mPort, pipeline);
+ wfdBindPipelineToPort(mDevice, mPort, mPipeline);
eError = wfdGetError(mDevice);
if (WFD_ERROR_NONE != eError)
qFatal("Failed to bind port to pipeline");
@@ -190,7 +190,7 @@ EGLNativeWindowType QEglFSOpenWFDIntegration::createNativeWindow(QPlatformWindow
if (WFD_INVALID_HANDLE == wfdEglImages[i])
qFatal("Failed to create WDFEGLImages");
- source[i] = wfdCreateSourceFromImage(mDevice, pipeline, eglImageHandles[i], nullptr);
+ source[i] = wfdCreateSourceFromImage(mDevice, mPipeline, eglImageHandles[i], nullptr);
if (WFD_INVALID_HANDLE == source[i])
qFatal("Failed to create source from EGLImage");
}
@@ -208,14 +208,13 @@ EGLNativeWindowType QEglFSOpenWFDIntegration::createNativeWindow(QPlatformWindow
nativeWindow->dev = mDevice;
nativeWindow->port = mPort;
- nativeWindow->pipeline = pipeline;
+ nativeWindow->pipeline = mPipeline;
nativeWindow->numBuffers = MAX_NUM_OF_WFD_BUFFERS;
for (int i = 0; i < MAX_NUM_OF_WFD_BUFFERS; i++) {
nativeWindow->buffers[i].image = wfdEglImages[i];
nativeWindow->buffers[i].source = source[i];
}
-
return (EGLNativeWindowType)nativeWindow;
}
@@ -231,7 +230,16 @@ QSurfaceFormat QEglFSOpenWFDIntegration::surfaceFormatFor(const QSurfaceFormat &
void QEglFSOpenWFDIntegration::destroyNativeWindow(EGLNativeWindowType window)
{
+ wfdDestroyPipeline(mDevice, mPipeline);
free((void*)window);
}
+void QEglFSOpenWFDIntegration::platformDestroy()
+{
+ wfdDestroyPort(mDevice, mPort);
+ WFDErrorCode error = wfdDestroyDevice(mDevice);
+ if (error != WFD_ERROR_NONE)
+ qWarning() << "Failed to release wfd device! Error = " << error;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.h
index 189ddd4d7a..ab2105c552 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.h
@@ -51,6 +51,7 @@ class QEglFSOpenWFDIntegration : public QEglFSDeviceIntegration
{
public:
void platformInit() override;
+ void platformDestroy() override;
QSize screenSize() const override;
EGLNativeWindowType createNativeWindow(QPlatformWindow *window, const QSize &size, const QSurfaceFormat &format) override;
void destroyNativeWindow(EGLNativeWindowType window) override;
@@ -62,6 +63,7 @@ private:
EGLNativeDisplayType mNativeDisplay;
WFDDevice mDevice;
WFDPort mPort;
+ WFDPipeline mPipeline;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h
index 8d7cabf15b..3bb9fab1fc 100644
--- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h
+++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h
@@ -54,7 +54,7 @@ public:
QIOSFileEngineAssetsLibrary(const QString &fileName);
~QIOSFileEngineAssetsLibrary();
- bool open(QIODevice::OpenMode openMode) override;
+ bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override;
bool close() override;
FileFlags fileFlags(FileFlags type) const override;
qint64 size() const override;
@@ -63,7 +63,6 @@ public:
bool seek(qint64 pos) override;
QString fileName(FileName file) const override;
void setFileName(const QString &file) override;
- QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override;
#ifndef QT_NO_FILESYSTEMITERATOR
Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override;
diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm
index 01524a00e8..9cb38a3461 100644
--- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm
+++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm
@@ -353,8 +353,11 @@ ALAsset *QIOSFileEngineAssetsLibrary::loadAsset() const
return m_data->m_asset;
}
-bool QIOSFileEngineAssetsLibrary::open(QIODevice::OpenMode openMode)
+bool QIOSFileEngineAssetsLibrary::open(QIODevice::OpenMode openMode,
+ std::optional<QFile::Permissions> permissions)
{
+ Q_UNUSED(permissions);
+
if (openMode & (QIODevice::WriteOnly | QIODevice::Text))
return false;
return loadAsset();
@@ -456,11 +459,6 @@ void QIOSFileEngineAssetsLibrary::setFileName(const QString &file)
m_assetUrl = QLatin1String("assets-library:/") + file.mid(index);
}
-QStringList QIOSFileEngineAssetsLibrary::entryList(QDir::Filters filters, const QStringList &filterNames) const
-{
- return QAbstractFileEngine::entryList(filters, filterNames);
-}
-
#ifndef QT_NO_FILESYSTEMITERATOR
QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::beginEntryList(
diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h
index 36421a57c3..d834abda40 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.h
+++ b/src/plugins/platforms/ios/qiosinputcontext.h
@@ -54,7 +54,7 @@ const char kImePlatformDataReturnKeyType[] = "returnKeyType";
@class QIOSLocaleListener;
@class QIOSKeyboardListener;
-@class QIOSTextInputResponder;
+@class QIOSTextResponder;
@protocol KeyboardState;
QT_BEGIN_NAMESPACE
@@ -125,7 +125,7 @@ private:
QIOSLocaleListener *m_localeListener;
QIOSKeyboardListener *m_keyboardHideGesture;
- QIOSTextInputResponder *m_textResponder;
+ QIOSTextResponder *m_textResponder;
KeyboardState m_keyboardState;
ImeState m_imeState;
};
diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm
index c6032b5147..1ae7f953b2 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.mm
+++ b/src/plugins/platforms/ios/qiosinputcontext.mm
@@ -675,16 +675,23 @@ void QIOSInputContext::update(Qt::InputMethodQueries updatedProperties)
}
// Mask for properties that we are interested in and see if any of them changed
- updatedProperties &= (Qt::ImEnabled | Qt::ImHints | Qt::ImQueryInput | Qt::ImEnterKeyType | Qt::ImPlatformData);
+ updatedProperties &= (Qt::ImEnabled | Qt::ImHints | Qt::ImQueryInput | Qt::ImEnterKeyType | Qt::ImPlatformData | Qt::ImReadOnly);
// Perform update first, so we can trust the value of inputMethodAccepted()
Qt::InputMethodQueries changedProperties = m_imeState.update(updatedProperties);
- if (inputMethodAccepted()) {
+ const bool inputIsReadOnly = m_imeState.currentState.value(Qt::ImReadOnly).toBool();
+
+ if (inputMethodAccepted() || inputIsReadOnly) {
if (!m_textResponder || [m_textResponder needsKeyboardReconfigure:changedProperties]) {
- qImDebug("creating new text responder");
[m_textResponder autorelease];
- m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this];
+ if (inputIsReadOnly) {
+ qImDebug("creating new read-only text responder");
+ m_textResponder = [[QIOSTextResponder alloc] initWithInputContext:this];
+ } else {
+ qImDebug("creating new read/write text responder");
+ m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this];
+ }
} else {
qImDebug("no need to reconfigure keyboard, just notifying input delegate");
[m_textResponder notifyInputDelegate:changedProperties];
@@ -732,8 +739,7 @@ void QIOSInputContext::reset()
update(Qt::ImQueryAll);
- [m_textResponder setMarkedText:@"" selectedRange:NSMakeRange(0, 0)];
- [m_textResponder notifyInputDelegate:Qt::ImQueryInput];
+ [m_textResponder reset];
}
/*!
@@ -747,9 +753,7 @@ void QIOSInputContext::reset()
void QIOSInputContext::commit()
{
qImDebug("unmarking text");
-
- [m_textResponder unmarkText];
- [m_textResponder notifyInputDelegate:Qt::ImSurroundingText];
+ [m_textResponder commit];
}
QLocale QIOSInputContext::locale() const
diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm
index 2ab732df7a..c72d8dc596 100644
--- a/src/plugins/platforms/ios/qiostextinputoverlay.mm
+++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm
@@ -1048,15 +1048,39 @@ void QIOSTextInputOverlay::updateFocusObject()
s_editMenu = nullptr;
}
- if (platformInputContext()->inputMethodAccepted()) {
- s_editMenu = [QIOSEditMenu new];
- m_cursorRecognizer = [QIOSCursorRecognizer new];
+ const QVariant hintsVariant = QGuiApplication::inputMethod()->queryFocusObject(Qt::ImHints, QVariant());
+ const Qt::InputMethodHints hints = Qt::InputMethodHints(hintsVariant.toUInt());
+ if (hints & Qt::ImhNoTextHandles)
+ return;
+
+ // The focus object can emit selection updates (e.g from mouse drag), and
+ // accept modifying it through IM when dragging on the handles, even if it
+ // doesn't accept text input and IM in general (and hence return false from
+ // inputMethodAccepted()). This is the case for read-only text fields.
+ // Therefore, listen for selection changes also when the focus object
+ // reports that it's ImReadOnly (which we take as a hint that it's actually
+ // a text field, and that selections therefore might happen). But since
+ // we have no guarantee that the focus object can actually accept new selections
+ // through IM (and since we also need to respect if the input accepts selections
+ // in the first place), we only support selections started by the text field (e.g from
+ // mouse drag), even if we in theory could also start selections from a loupe.
+
+ const bool inputAccepted = platformInputContext()->inputMethodAccepted();
+ const bool readOnly = QGuiApplication::inputMethod()->queryFocusObject(Qt::ImReadOnly, QVariant()).toBool();
+
+ if (inputAccepted || readOnly) {
+ if (!(hints & Qt::ImhNoEditMenu))
+ s_editMenu = [QIOSEditMenu new];
m_selectionRecognizer = [QIOSSelectionRecognizer new];
m_openMenuOnTapRecognizer = [QIOSTapRecognizer new];
- m_cursorRecognizer.enabled = YES;
m_selectionRecognizer.enabled = YES;
m_openMenuOnTapRecognizer.enabled = YES;
}
+
+ if (inputAccepted) {
+ m_cursorRecognizer = [QIOSCursorRecognizer new];
+ m_cursorRecognizer.enabled = YES;
+ }
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiostextresponder.h b/src/plugins/platforms/ios/qiostextresponder.h
index 074598c1c3..d6f1cfd03f 100644
--- a/src/plugins/platforms/ios/qiostextresponder.h
+++ b/src/plugins/platforms/ios/qiostextresponder.h
@@ -48,7 +48,18 @@ class QIOSInputContext;
QT_END_NAMESPACE
-@interface QIOSTextInputResponder : UIResponder <UITextInputTraits, UIKeyInput, UITextInput>
+@interface QIOSTextResponder : UIResponder
+
+- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context;
+
+- (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties;
+- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties;
+- (void)reset;
+- (void)commit;
+
+@end
+
+@interface QIOSTextInputResponder : QIOSTextResponder <UITextInputTraits, UIKeyInput, UITextInput>
- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context;
- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties;
diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm
index a65f8bbaa1..deca8c7617 100644
--- a/src/plugins/platforms/ios/qiostextresponder.mm
+++ b/src/plugins/platforms/ios/qiostextresponder.mm
@@ -161,12 +161,11 @@
// -------------------------------------------------------------------------
-@implementation QIOSTextInputResponder {
+@implementation QIOSTextResponder {
+ @public
QT_PREPEND_NAMESPACE(QIOSInputContext) *m_inputContext;
QT_PREPEND_NAMESPACE(QInputMethodQueryEvent) *m_configuredImeState;
- QString m_markedText;
BOOL m_inSendEventToFocusObject;
- BOOL m_inSelectionChange;
}
- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)inputContext
@@ -174,14 +173,207 @@
if (!(self = [self init]))
return self;
+ m_inputContext = inputContext;
+ m_configuredImeState = static_cast<QInputMethodQueryEvent*>(m_inputContext->imeState().currentState.clone());
m_inSendEventToFocusObject = NO;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ delete m_configuredImeState;
+ [super dealloc];
+}
+
+- (QVariant)currentImeState:(Qt::InputMethodQuery)query
+{
+ return m_inputContext->imeState().currentState.value(query);
+}
+
+- (BOOL)canBecomeFirstResponder
+{
+ return YES;
+}
+
+- (BOOL)becomeFirstResponder
+{
+ FirstResponderCandidate firstResponderCandidate(self);
+
+ qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
+
+ if (![super becomeFirstResponder]) {
+ qImDebug() << self << "was not allowed to become first responder";
+ return NO;
+ }
+
+ qImDebug() << self << "became first responder";
+
+ return YES;
+}
+
+- (BOOL)resignFirstResponder
+{
+ qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
+
+ // Don't allow activation events of the window that we're doing text on behalf on
+ // to steal responder.
+ if (FirstResponderCandidate::currentCandidate() == [self nextResponder]) {
+ qImDebug("not allowing parent window to steal responder");
+ return NO;
+ }
+
+ if (![super resignFirstResponder])
+ return NO;
+
+ qImDebug() << self << "resigned first responder";
+
+ // Dismissing the keyboard will trigger resignFirstResponder, but so will
+ // a regular responder transfer to another window. In the former case, iOS
+ // will set the new first-responder to our next-responder, and in the latter
+ // case we'll have an active responder candidate.
+ if (![UIResponder currentFirstResponder] && !FirstResponderCandidate::currentCandidate()) {
+ // No first responder set anymore, sync this with Qt by clearing the
+ // focus object.
+ m_inputContext->clearCurrentFocusObject();
+ } else if ([UIResponder currentFirstResponder] == [self nextResponder]) {
+ // We have resigned the keyboard, and transferred first responder back to the parent view
+ Q_ASSERT(!FirstResponderCandidate::currentCandidate());
+ if ([self currentImeState:Qt::ImEnabled].toBool()) {
+ // The current focus object expects text input, but there
+ // is no keyboard to get input from. So we clear focus.
+ qImDebug("no keyboard available, clearing focus object");
+ m_inputContext->clearCurrentFocusObject();
+ }
+ } else {
+ // We've lost responder status because another Qt window was made active,
+ // another QIOSTextResponder was made first-responder, another UIView was
+ // made first-responder, or the first-responder was cleared globally. In
+ // either of these cases we don't have to do anything.
+ qImDebug("lost first responder, but not clearing focus object");
+ }
+
+ return YES;
+}
+
+- (UIResponder*)nextResponder
+{
+ return qApp->focusWindow() ?
+ reinterpret_cast<QUIView *>(qApp->focusWindow()->handle()->winId()) : 0;
+}
+
+// -------------------------------------------------------------------------
+
+- (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties
+{
+ Q_UNUSED(updatedProperties);
+}
+
+- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties
+{
+ if (updatedProperties & Qt::ImEnabled) {
+ qImDebug() << "Qt::ImEnabled has changed since text responder was configured, need reconfigure";
+ return YES;
+ }
+
+ if (updatedProperties & Qt::ImReadOnly) {
+ qImDebug() << "Qt::ImReadOnly has changed since text responder was configured, need reconfigure";
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)reset
+{
+ // Nothing to reset for read-only text fields
+}
+
+- (void)commit
+{
+ // Nothing to commit for read-only text fields
+}
+
+// -------------------------------------------------------------------------
+
+#ifndef QT_NO_SHORTCUT
+
+- (void)sendKeyPressRelease:(Qt::Key)key modifiers:(Qt::KeyboardModifiers)modifiers
+{
+ QScopedValueRollback<BOOL> rollback(m_inSendEventToFocusObject, true);
+ QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyPress, key, modifiers);
+ QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyRelease, key, modifiers);
+}
+
+- (void)sendShortcut:(QKeySequence::StandardKey)standardKey
+{
+ const QKeyCombination combination = QKeySequence(standardKey)[0];
+ [self sendKeyPressRelease:combination.key() modifiers:combination.keyboardModifiers()];
+}
+
+- (BOOL)hasSelection
+{
+ QInputMethodQueryEvent query(Qt::ImAnchorPosition | Qt::ImCursorPosition);
+ QGuiApplication::sendEvent(QGuiApplication::focusObject(), &query);
+ int anchorPos = query.value(Qt::ImAnchorPosition).toInt();
+ int cursorPos = query.value(Qt::ImCursorPosition).toInt();
+ return anchorPos != cursorPos;
+}
+
+- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
+{
+ const bool isSelectAction =
+ action == @selector(select:) ||
+ action == @selector(selectAll:);
+
+ const bool isReadAction = action == @selector(copy:);
+
+ if (!isSelectAction && !isReadAction)
+ return [super canPerformAction:action withSender:sender];
+
+ const bool hasSelection = [self hasSelection];
+ return (!hasSelection && isSelectAction) || (hasSelection && isReadAction);
+}
+
+- (void)copy:(id)sender
+{
+ Q_UNUSED(sender);
+ [self sendShortcut:QKeySequence::Copy];
+}
+
+- (void)select:(id)sender
+{
+ Q_UNUSED(sender);
+ [self sendShortcut:QKeySequence::MoveToPreviousWord];
+ [self sendShortcut:QKeySequence::SelectNextWord];
+}
+
+- (void)selectAll:(id)sender
+{
+ Q_UNUSED(sender);
+ [self sendShortcut:QKeySequence::SelectAll];
+}
+
+#endif // QT_NO_SHORTCUT
+
+@end
+
+// -------------------------------------------------------------------------
+
+@implementation QIOSTextInputResponder {
+ QString m_markedText;
+ BOOL m_inSelectionChange;
+}
+
+- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)inputContext
+{
+ if (!(self = [super initWithInputContext:inputContext]))
+ return self;
+
m_inSelectionChange = NO;
- m_inputContext = inputContext;
- m_configuredImeState = static_cast<QInputMethodQueryEvent*>(m_inputContext->imeState().currentState.clone());
QVariantMap platformData = m_configuredImeState->value(Qt::ImPlatformData).toMap();
Qt::InputMethodHints hints = Qt::InputMethodHints(m_configuredImeState->value(Qt::ImHints).toUInt());
-
Qt::EnterKeyType enterKeyType = Qt::EnterKeyType(m_configuredImeState->value(Qt::ImEnterKeyType).toUInt());
switch (enterKeyType) {
@@ -264,29 +456,27 @@
{
self.inputView = 0;
self.inputAccessoryView = 0;
- delete m_configuredImeState;
[super dealloc];
}
- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties
{
- if ((updatedProperties & Qt::ImEnabled)) {
- Q_ASSERT([self currentImeState:Qt::ImEnabled].toBool());
-
+ Qt::InputMethodQueries relevantProperties = updatedProperties;
+ if ((relevantProperties & Qt::ImEnabled)) {
// When switching on input-methods we need to consider hints and platform data
// as well, as the IM state that we were based on may have been invalidated when
// IM was switched off.
qImDebug("IM was turned on, we need to check hints and platform data as well");
- updatedProperties |= (Qt::ImHints | Qt::ImPlatformData);
+ relevantProperties |= (Qt::ImHints | Qt::ImPlatformData);
}
// Based on what we set up in initWithInputContext above
- updatedProperties &= (Qt::ImHints | Qt::ImEnterKeyType | Qt::ImPlatformData);
+ relevantProperties &= (Qt::ImHints | Qt::ImEnterKeyType | Qt::ImPlatformData);
- if (!updatedProperties)
- return NO;
+ if (!relevantProperties)
+ return [super needsKeyboardReconfigure:updatedProperties];
for (uint i = 0; i < (sizeof(Qt::ImQueryAll) * CHAR_BIT); ++i) {
if (Qt::InputMethodQuery property = Qt::InputMethodQuery(int(updatedProperties & (1 << i)))) {
@@ -297,100 +487,13 @@
}
}
- return NO;
-}
-
-- (BOOL)canBecomeFirstResponder
-{
- return YES;
-}
-
-- (BOOL)becomeFirstResponder
-{
- FirstResponderCandidate firstResponderCandidate(self);
-
- qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
-
- if (![super becomeFirstResponder]) {
- qImDebug() << self << "was not allowed to become first responder";
- return NO;
- }
-
- qImDebug() << self << "became first responder";
-
- return YES;
-}
-
-- (BOOL)resignFirstResponder
-{
- qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
-
- // Don't allow activation events of the window that we're doing text on behalf on
- // to steal responder.
- if (FirstResponderCandidate::currentCandidate() == [self nextResponder]) {
- qImDebug("not allowing parent window to steal responder");
- return NO;
- }
-
- if (![super resignFirstResponder])
- return NO;
-
- qImDebug() << self << "resigned first responder";
-
- // Dismissing the keyboard will trigger resignFirstResponder, but so will
- // a regular responder transfer to another window. In the former case, iOS
- // will set the new first-responder to our next-responder, and in the latter
- // case we'll have an active responder candidate.
- if (![UIResponder currentFirstResponder] && !FirstResponderCandidate::currentCandidate()) {
- // No first responder set anymore, sync this with Qt by clearing the
- // focus object.
- m_inputContext->clearCurrentFocusObject();
- } else if ([UIResponder currentFirstResponder] == [self nextResponder]) {
- // We have resigned the keyboard, and transferred first responder back to the parent view
- Q_ASSERT(!FirstResponderCandidate::currentCandidate());
- if ([self currentImeState:Qt::ImEnabled].toBool()) {
- // The current focus object expects text input, but there
- // is no keyboard to get input from. So we clear focus.
- qImDebug("no keyboard available, clearing focus object");
- m_inputContext->clearCurrentFocusObject();
- }
- } else {
- // We've lost responder status because another Qt window was made active,
- // another QIOSTextResponder was made first-responder, another UIView was
- // made first-responder, or the first-responder was cleared globally. In
- // either of these cases we don't have to do anything.
- qImDebug("lost first responder, but not clearing focus object");
- }
-
- return YES;
-}
-
-
-- (UIResponder*)nextResponder
-{
- return qApp->focusWindow() ?
- reinterpret_cast<QUIView *>(qApp->focusWindow()->handle()->winId()) : 0;
+ return [super needsKeyboardReconfigure:updatedProperties];
}
// -------------------------------------------------------------------------
-- (void)sendKeyPressRelease:(Qt::Key)key modifiers:(Qt::KeyboardModifiers)modifiers
-{
- QScopedValueRollback<BOOL> rollback(m_inSendEventToFocusObject, true);
- QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyPress, key, modifiers);
- QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyRelease, key, modifiers);
-}
-
#ifndef QT_NO_SHORTCUT
-- (void)sendShortcut:(QKeySequence::StandardKey)standardKey
-{
- const int keys = QKeySequence(standardKey)[0];
- Qt::Key key = Qt::Key(keys & 0x0000FFFF);
- Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(keys & 0xFFFF0000);
- [self sendKeyPressRelease:key modifiers:modifiers];
-}
-
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
bool isEditAction = (action == @selector(cut:)
@@ -410,7 +513,7 @@
|| action == @selector(redo));
const bool unknownAction = !isEditAction && !isSelectAction;
- const bool hasSelection = ![self selectedTextRange].empty;
+ const bool hasSelection = [self hasSelection];
if (unknownAction)
return [super canPerformAction:action withSender:sender];
@@ -434,31 +537,12 @@
[self sendShortcut:QKeySequence::Cut];
}
-- (void)copy:(id)sender
-{
- Q_UNUSED(sender);
- [self sendShortcut:QKeySequence::Copy];
-}
-
- (void)paste:(id)sender
{
Q_UNUSED(sender);
[self sendShortcut:QKeySequence::Paste];
}
-- (void)select:(id)sender
-{
- Q_UNUSED(sender);
- [self sendShortcut:QKeySequence::MoveToPreviousWord];
- [self sendShortcut:QKeySequence::SelectNextWord];
-}
-
-- (void)selectAll:(id)sender
-{
- Q_UNUSED(sender);
- [self sendShortcut:QKeySequence::SelectAll];
-}
-
- (void)delete:(id)sender
{
Q_UNUSED(sender);
@@ -647,11 +731,6 @@
QCoreApplication::sendEvent(focusObject, &e);
}
-- (QVariant)currentImeState:(Qt::InputMethodQuery)query
-{
- return m_inputContext->imeState().currentState.value(query);
-}
-
- (id<UITextInputTokenizer>)tokenizer
{
return [[[UITextInputStringTokenizer alloc] initWithTextInput:self] autorelease];
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm
index 7aeb3b9ca6..40beda6f5f 100644
--- a/src/plugins/platforms/ios/qiosviewcontroller.mm
+++ b/src/plugins/platforms/ios/qiosviewcontroller.mm
@@ -147,9 +147,13 @@
UIWindow *uiWindow = self.window;
if (uiWindow.screen != [UIScreen mainScreen] && self.subviews.count == 1) {
- // Removing the last view of an external screen, go back to mirror mode
- uiWindow.screen = [UIScreen mainScreen];
- uiWindow.hidden = YES;
+ // We're about to remove the last view of an external screen, so go back
+ // to mirror mode, but defer it until after the view has been removed,
+ // to ensure that we don't try to layout the view that's being removed.
+ dispatch_async(dispatch_get_main_queue(), ^{
+ uiWindow.hidden = YES;
+ uiWindow.screen = [UIScreen mainScreen];
+ });
}
}
diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm
index ef6c9f49ad..1c97eee216 100644
--- a/src/plugins/platforms/ios/quiview.mm
+++ b/src/plugins/platforms/ios/quiview.mm
@@ -306,7 +306,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
// Nor do we want to deactivate the Qt window if the new responder
// is temporarily handling text input on behalf of a Qt window.
- if ([responder isKindOfClass:[QIOSTextInputResponder class]]) {
+ if ([responder isKindOfClass:[QIOSTextResponder class]]) {
while ((responder = [responder nextResponder])) {
if ([responder isKindOfClass:[QUIView class]])
return NO;
diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm
index 6612dc131e..9a103509cc 100644
--- a/src/plugins/platforms/ios/quiview_accessibility.mm
+++ b/src/plugins/platforms/ios/quiview_accessibility.mm
@@ -66,6 +66,11 @@
- (void)initAccessibility
{
+ // The window may have gone away, but with the view
+ // temporarily caught in the a11y subsystem.
+ if (!self.platformWindow)
+ return;
+
static bool init = false;
if (!init)
QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.cpp b/src/plugins/platforms/offscreen/qoffscreencommon.cpp
index f485b4eed8..76ebe79590 100644
--- a/src/plugins/platforms/offscreen/qoffscreencommon.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreencommon.cpp
@@ -42,6 +42,7 @@
#include "qoffscreenwindow.h"
+#include <QtGui/QPainter>
#include <QtGui/private/qpixmap_raster_p.h>
#include <QtGui/private/qguiapplication_p.h>
@@ -116,22 +117,25 @@ QPixmap QOffscreenScreen::grabWindow(WId id, int x, int y, int width, int height
{
QRect rect(x, y, width, height);
- QOffscreenWindow *window = QOffscreenWindow::windowForWinId(id);
- if (!window || window->window()->type() == Qt::Desktop) {
+ // id == 0 -> grab the screen, so all windows intersecting rect
+ if (!id) {
+ if (width == -1)
+ rect.setWidth(m_geometry.width());
+ if (height == -1)
+ rect.setHeight(m_geometry.height());
+ QPixmap screenImage(rect.size());
+ QPainter painter(&screenImage);
+ painter.translate(-x, -y);
const QWindowList wl = QGuiApplication::topLevelWindows();
- QWindow *containing = nullptr;
for (QWindow *w : wl) {
- if (w->type() != Qt::Desktop && w->isExposed() && w->geometry().contains(rect)) {
- containing = w;
- break;
+ if (w->isExposed() && w->geometry().intersects(rect)) {
+ QOffscreenBackingStore *store = QOffscreenBackingStore::backingStoreForWinId(w->winId());
+ const QImage windowImage = store ? store->toImage() : QImage();
+ if (!windowImage.isNull())
+ painter.drawImage(w->position(), windowImage);
}
}
-
- if (!containing)
- return QPixmap();
-
- id = containing->winId();
- rect = rect.translated(-containing->geometry().topLeft());
+ return screenImage;
}
QOffscreenBackingStore *store = QOffscreenBackingStore::backingStoreForWinId(id);
diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.h b/src/plugins/platforms/offscreen/qoffscreencommon.h
index d0edfcc48b..6ed6f43816 100644
--- a/src/plugins/platforms/offscreen/qoffscreencommon.h
+++ b/src/plugins/platforms/offscreen/qoffscreencommon.h
@@ -106,6 +106,7 @@ public:
bool scroll(const QRegion &area, int dx, int dy) override;
QPixmap grabWindow(WId window, const QRect &rect) const;
+ QImage toImage() const override { return m_image; }
static QOffscreenBackingStore *backingStoreForWinId(WId id);
diff --git a/src/plugins/platforms/offscreen/qoffscreenwindow.cpp b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
index 20aae06650..1046b2a908 100644
--- a/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
@@ -122,7 +122,7 @@ void QOffscreenWindow::setVisible(bool visible)
if (visible) {
if (window()->type() != Qt::ToolTip)
- QWindowSystemInterface::handleWindowActivated(window());
+ QWindowSystemInterface::handleWindowActivated(window(), Qt::ActiveWindowFocusReason);
if (m_pendingGeometryChangeOnShow) {
m_pendingGeometryChangeOnShow = false;
@@ -158,7 +158,7 @@ void QOffscreenWindow::setVisible(bool visible)
void QOffscreenWindow::requestActivateWindow()
{
if (m_visible)
- QWindowSystemInterface::handleWindowActivated(window());
+ QWindowSystemInterface::handleWindowActivated(window(), Qt::ActiveWindowFocusReason);
}
WId QOffscreenWindow::winId() const
diff --git a/src/plugins/platforms/qnx/.prev_CMakeLists.txt b/src/plugins/platforms/qnx/.prev_CMakeLists.txt
deleted file mode 100644
index ad0731b840..0000000000
--- a/src/plugins/platforms/qnx/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,91 +0,0 @@
-# Generated from qnx.pro.
-
-#####################################################################
-## QQnxIntegrationPlugin Plugin:
-#####################################################################
-
-qt_internal_add_plugin(QQnxIntegrationPlugin
- OUTPUT_NAME qqnx
- TYPE platforms
- SOURCES
- main.cpp main.h
- qqnxabstractcover.h
- qqnxabstractnavigator.cpp qqnxabstractnavigator.h
- qqnxabstractvirtualkeyboard.cpp qqnxabstractvirtualkeyboard.h
- qqnxbuffer.cpp qqnxbuffer.h
- qqnxcursor.cpp qqnxcursor.h
- qqnxforeignwindow.cpp qqnxforeignwindow.h
- qqnxglobal.cpp qqnxglobal.h
- qqnxintegration.cpp qqnxintegration.h
- qqnxkeytranslator.h
- qqnxlgmon.h
- qqnxnativeinterface.cpp qqnxnativeinterface.h
- qqnxnavigatoreventhandler.cpp qqnxnavigatoreventhandler.h
- qqnxrasterbackingstore.cpp qqnxrasterbackingstore.h
- qqnxrasterwindow.cpp qqnxrasterwindow.h
- qqnxscreen.cpp qqnxscreen.h
- qqnxscreeneventfilter.h
- qqnxscreeneventhandler.cpp qqnxscreeneventhandler.h
- qqnxscreeneventthread.cpp qqnxscreeneventthread.h
- qqnxservices.cpp qqnxservices.h
- qqnxwindow.cpp qqnxwindow.h
- PUBLIC_LIBRARIES
- Qt::Core
- Qt::CorePrivate
- Qt::Gui
- Qt::GuiPrivate
- screen
-)
-
-#### Keys ignored in scope 1:.:.:qnx.pro:<TRUE>:
-# OTHER_FILES = "qnx.json"
-
-## Scopes:
-#####################################################################
-
-qt_internal_extend_target(QQnxIntegrationPlugin CONDITION QT_FEATURE_egl
- SOURCES
- qqnxeglwindow.cpp qqnxeglwindow.h
- qqnxglcontext.cpp qqnxglcontext.h
- PUBLIC_LIBRARIES
- EGL::EGL
-)
-
-qt_internal_extend_target(QQnxIntegrationPlugin CONDITION QT_FEATURE_qqnx_pps
- SOURCES
- qqnxbuttoneventnotifier.cpp qqnxbuttoneventnotifier.h
- qqnxnavigatoreventnotifier.cpp qqnxnavigatoreventnotifier.h
- qqnxnavigatorpps.cpp qqnxnavigatorpps.h
- qqnxvirtualkeyboardpps.cpp qqnxvirtualkeyboardpps.h
- PUBLIC_LIBRARIES
- PPS::PPS
-)
-
-qt_internal_extend_target(QQnxIntegrationPlugin CONDITION QT_FEATURE_clipboard AND QT_FEATURE_qqnx_pps
- SOURCES
- qqnxclipboard.cpp qqnxclipboard.h
- PUBLIC_LIBRARIES
- clipboard
-)
-
-qt_internal_extend_target(QQnxIntegrationPlugin CONDITION QT_FEATURE_qqnx_imf AND QT_FEATURE_qqnx_pps
- SOURCES
- qqnxinputcontext_imf.cpp qqnxinputcontext_imf.h
-)
-
-qt_internal_extend_target(QQnxIntegrationPlugin CONDITION QT_FEATURE_qqnx_pps AND NOT QT_FEATURE_qqnx_imf
- SOURCES
- qqnxinputcontext_noimf.cpp qqnxinputcontext_noimf.h
-)
-
-qt_internal_extend_target(QQnxIntegrationPlugin CONDITION QT_FEATURE_lgmon
- SOURCES
- qqnxlgmon.cpp
- DEFINES
- QQNX_LGMON
- PUBLIC_LIBRARIES
- lgmon
-)
-
-#### Keys ignored in scope 8:.:.:qnx.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN:
-# PLUGIN_EXTENDS = "-"
diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.cpp b/src/plugins/platforms/qnx/qqnxeglwindow.cpp
index 48766fc435..df36048e48 100644
--- a/src/plugins/platforms/qnx/qqnxeglwindow.cpp
+++ b/src/plugins/platforms/qnx/qqnxeglwindow.cpp
@@ -79,7 +79,7 @@ bool QQnxEglWindow::isInitialized() const
void QQnxEglWindow::ensureInitialized(QQnxGLContext* context)
{
if (m_newSurfaceRequested.testAndSetOrdered(true, false)) {
- const QMutexLocker locker(&m_mutex); // Set geomety must not reset the requestedBufferSize till
+ const QMutexLocker locker(&m_mutex); // Set geometry must not reset the requestedBufferSize till
// the surface is created
if (m_requestedBufferSize != bufferSize() || m_eglSurface == EGL_NO_SURFACE) {
diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
index fc682d66e2..84cfbea27d 100644
--- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
+++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
@@ -577,7 +577,7 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType)
parent = parent->parent();
}
- //Qt expects the pressure between 0 and 1. There is however no definit upper limit for
+ //Qt expects the pressure between 0 and 1. There is however no definite upper limit for
//the integer value of touch event pressure. The 200 was determined by experiment, it
//usually does not get higher than that.
m_touchPoints[touchId].pressure = static_cast<qreal>(touchPressure)/200.0;
@@ -742,7 +742,7 @@ void QQnxScreenEventHandler::handleKeyboardFocusPropertyEvent(screen_window_t wi
}
if (focus && focusWindow != QGuiApplication::focusWindow())
- QWindowSystemInterface::handleWindowActivated(focusWindow);
+ QWindowSystemInterface::handleWindowActivated(focusWindow, Qt::ActiveWindowFocusReason);
else if (!focus && focusWindow == QGuiApplication::focusWindow())
m_focusLostTimer = startTimer(50);
}
diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp
index 6f496571fa..0d7bf09599 100644
--- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp
+++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp
@@ -240,7 +240,7 @@ bool QQnxVirtualKeyboardPps::showKeyboard()
if (!prepareToSend())
return false;
- // NOTE: This must be done everytime the keyboard is shown even if there is no change because
+ // NOTE: This must be done every time the keyboard is shown even if there is no change because
// hiding the keyboard wipes the setting.
applyKeyboardOptions();
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt
index 00f86b837c..130af2dec6 100644
--- a/src/plugins/platforms/wasm/CMakeLists.txt
+++ b/src/plugins/platforms/wasm/CMakeLists.txt
@@ -68,9 +68,10 @@ set(wasm_support_files
qtlogo.svg
)
+qt_path_join(destination ${QT_INSTALL_DIR} "plugins/platforms")
qt_copy_or_install(FILES
${wasm_support_files}
- DESTINATION "${CMAKE_INSTALL_PREFIX}/plugins/platforms"
+ DESTINATION "${destination}"
)
# Need to copy the support files to the build dir in a top-level prefix build
# So _qt_internal_wasm_add_target_helpers finds them.
diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js
index 1180e55f73..08bf0ed421 100644
--- a/src/plugins/platforms/wasm/qtloader.js
+++ b/src/plugins/platforms/wasm/qtloader.js
@@ -407,6 +407,12 @@ function QtLoader(config)
setStatus("Exited");
};
self.moduleConfig.quit = self.moduleConfig.quit || function(code, exception) {
+
+ // Emscripten (and Qt) supports exiting from main() while keeping the app
+ // running. Don't transition into the "Exited" state for clean exits.
+ if (code == 0)
+ return;
+
if (exception.name == "ExitStatus") {
// Clean exit with code
publicAPI.exitText = undefined
diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.cpp b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
index 3d667ccf97..989c2a1b90 100644
--- a/src/plugins/platforms/wasm/qwasmbackingstore.cpp
+++ b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
@@ -90,7 +90,7 @@ void QWasmBackingStore::flush(QWindow *window, const QRegion &region, const QPoi
Q_UNUSED(offset);
m_dirty |= region;
- m_compositor->requestRedraw();
+ m_compositor->handleBackingStoreFlush();
}
void QWasmBackingStore::updateTexture()
diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp
index 222dcff7fa..17ba1f4db6 100644
--- a/src/plugins/platforms/wasm/qwasmclipboard.cpp
+++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp
@@ -30,103 +30,264 @@
#include "qwasmclipboard.h"
#include "qwasmwindow.h"
#include "qwasmstring.h"
+#include <private/qstdweb_p.h>
#include <emscripten.h>
#include <emscripten/html5.h>
#include <emscripten/bind.h>
+#include <emscripten/val.h>
#include <QCoreApplication>
#include <qpa/qwindowsysteminterface.h>
+#include <QBuffer>
+#include <QString>
using namespace emscripten;
-// there has got to be a better way...
-static QString g_clipboardText;
-static QString g_clipboardFormat;
+static void pasteClipboardData(emscripten::val format, emscripten::val dataPtr)
+{
+ QString formatString = QWasmString::toQString(format);
+ QByteArray dataArray = QByteArray::fromStdString(dataPtr.as<std::string>());
+
+ QMimeData *mMimeData = new QMimeData;
+ mMimeData->setData(formatString, dataArray);
-static val getClipboardData()
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
+// QWasmIntegration::get()->getWasmClipboard()->isPaste = false;
+}
+
+static void qClipboardPasteResolve(emscripten::val blob)
{
- return QWasmString::fromQString(g_clipboardText);
+ // read Blob here
+
+ auto fileReader = std::make_shared<qstdweb::FileReader>();
+ auto _blob = qstdweb::Blob(blob);
+ QString formatString = QString::fromStdString(_blob.type());
+
+ fileReader->readAsArrayBuffer(_blob);
+ char *chunkBuffer = nullptr;
+ qstdweb::ArrayBuffer result = fileReader->result();
+ qstdweb::Uint8Array(result).copyTo(chunkBuffer);
+ QMimeData *mMimeData = new QMimeData;
+ mMimeData->setData(formatString, chunkBuffer);
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
}
-static val getClipboardFormat()
+static void qClipboardPromiseResolve(emscripten::val clipboardItems)
{
- return QWasmString::fromQString(g_clipboardFormat);
+ int itemsCount = clipboardItems["length"].as<int>();
+
+ for (int i = 0; i < itemsCount; i++) {
+ int typesCount = clipboardItems[i]["types"]["length"].as<int>(); // ClipboardItem
+
+ std::string mimeFormat = clipboardItems[i]["types"][0].as<std::string>();
+
+ if (mimeFormat.find(std::string("text")) != std::string::npos) {
+ // simple val object, no further processing
+
+ val navigator = val::global("navigator");
+ val textPromise = navigator["clipboard"].call<val>("readText");
+ val readTextResolve = val::global("Module")["qtClipboardTextPromiseResolve"];
+ textPromise.call<val>("then", readTextResolve);
+
+ } else {
+ // binary types require additional processing
+ for (int j = 0; j < typesCount; j++) {
+ val pasteResolve = emscripten::val::module_property("qtClipboardPasteResolve");
+ val pasteException = emscripten::val::module_property("qtClipboardPromiseException");
+
+ // get the blob
+ clipboardItems[i]
+ .call<val>("getType", clipboardItems[i]["types"][j])
+ .call<val>("then", pasteResolve)
+ .call<val>("catch", pasteException);
+ }
+ }
+ }
}
-static void pasteClipboardData(emscripten::val format, emscripten::val dataPtr)
+static void qClipboardCopyPromiseResolve(emscripten::val something)
{
- QString formatString = QWasmString::toQString(format);
- QByteArray dataArray = QByteArray::fromStdString(dataPtr.as<std::string>());
- QMimeData *mMimeData = new QMimeData;
- mMimeData->setData(formatString, dataArray);
- QWasmClipboard::qWasmClipboardPaste(mMimeData);
+ Q_UNUSED(something)
+ qWarning() << "copy succeeeded";
}
-static void qClipboardPromiseResolve(emscripten::val something)
+
+static emscripten::val qClipboardPromiseException(emscripten::val something)
{
- pasteClipboardData(emscripten::val("text/plain"), something);
+ qWarning() << "clipboard error"
+ << QString::fromStdString(something["name"].as<std::string>())
+ << QString::fromStdString(something["message"].as<std::string>());
+ return something;
+}
+
+static void commonCopyEvent(val event)
+{
+ QMimeData *_mimes = QWasmIntegration::get()->getWasmClipboard()->mimeData(QClipboard::Clipboard);
+ if (!_mimes)
+ return;
+
+ // doing it this way seems to sanitize the text better that calling data() like down below
+ if (_mimes->hasText()) {
+ event["clipboardData"].call<void>("setData", val("text/plain")
+ , QWasmString::fromQString(_mimes->text()));
+ }
+ if (_mimes->hasHtml()) {
+ event["clipboardData"].call<void>("setData", val("text/html")
+ , QWasmString::fromQString(_mimes->html()));
+ }
+
+ for (auto mimetype : _mimes->formats()) {
+ if (mimetype.contains("text/"))
+ continue;
+ QByteArray ba = _mimes->data(mimetype);
+ if (!ba.isEmpty())
+ event["clipboardData"].call<void>("setData", QWasmString::fromQString(mimetype)
+ , val(ba.constData()));
+ }
+
+ event.call<void>("preventDefault");
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = false;
}
static void qClipboardCutTo(val event)
{
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = true;
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");
- }
- event["clipboardData"].call<void>("setData", getClipboardFormat(), getClipboardData());
- event.call<void>("preventDefault");
+ QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "X");
+ }
+
+ commonCopyEvent(event);
}
static void qClipboardCopyTo(val event)
{
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = true;
+
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");
+ QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C");
}
- event["clipboardData"].call<void>("setData", getClipboardFormat(), getClipboardData());
- event.call<void>("preventDefault");
+ commonCopyEvent(event);
}
-static void qClipboardPasteTo(val event)
+static void qClipboardPasteTo(val dataTransfer)
{
- bool hasClipboardApi = QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi;
- val clipdata = hasClipboardApi ? getClipboardData() :
- event["clipboardData"].call<val>("getData", val("text"));
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = true;
+ val clipboardData = dataTransfer["clipboardData"];
+ val types = clipboardData["types"];
+ int typesCount = types["length"].as<int>();
+ std::string stdMimeFormat;
+ QMimeData *mMimeData = new QMimeData;
+ for (int i = 0; i < typesCount; i++) {
+ stdMimeFormat = types[i].as<std::string>();
+ QString mimeFormat = QString::fromStdString(stdMimeFormat);
+ if (mimeFormat.contains("STRING", Qt::CaseSensitive) || mimeFormat.contains("TEXT", Qt::CaseSensitive))
+ continue;
- const QString qstr = QWasmString::toQString(clipdata);
- if (qstr.length() > 0) {
- QMimeData *mMimeData = new QMimeData;
- mMimeData->setText(qstr);
- QWasmClipboard::qWasmClipboardPaste(mMimeData);
+ if (mimeFormat.contains("text")) {
+// also "text/plain;charset=utf-8"
+// "UTF8_STRING" "MULTIPLE"
+ val mimeData = clipboardData.call<val>("getData", val(stdMimeFormat)); // as DataTransfer
+
+ const QString qstr = QWasmString::toQString(mimeData);
+
+ if (qstr.length() > 0) {
+ if (mimeFormat.contains("text/html")) {
+ mMimeData->setHtml(qstr);
+ } else if (mimeFormat.isEmpty() || mimeFormat.contains("text/plain")) {
+ mMimeData->setText(qstr); // the type can be empty
+ } else {
+ mMimeData->setData(mimeFormat, qstr.toLocal8Bit());}
+ }
+ } else {
+ val items = clipboardData["items"];
+
+ int itemsCount = items["length"].as<int>();
+ // handle data
+ for (int i = 0; i < itemsCount; i++) {
+ val item = items[i];
+ val clipboardFile = item.call<emscripten::val>("getAsFile"); // string kind is handled above
+ if (clipboardFile.isUndefined() || item["kind"].as<std::string>() == "string" ) {
+ continue;
+ }
+ qstdweb::File file(clipboardFile);
+
+ mimeFormat = QString::fromStdString(file.type());
+ QByteArray fileContent;
+ fileContent.resize(file.size());
+
+ file.stream(fileContent.data(), [=]() {
+ if (!fileContent.isEmpty()) {
+
+ if (mimeFormat.contains("image")) {
+ QImage image;
+ image.loadFromData(fileContent, nullptr);
+ mMimeData->setImageData(image);
+ } else {
+ mMimeData->setData(mimeFormat,fileContent.data());
+ }
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
+ }
+ });
+ } // next item
+ }
}
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = false;
+}
+
+static void qClipboardTextPromiseResolve(emscripten::val clipdata)
+{
+ pasteClipboardData(emscripten::val("text/plain"), clipdata);
}
EMSCRIPTEN_BINDINGS(qtClipboardModule) {
+ function("qtPasteClipboardData", &pasteClipboardData);
+
+ function("qtClipboardTextPromiseResolve", &qClipboardTextPromiseResolve);
function("qtClipboardPromiseResolve", &qClipboardPromiseResolve);
+
+ function("qtClipboardCopyPromiseResolve", &qClipboardCopyPromiseResolve);
+ function("qtClipboardPromiseException", &qClipboardPromiseException);
+
function("qtClipboardCutTo", &qClipboardCutTo);
function("qtClipboardCopyTo", &qClipboardCopyTo);
function("qtClipboardPasteTo", &qClipboardPasteTo);
+ function("qtClipboardPasteResolve", &qClipboardPasteResolve);
}
-QWasmClipboard::QWasmClipboard()
+QWasmClipboard::QWasmClipboard() :
+ isPaste(false),
+ m_isListener(false)
{
val clipboard = val::global("navigator")["clipboard"];
val permissions = val::global("navigator")["permissions"];
- hasClipboardApi = (!clipboard.isUndefined() && !permissions.isUndefined() && !clipboard["readText"].isUndefined());
- if (hasClipboardApi)
- initClipboardEvents();
+ val hasInstallTrigger = val::global("window")["InstallTrigger"];
+
+ hasPermissionsApi = !permissions.isUndefined();
+ hasClipboardApi = (!clipboard.isUndefined() && !clipboard["readText"].isUndefined());
+ bool isFirefox = !hasInstallTrigger.isUndefined();
+ isSafari = !emscripten::val::global("window")["safari"].isUndefined();
+
+ // firefox has clipboard API if user sets these config tweaks:
+ // dom.events.asyncClipboard.clipboardItem true
+ // dom.events.asyncClipboard.read true
+ // dom.events.testing.asyncClipboard
+ // and permissions API, but does not currently support
+ // the clipboardRead and clipboardWrite permissions
+ if (hasClipboardApi && hasPermissionsApi && !isFirefox)
+ initClipboardPermissions();
}
QWasmClipboard::~QWasmClipboard()
{
- g_clipboardText.clear();
- g_clipboardFormat.clear();
}
-QMimeData* QWasmClipboard::mimeData(QClipboard::Mode mode)
+QMimeData *QWasmClipboard::mimeData(QClipboard::Mode mode)
{
if (mode != QClipboard::Clipboard)
return nullptr;
@@ -134,17 +295,18 @@ QMimeData* QWasmClipboard::mimeData(QClipboard::Mode mode)
return QPlatformClipboard::mimeData(mode);
}
-void QWasmClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode)
+void QWasmClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode)
{
- if (mimeData->hasText()) {
- g_clipboardFormat = mimeData->formats().at(0);
- g_clipboardText = mimeData->text();
- } else if (mimeData->hasHtml()) {
- g_clipboardFormat = mimeData->formats().at(0);
- g_clipboardText = mimeData->html();
- }
-
QPlatformClipboard::setMimeData(mimeData, mode);
+ // handle setText/ setData programmatically
+ if (!isPaste) {
+ if (hasClipboardApi) {
+ writeToClipboardApi();
+ } else if (!m_isListener) {
+ writeToClipboard(mimeData);
+ }
+ }
+ isPaste = false;
}
bool QWasmClipboard::supportsMode(QClipboard::Mode mode) const
@@ -163,10 +325,10 @@ void QWasmClipboard::qWasmClipboardPaste(QMimeData *mData)
QWasmIntegration::get()->clipboard()->setMimeData(mData, QClipboard::Clipboard);
QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
- 0, QEvent::KeyPress, Qt::Key_V, Qt::ControlModifier, "V");
+ 0, QEvent::KeyPress, Qt::Key_V, Qt::ControlModifier, "V");
}
-void QWasmClipboard::initClipboardEvents()
+void QWasmClipboard::initClipboardPermissions()
{
if (!hasClipboardApi)
return;
@@ -183,32 +345,122 @@ void QWasmClipboard::initClipboardEvents()
void QWasmClipboard::installEventHandlers(const emscripten::val &canvas)
{
- if (hasClipboardApi)
- return;
-
+ emscripten::val cContext = val::undefined();
+ emscripten::val isChromium = val::global("window")["chrome"];
+ if (!isChromium.isUndefined()) {
+ cContext = val::global("document");
+ } else {
+ cContext = canvas;
+ }
// Fallback path for browsers which do not support direct clipboard access
- canvas.call<void>("addEventListener", val("cut"),
- val::module_property("qtClipboardCutTo"));
- canvas.call<void>("addEventListener", val("copy"),
- val::module_property("qtClipboardCopyTo"));
- canvas.call<void>("addEventListener", val("paste"),
- val::module_property("qtClipboardPasteTo"));
+ cContext.call<void>("addEventListener", val("cut"),
+ val::module_property("qtClipboardCutTo"), true);
+ cContext.call<void>("addEventListener", val("copy"),
+ val::module_property("qtClipboardCopyTo"), true);
+ cContext.call<void>("addEventListener", val("paste"),
+ val::module_property("qtClipboardPasteTo"), true);
}
-void QWasmClipboard::readTextFromClipboard()
+void QWasmClipboard::writeToClipboardApi()
{
- if (QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
- val navigator = val::global("navigator");
- val textPromise = navigator["clipboard"].call<val>("readText");
- val readTextResolve = val::module_property("qtClipboardPromiseResolve");
- textPromise.call<val>("then", readTextResolve);
+ if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi)
+ return;
+
+ // copy event
+ // browser event handler detected ctrl c if clipboard API
+ // or Qt call from keyboard event handler
+
+ QMimeData *_mimes = QWasmIntegration::get()->getWasmClipboard()->mimeData(QClipboard::Clipboard);
+ if (!_mimes)
+ return;
+
+ emscripten::val clipboardWriteArray = emscripten::val::array();
+ QByteArray ba;
+
+ for (auto mimetype : _mimes->formats()) {
+ // we need to treat binary and text differently, as the blob method below
+ // fails for text mimetypes
+ // ignore text types
+
+ if (mimetype.contains("STRING", Qt::CaseSensitive) || mimetype.contains("TEXT", Qt::CaseSensitive))
+ continue;
+
+ if (_mimes->hasHtml()) { // prefer html over text
+ ba = _mimes->html().toLocal8Bit();
+ // force this mime
+ mimetype = "text/html";
+ } else if (mimetype.contains("text/plain")) {
+ ba = _mimes->text().toLocal8Bit();
+ } else if (mimetype.contains("image")) {
+ QImage img = qvariant_cast<QImage>( _mimes->imageData());
+ QBuffer buffer(&ba);
+ buffer.open(QIODevice::WriteOnly);
+ img.save(&buffer, "PNG");
+ mimetype = "image/png"; // chrome only allows png
+ // clipboard error "NotAllowedError" "Type application/x-qt-image not supported on write."
+ // safari silently fails
+ // so we use png internally for now
+ } else {
+ // DATA
+ ba = _mimes->data(mimetype);
+ }
+ // Create file data Blob
+
+ const char *content = ba.data();
+ int dataLength = ba.length();
+ if (dataLength < 1) {
+ qDebug() << "no content found";
+ return;
+ }
+
+ emscripten::val document = emscripten::val::global("document");
+ emscripten::val window = emscripten::val::global("window");
+
+ emscripten::val fileContentView =
+ emscripten::val(emscripten::typed_memory_view(dataLength, content));
+ emscripten::val fileContentCopy = emscripten::val::global("ArrayBuffer").new_(dataLength);
+ emscripten::val fileContentCopyView =
+ emscripten::val::global("Uint8Array").new_(fileContentCopy);
+ fileContentCopyView.call<void>("set", fileContentView);
+
+ emscripten::val contentArray = emscripten::val::array();
+ contentArray.call<void>("push", fileContentCopyView);
+
+ // we have a blob, now create a ClipboardItem
+ emscripten::val type = emscripten::val::array();
+ type.set("type", val(QWasmString::fromQString(mimetype)));
+
+ emscripten::val contentBlob = emscripten::val::global("Blob").new_(contentArray, type);
+
+ emscripten::val clipboardItemObject = emscripten::val::object();
+ clipboardItemObject.set(val(QWasmString::fromQString(mimetype)), contentBlob);
+
+ val clipboardItemData = val::global("ClipboardItem").new_(clipboardItemObject);
+
+ clipboardWriteArray.call<void>("push", clipboardItemData);
+
+ // Clipboard write is only supported with one ClipboardItem at the moment
+ // but somehow this still works?
+ // break;
}
+
+ val copyResolve = emscripten::val::module_property("qtClipboardCopyPromiseResolve");
+ val copyException = emscripten::val::module_property("qtClipboardPromiseException");
+
+ val navigator = val::global("navigator");
+ navigator["clipboard"]
+ .call<val>("write", clipboardWriteArray)
+ .call<val>("then", copyResolve)
+ .call<val>("catch", copyException);
}
-void QWasmClipboard::writeTextToClipboard()
+void QWasmClipboard::writeToClipboard(const QMimeData *data)
{
- if (QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
- val navigator = val::global("navigator");
- navigator["clipboard"].call<void>("writeText", getClipboardData());
- }
+ Q_UNUSED(data)
+ // this works for firefox, chrome by generating
+ // copy event, but not safari
+ // execCommand has been deemed deprecated in the docs, but browsers do not seem
+ // interested in removing it. There is no replacement, so we use it here.
+ val document = val::global("document");
+ document.call<val>("execCommand", val("copy"));
}
diff --git a/src/plugins/platforms/wasm/qwasmclipboard.h b/src/plugins/platforms/wasm/qwasmclipboard.h
index 3b28e2c381..9a33b79667 100644
--- a/src/plugins/platforms/wasm/qwasmclipboard.h
+++ b/src/plugins/platforms/wasm/qwasmclipboard.h
@@ -51,11 +51,15 @@ public:
bool ownsMode(QClipboard::Mode mode) const override;
static void qWasmClipboardPaste(QMimeData *mData);
- void initClipboardEvents();
+ void initClipboardPermissions();
void installEventHandlers(const emscripten::val &canvas);
bool hasClipboardApi;
- void readTextFromClipboard();
- void writeTextToClipboard();
+ bool hasPermissionsApi;
+ void writeToClipboardApi();
+ void writeToClipboard(const QMimeData *data);
+ bool isPaste;
+ bool m_isListener;
+ bool isSafari;
};
#endif // QWASMCLIPBOARD_H
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp
index 385e47f0a0..048dcfa2fc 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp
@@ -28,8 +28,11 @@
****************************************************************************/
#include "qwasmcompositor.h"
-#include "qwasmwindow.h"
#include "qwasmstylepixmaps_p.h"
+#include "qwasmwindow.h"
+#include "qwasmeventtranslator.h"
+#include "qwasmeventdispatcher.h"
+#include "qwasmclipboard.h"
#include <QtOpenGL/qopengltexture.h>
@@ -46,6 +49,10 @@
#include <QtCore/qcoreapplication.h>
#include <QtGui/qguiapplication.h>
+#include <emscripten/bind.h>
+
+using namespace emscripten;
+
Q_GUI_EXPORT int qt_defaultDpiX();
QWasmCompositedWindow::QWasmCompositedWindow()
@@ -56,6 +63,25 @@ QWasmCompositedWindow::QWasmCompositedWindow()
{
}
+// macOS CTRL <-> META switching. We most likely want to enable
+// the existing switching code in QtGui, but for now do it here.
+
+
+bool g_useNaturalScrolling = true; // natural scrolling is default on linux/windows
+
+static void mouseWheelEvent(emscripten::val event) {
+
+ emscripten::val wheelInterted = event["webkitDirectionInvertedFromDevice"];
+
+ if (wheelInterted.as<bool>()) {
+ g_useNaturalScrolling = true;
+ }
+}
+
+EMSCRIPTEN_BINDINGS(qtMouseModule) {
+ function("qtMouseWheelEvent", &mouseWheelEvent);
+}
+
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
:QObject(screen)
, m_blitter(new QOpenGLTextureBlitter)
@@ -64,14 +90,51 @@ QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
, m_inResize(false)
, m_isEnabled(true)
, m_targetDevicePixelRatio(1)
+ , draggedWindow(nullptr)
+ , lastWindow(nullptr)
+ , pressedButtons(Qt::NoButton)
+ , resizeMode(QWasmCompositor::ResizeNone)
+ , eventTranslator(new QWasmEventTranslator())
{
+ touchDevice = new QPointingDevice(
+ "touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
+ QPointingDevice::PointerType::Finger,
+ QPointingDevice::Capability::Position | QPointingDevice::Capability::Area | QPointingDevice::Capability::NormalizedPosition,
+ 10, 0);
+ QWindowSystemInterface::registerInputDevice(touchDevice);
+
+ initEventHandlers();
}
QWasmCompositor::~QWasmCompositor()
{
+ if (m_requestAnimationFrameId != -1)
+ emscripten_cancel_animation_frame(m_requestAnimationFrameId);
+
+ deregisterEventHandlers();
destroy();
}
+void QWasmCompositor::deregisterEventHandlers()
+{
+ QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8();
+ emscripten_set_keydown_callback(canvasSelector.constData(), 0, 0, NULL);
+ emscripten_set_keyup_callback(canvasSelector.constData(), 0, 0, NULL);
+
+ emscripten_set_mousedown_callback(canvasSelector.constData(), 0, 0, NULL);
+ emscripten_set_mouseup_callback(canvasSelector.constData(), 0, 0, NULL);
+ emscripten_set_mousemove_callback(canvasSelector.constData(), 0, 0, NULL);
+
+ emscripten_set_focus_callback(canvasSelector.constData(), 0, 0, NULL);
+
+ emscripten_set_wheel_callback(canvasSelector.constData(), 0, 0, NULL);
+
+ emscripten_set_touchstart_callback(canvasSelector.constData(), 0, 0, NULL);
+ emscripten_set_touchend_callback(canvasSelector.constData(), 0, 0, NULL);
+ emscripten_set_touchmove_callback(canvasSelector.constData(), 0, 0, NULL);
+ emscripten_set_touchcancel_callback(canvasSelector.constData(), 0, 0, NULL);
+}
+
void QWasmCompositor::destroy()
{
// Destroy OpenGL resources. This is done here in a separate function
@@ -93,6 +156,48 @@ void QWasmCompositor::destroy()
m_isEnabled = false; // prevent frame() from creating a new m_context
}
+void QWasmCompositor::initEventHandlers()
+{
+ QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8();
+
+ // 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")));
+
+ eventTranslator->setIsMac(platform == MacOSPlatform);
+
+ if (platform == MacOSPlatform) {
+ g_useNaturalScrolling = false; // make this !default on macOS
+
+ if (!emscripten::val::global("window")["safari"].isUndefined()) {
+ val canvas = screen()->canvas();
+ canvas.call<void>("addEventListener",
+ val("wheel"),
+ val::module_property("qtMouseWheelEvent"));
+ }
+ }
+
+ emscripten_set_keydown_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb);
+ emscripten_set_keyup_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb);
+
+ emscripten_set_mousedown_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb);
+ emscripten_set_mouseup_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb);
+ emscripten_set_mousemove_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb);
+
+ emscripten_set_focus_callback(canvasSelector.constData(), (void *)this, 1, &focus_cb);
+
+ emscripten_set_wheel_callback(canvasSelector.constData(), (void *)this, 1, &wheel_cb);
+
+ emscripten_set_touchstart_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
+ emscripten_set_touchend_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
+ emscripten_set_touchmove_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
+ emscripten_set_touchcancel_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
+}
+
void QWasmCompositor::setEnabled(bool enabled)
{
m_isEnabled = enabled;
@@ -131,8 +236,8 @@ void QWasmCompositor::removeWindow(QWasmWindow *window)
if (!m_windowStack.isEmpty() && !QGuiApplication::focusWindow()) {
auto lastWindow = m_windowStack.last();
lastWindow->requestActivateWindow();
+ notifyTopWindowChanged(lastWindow);
}
- notifyTopWindowChanged(window);
}
void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
@@ -148,7 +253,7 @@ void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
else
m_globalDamage = compositedWindow.window->geometry(); // repaint previously covered area.
- requestRedraw();
+ requestUpdateWindow(window, QWasmCompositor::ExposeEventDelivery);
}
void QWasmCompositor::raise(QWasmWindow *window)
@@ -181,16 +286,7 @@ void QWasmCompositor::setParent(QWasmWindow *window, QWasmWindow *parent)
{
m_compositedWindows[window].parentWindow = parent;
- requestRedraw();
-}
-
-void QWasmCompositor::flush(QWasmWindow *window, const QRegion &region)
-{
- QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
- compositedWindow.flushPending = true;
- compositedWindow.damage = region;
-
- requestRedraw();
+ requestUpdate();
}
int QWasmCompositor::windowCount() const
@@ -198,28 +294,6 @@ int QWasmCompositor::windowCount() const
return m_windowStack.count();
}
-
-void QWasmCompositor::redrawWindowContent()
-{
- // Redraw window content by sending expose events. This redraw
- // will cause a backing store flush, which will call requestRedraw()
- // to composit.
- for (QWasmWindow *platformWindow : m_windowStack) {
- QWindow *window = platformWindow->window();
- QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
- window, QRect(QPoint(0, 0), window->geometry().size()));
- }
-}
-
-void QWasmCompositor::requestRedraw()
-{
- if (m_needComposit)
- return;
-
- m_needComposit = true;
- QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
-}
-
QWindow *QWasmCompositor::windowAt(QPoint globalPoint, int padding) const
{
int index = m_windowStack.count() - 1;
@@ -245,17 +319,6 @@ QWindow *QWasmCompositor::keyWindow() const
return m_windowStack.at(m_windowStack.count() - 1)->window();
}
-bool QWasmCompositor::event(QEvent *ev)
-{
- if (ev->type() == QEvent::UpdateRequest) {
- if (m_isEnabled)
- frame();
- return true;
- }
-
- return QObject::event(ev);
-}
-
void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, const QOpenGLTexture *texture, QRect targetGeometry)
{
QMatrix4x4 m;
@@ -366,6 +429,97 @@ QRect QWasmCompositor::titlebarRect(QWasmTitleBarOptions tb, QWasmCompositor::Su
return rect;
}
+void QWasmCompositor::requestUpdateAllWindows()
+{
+ m_requestUpdateAllWindows = true;
+ requestUpdate();
+}
+
+void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType)
+{
+ auto it = m_requestUpdateWindows.find(window);
+ if (it == m_requestUpdateWindows.end()) {
+ m_requestUpdateWindows.insert(window, updateType);
+ } else {
+ // Already registered, but upgrade ExposeEventDeliveryType to UpdateRequestDeliveryType.
+ // if needed, to make sure QWindow::updateRequest's are matched.
+ if (it.value() == ExposeEventDelivery && updateType == UpdateRequestDelivery)
+ it.value() = UpdateRequestDelivery;
+ }
+
+ requestUpdate();
+}
+
+// Requests an upate/new frame using RequestAnimationFrame
+void QWasmCompositor::requestUpdate()
+{
+ if (m_requestAnimationFrameId != -1)
+ return;
+
+ static auto frame = [](double frameTime, void *context) -> int {
+ Q_UNUSED(frameTime);
+ QWasmCompositor *compositor = reinterpret_cast<QWasmCompositor *>(context);
+ compositor->m_requestAnimationFrameId = -1;
+ compositor->deliverUpdateRequests();
+ return 0;
+ };
+ m_requestAnimationFrameId = emscripten_request_animation_frame(frame, this);
+}
+
+void QWasmCompositor::deliverUpdateRequests()
+{
+ // We may get new update requests during the window content update below:
+ // prepare for recording the new update set by setting aside the current
+ // update set.
+ auto requestUpdateWindows = m_requestUpdateWindows;
+ m_requestUpdateWindows.clear();
+ bool requestUpdateAllWindows = m_requestUpdateAllWindows;
+ m_requestUpdateAllWindows = false;
+
+ // Update window content, either all windows or a spesific set of windows. Use the correct update
+ // type: QWindow subclasses expect that requested and delivered updateRequests matches exactly.
+ m_inDeliverUpdateRequest = true;
+ if (requestUpdateAllWindows) {
+ for (QWasmWindow *window : m_windowStack) {
+ auto it = requestUpdateWindows.find(window);
+ UpdateRequestDeliveryType updateType =
+ (it == m_requestUpdateWindows.end() ? ExposeEventDelivery : it.value());
+ deliverUpdateRequest(window, updateType);
+ }
+ } else {
+ for (auto it = requestUpdateWindows.constBegin(); it != requestUpdateWindows.constEnd(); ++it) {
+ auto *window = it.key();
+ UpdateRequestDeliveryType updateType = it.value();
+ deliverUpdateRequest(window, updateType);
+ }
+ }
+ m_inDeliverUpdateRequest = false;
+
+ // Compose window content
+ frame();
+}
+
+void QWasmCompositor::deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType)
+{
+ // update by deliverUpdateRequest and expose event accordingly.
+ if (updateType == UpdateRequestDelivery) {
+ window->QPlatformWindow::deliverUpdateRequest();
+ } else {
+ QWindow *qwindow = window->window();
+ QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
+ qwindow, QRect(QPoint(0, 0), qwindow->geometry().size()));
+ }
+}
+
+void QWasmCompositor::handleBackingStoreFlush()
+{
+ // Request update to flush the updated backing store content,
+ // unless we are currently processing an update, in which case
+ // the new content will flushed as a part of that update.
+ if (!m_inDeliverUpdateRequest)
+ requestUpdate();
+}
+
int dpiScaled(qreal value)
{
return value * (qreal(qt_defaultDpiX()) / 96.0);
@@ -411,6 +565,8 @@ QWasmCompositor::QWasmTitleBarOptions QWasmCompositor::makeTitleBarOptions(const
if (!window->window()->title().isEmpty())
titleBarOptions.titleBarOptionsString = window->window()->title();
+ titleBarOptions.windowIcon = window->window()->icon();
+
return titleBarOptions;
}
@@ -554,13 +710,11 @@ void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *pain
Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb.titleBarOptionsString);
} // SC_TitleBarLabel
- bool down = false;
QPixmap pixmap;
if (tb.subControls.testFlag(SC_TitleBarCloseButton)
&& tb.flags & Qt::WindowSystemMenuHint) {
ir = titlebarRect(tb, SC_TitleBarCloseButton);
- down = tb.subControls & SC_TitleBarCloseButton && (tb.state & State_Sunken);
pixmap = cachedPixmapFromXPM(qt_close_xpm).scaled(QSize(10, 10));
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
} //SC_TitleBarCloseButton
@@ -569,7 +723,6 @@ void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *pain
&& tb.flags & Qt::WindowMaximizeButtonHint
&& !(tb.state & Qt::WindowMaximized)) {
ir = titlebarRect(tb, SC_TitleBarMaxButton);
- down = tb.subControls & SC_TitleBarMaxButton && (tb.state & State_Sunken);
pixmap = cachedPixmapFromXPM(qt_maximize_xpm).scaled(QSize(10, 10));
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
} //SC_TitleBarMaxButton
@@ -582,7 +735,6 @@ void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *pain
if (drawNormalButton) {
ir = titlebarRect(tb, SC_TitleBarNormalButton);
- down = tb.subControls & SC_TitleBarNormalButton && (tb.state & State_Sunken);
pixmap = cachedPixmapFromXPM(qt_normalizeup_xpm).scaled( QSize(10, 10));
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
@@ -590,8 +742,12 @@ void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *pain
if (tb.subControls & SC_TitleBarSysMenu && tb.flags & Qt::WindowSystemMenuHint) {
ir = titlebarRect(tb, SC_TitleBarSysMenu);
- pixmap = cachedPixmapFromXPM(qt_menu_xpm).scaled(QSize(10, 10));
- drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
+ if (!tb.windowIcon.isNull()) {
+ tb.windowIcon.paint(painter, ir, Qt::AlignCenter);
+ } else {
+ pixmap = cachedPixmapFromXPM(qt_menu_xpm).scaled(QSize(10, 10));
+ drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
+ }
}
}
@@ -676,11 +832,6 @@ void QWasmCompositor::drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *sc
void QWasmCompositor::frame()
{
- if (!m_needComposit)
- return;
-
- m_needComposit = false;
-
if (!m_isEnabled || m_windowStack.empty() || !screen())
return;
@@ -735,6 +886,62 @@ void QWasmCompositor::frame()
m_context->swapBuffers(someWindow->window());
}
+void QWasmCompositor::resizeWindow(QWindow *window, QWasmCompositor::ResizeMode mode,
+ QRect startRect, QPoint amount)
+{
+ if (mode == QWasmCompositor::ResizeNone)
+ return;
+
+ bool top = mode == QWasmCompositor::ResizeTopLeft ||
+ mode == QWasmCompositor::ResizeTop ||
+ mode == QWasmCompositor::ResizeTopRight;
+
+ bool bottom = mode == QWasmCompositor::ResizeBottomLeft ||
+ mode == QWasmCompositor::ResizeBottom ||
+ mode == QWasmCompositor::ResizeBottomRight;
+
+ bool left = mode == QWasmCompositor::ResizeLeft ||
+ mode == QWasmCompositor::ResizeTopLeft ||
+ mode == QWasmCompositor::ResizeBottomLeft;
+
+ bool right = mode == QWasmCompositor::ResizeRight ||
+ mode == QWasmCompositor::ResizeTopRight ||
+ mode == QWasmCompositor::ResizeBottomRight;
+
+ int x1 = startRect.left();
+ int y1 = startRect.top();
+ int x2 = startRect.right();
+ int y2 = startRect.bottom();
+
+ if (left)
+ x1 += amount.x();
+ if (top)
+ y1 += amount.y();
+ if (right)
+ x2 += amount.x();
+ if (bottom)
+ y2 += amount.y();
+
+ int w = x2-x1;
+ int h = y2-y1;
+
+ if (w < window->minimumWidth()) {
+ if (left)
+ x1 -= window->minimumWidth() - w;
+
+ w = window->minimumWidth();
+ }
+
+ if (h < window->minimumHeight()) {
+ if (top)
+ y1 -= window->minimumHeight() - h;
+
+ h = window->minimumHeight();
+ }
+
+ window->setGeometry(x1, y1, w, h);
+}
+
void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
{
QWindow *modalWindow;
@@ -746,8 +953,7 @@ void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
return;
}
- requestRedraw();
-
+ requestUpdate();
}
QWasmScreen *QWasmCompositor::screen()
@@ -759,3 +965,345 @@ QOpenGLContext *QWasmCompositor::context()
{
return m_context.data();
}
+
+int QWasmCompositor::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
+{
+ QWasmCompositor *wasmCompositor = reinterpret_cast<QWasmCompositor *>(userData);
+ bool accepted = wasmCompositor->processKeyboard(eventType, keyEvent);
+
+ return accepted ? 1 : 0;
+}
+
+int QWasmCompositor::mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+{
+ QWasmCompositor *compositor = (QWasmCompositor*)userData;
+ bool accepted = compositor->processMouse(eventType, mouseEvent);
+ return accepted;
+}
+
+int QWasmCompositor::focus_cb(int /*eventType*/, const EmscriptenFocusEvent */*focusEvent*/, void */*userData*/)
+{
+ return 0;
+}
+
+int QWasmCompositor::wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
+{
+ QWasmCompositor *compositor = (QWasmCompositor *) userData;
+ bool accepted = compositor->processWheel(eventType, wheelEvent);
+ return accepted ? 1 : 0;
+}
+
+int QWasmCompositor::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
+{
+ auto compositor = reinterpret_cast<QWasmCompositor*>(userData);
+ return compositor->handleTouch(eventType, touchEvent);
+}
+
+bool QWasmCompositor::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent)
+{
+ QPoint targetPoint(mouseEvent->targetX, mouseEvent->targetY);
+ QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
+
+ QEvent::Type buttonEventType = QEvent::None;
+ Qt::MouseButton button = Qt::NoButton;
+ Qt::KeyboardModifiers modifiers = eventTranslator->translateMouseEventModifier(mouseEvent);
+
+ QWindow *window2 = nullptr;
+ if (resizeMode == QWasmCompositor::ResizeNone)
+ window2 = screen()->compositor()->windowAt(globalPoint, 5);
+
+ if (window2 == nullptr) {
+ window2 = lastWindow;
+ } else {
+ lastWindow = window2;
+ }
+
+ QPoint localPoint = window2->mapFromGlobal(globalPoint);
+ bool interior = window2->geometry().contains(globalPoint);
+
+ QWasmWindow *htmlWindow = static_cast<QWasmWindow*>(window2->handle());
+ switch (eventType) {
+ case EMSCRIPTEN_EVENT_MOUSEDOWN:
+ {
+ button = QWasmEventTranslator::translateMouseButton(mouseEvent->button);
+
+ if (window2)
+ window2->requestActivate();
+
+ pressedButtons.setFlag(button);
+
+ pressedWindow = window2;
+ buttonEventType = QEvent::MouseButtonPress;
+
+ // button overview:
+ // 0 = primary mouse button, usually left click
+ // 1 = middle mouse button, usually mouse wheel
+ // 2 = right mouse button, usually right click
+ // from: https://w3c.github.io/uievents/#dom-mouseevent-button
+ if (mouseEvent->button == 0) {
+ if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) {
+ if (htmlWindow && window2->flags().testFlag(Qt::WindowTitleHint) && htmlWindow->isPointOnTitle(globalPoint))
+ draggedWindow = window2;
+ else if (htmlWindow && htmlWindow->isPointOnResizeRegion(globalPoint)) {
+ draggedWindow = window2;
+ resizeMode = htmlWindow->resizeModeAtPoint(globalPoint);
+ resizePoint = globalPoint;
+ resizeStartRect = window2->geometry();
+ }
+ }
+ }
+
+ htmlWindow->injectMousePressed(localPoint, globalPoint, button, modifiers);
+ break;
+ }
+ case EMSCRIPTEN_EVENT_MOUSEUP:
+ {
+ button = QWasmEventTranslator::translateMouseButton(mouseEvent->button);
+ pressedButtons.setFlag(button, false);
+ buttonEventType = QEvent::MouseButtonRelease;
+ QWasmWindow *oldWindow = nullptr;
+
+ if (mouseEvent->button == 0 && pressedWindow) {
+ oldWindow = static_cast<QWasmWindow*>(pressedWindow->handle());
+ pressedWindow = nullptr;
+ }
+
+ if (draggedWindow && pressedButtons.testFlag(Qt::NoButton)) {
+ draggedWindow = nullptr;
+ resizeMode = QWasmCompositor::ResizeNone;
+ }
+
+ if (oldWindow)
+ oldWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers);
+ else
+ htmlWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers);
+ break;
+ }
+ case EMSCRIPTEN_EVENT_MOUSEMOVE: // drag event
+ {
+ buttonEventType = QEvent::MouseMove;
+
+ if (htmlWindow && pressedButtons.testFlag(Qt::NoButton)) {
+
+ if (htmlWindow->isPointOnResizeRegion(globalPoint)) {
+ QCursor resizingCursor = eventTranslator->cursorForMode(htmlWindow->resizeModeAtPoint(globalPoint));
+
+ if (resizingCursor != window2->cursor()) {
+ isCursorOverridden = true;
+ QWasmCursor::setOverrideWasmCursor(&resizingCursor, window2->screen());
+ }
+ } else { // off resizing area
+ if (isCursorOverridden) {
+ isCursorOverridden = false;
+ QWasmCursor::clearOverrideWasmCursor(window2->screen());
+ }
+ }
+ }
+
+ if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) {
+ if (resizeMode == QWasmCompositor::ResizeNone && draggedWindow) {
+ draggedWindow->setX(draggedWindow->x() + mouseEvent->movementX);
+ draggedWindow->setY(draggedWindow->y() + mouseEvent->movementY);
+ }
+
+ if (resizeMode != QWasmCompositor::ResizeNone && !(htmlWindow->m_windowState & Qt::WindowFullScreen)) {
+ QPoint delta = QPoint(mouseEvent->targetX, mouseEvent->targetY) - resizePoint;
+ resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta);
+ }
+ }
+ break;
+ }
+ default: // MOUSELEAVE MOUSEENTER
+ break;
+ };
+ if (!window2 && buttonEventType == QEvent::MouseButtonRelease) {
+ window2 = lastWindow;
+ lastWindow = nullptr;
+ interior = true;
+ }
+ bool accepted = true;
+ if (window2 && interior) {
+ accepted = QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(
+ window2, QWasmIntegration::getTimestamp(), localPoint, globalPoint, pressedButtons, button, buttonEventType, modifiers);
+ }
+ return accepted;
+}
+
+bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent)
+{
+ Qt::Key qtKey;
+ QString keyText;
+ QEvent::Type keyType = QEvent::None;
+ switch (eventType) {
+ case EMSCRIPTEN_EVENT_KEYPRESS:
+ case EMSCRIPTEN_EVENT_KEYDOWN: // down
+ keyType = QEvent::KeyPress;
+ keyText = this->eventTranslator->getKeyText(keyEvent);
+ qtKey = this->eventTranslator->getKey(keyEvent);
+ break;
+ case EMSCRIPTEN_EVENT_KEYUP: // up
+ keyType = QEvent::KeyRelease;
+ this->eventTranslator->setStickyDeadKey(keyEvent);
+ break;
+ default:
+ break;
+ };
+
+ if (keyType == QEvent::None)
+ return 0;
+
+ QFlags<Qt::KeyboardModifier> modifiers = eventTranslator->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)) {
+ if (qtKey == Qt::Key_V) {
+ QWasmIntegration::get()->getWasmClipboard()->isPaste = true;
+ }
+ return false;
+ }
+
+ bool accepted = false;
+
+ if (keyType == QEvent::KeyPress &&
+ modifiers.testFlag(Qt::ControlModifier)
+ && qtKey == Qt::Key_V) {
+ QWasmIntegration::get()->getWasmClipboard()->isPaste = true;
+ accepted = false; // continue on to event
+ } else {
+ if (keyText.isEmpty())
+ keyText = QString(keyEvent->key);
+ if (keyText.size() > 1)
+ keyText.clear();
+ accepted = QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, keyType, qtKey, modifiers, keyText);
+ }
+ if (keyType == QEvent::KeyPress &&
+ modifiers.testFlag(Qt::ControlModifier)
+ && qtKey == Qt::Key_C) {
+ QWasmIntegration::get()->getWasmClipboard()->isPaste = false;
+ accepted = false; // continue on to event
+ }
+
+ return accepted;
+}
+
+bool QWasmCompositor::processWheel(int eventType, const EmscriptenWheelEvent *wheelEvent)
+{
+ Q_UNUSED(eventType);
+
+ EmscriptenMouseEvent mouseEvent = wheelEvent->mouse;
+
+ int scrollFactor = 0;
+ switch (wheelEvent->deltaMode) {
+ case DOM_DELTA_PIXEL://chrome safari
+ scrollFactor = 1;
+ break;
+ case DOM_DELTA_LINE: //firefox
+ scrollFactor = 12;
+ break;
+ case DOM_DELTA_PAGE:
+ scrollFactor = 20;
+ break;
+ };
+
+ if (g_useNaturalScrolling) //macOS platform has document oriented scrolling
+ scrollFactor = -scrollFactor;
+
+ Qt::KeyboardModifiers modifiers = eventTranslator->translateMouseEventModifier(&mouseEvent);
+ QPoint targetPoint(mouseEvent.targetX, mouseEvent.targetY);
+ QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
+
+ QWindow *window2 = screen()->compositor()->windowAt(globalPoint, 5);
+ if (!window2)
+ return 0;
+ QPoint localPoint = window2->mapFromGlobal(globalPoint);
+
+ QPoint pixelDelta;
+
+ if (wheelEvent->deltaY != 0) pixelDelta.setY(wheelEvent->deltaY * scrollFactor);
+ if (wheelEvent->deltaX != 0) pixelDelta.setX(wheelEvent->deltaX * scrollFactor);
+
+ bool accepted = QWindowSystemInterface::handleWheelEvent(
+ window2, QWasmIntegration::getTimestamp(), localPoint,
+ globalPoint, QPoint(), pixelDelta, modifiers);
+ return accepted;
+}
+
+int QWasmCompositor::handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent)
+{
+ QList<QWindowSystemInterface::TouchPoint> touchPointList;
+ touchPointList.reserve(touchEvent->numTouches);
+ QWindow *window2;
+
+ for (int i = 0; i < touchEvent->numTouches; i++) {
+
+ const EmscriptenTouchPoint *touches = &touchEvent->touches[i];
+
+ QPoint targetPoint(touches->targetX, touches->targetY);
+ QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
+
+ window2 = this->screen()->compositor()->windowAt(globalPoint, 5);
+ if (window2 == nullptr)
+ continue;
+
+ QWindowSystemInterface::TouchPoint touchPoint;
+
+ touchPoint.area = QRect(0, 0, 8, 8);
+ touchPoint.id = touches->identifier;
+ touchPoint.pressure = 1.0;
+
+ touchPoint.area.moveCenter(globalPoint);
+
+ const auto tp = pressedTouchIds.constFind(touchPoint.id);
+ if (tp != pressedTouchIds.constEnd())
+ touchPoint.normalPosition = tp.value();
+
+ QPointF localPoint = QPointF(window2->mapFromGlobal(globalPoint));
+ QPointF normalPosition(localPoint.x() / window2->width(),
+ localPoint.y() / window2->height());
+
+ const bool stationaryTouchPoint = (normalPosition == touchPoint.normalPosition);
+ touchPoint.normalPosition = normalPosition;
+
+ switch (eventType) {
+ case EMSCRIPTEN_EVENT_TOUCHSTART:
+ if (tp != pressedTouchIds.constEnd()) {
+ touchPoint.state = (stationaryTouchPoint
+ ? QEventPoint::State::Stationary
+ : QEventPoint::State::Updated);
+ } else {
+ touchPoint.state = QEventPoint::State::Pressed;
+ }
+ pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
+
+ break;
+ case EMSCRIPTEN_EVENT_TOUCHEND:
+ touchPoint.state = QEventPoint::State::Released;
+ pressedTouchIds.remove(touchPoint.id);
+ break;
+ case EMSCRIPTEN_EVENT_TOUCHMOVE:
+ touchPoint.state = (stationaryTouchPoint
+ ? QEventPoint::State::Stationary
+ : QEventPoint::State::Updated);
+
+ pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
+ break;
+ default:
+ break;
+ }
+
+ touchPointList.append(touchPoint);
+ }
+
+ QFlags<Qt::KeyboardModifier> keyModifier = eventTranslator->translateTouchEventModifier(touchEvent);
+
+ bool accepted = QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(
+ window2, QWasmIntegration::getTimestamp(), touchDevice, touchPointList, keyModifier);
+
+ if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL)
+ accepted = QWindowSystemInterface::handleTouchCancelEvent(window2, QWasmIntegration::getTimestamp(), touchDevice, keyModifier);
+
+ return static_cast<int>(accepted);
+}
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h
index a07c747a98..1feda04870 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.h
+++ b/src/plugins/platforms/wasm/qwasmcompositor.h
@@ -36,6 +36,12 @@
#include <QtOpenGL/qopengltextureblitter.h>
#include <QtGui/qpalette.h>
#include <QtGui/qpainter.h>
+#include <QtGui/qinputdevice.h>
+
+#include <QPointer>
+#include <QPointingDevice>
+
+#include <emscripten/html5.h>
QT_BEGIN_NAMESPACE
@@ -43,6 +49,7 @@ class QWasmWindow;
class QWasmScreen;
class QOpenGLContext;
class QOpenGLTexture;
+class QWasmEventTranslator;
class QWasmCompositedWindow
{
@@ -63,6 +70,7 @@ class QWasmCompositor : public QObject
public:
QWasmCompositor(QWasmScreen *screen);
~QWasmCompositor();
+ void deregisterEventHandlers();
void destroy();
enum QWasmSubControl {
@@ -84,6 +92,18 @@ public:
};
Q_DECLARE_FLAGS(StateFlags, QWasmStateFlag)
+ enum ResizeMode {
+ ResizeNone,
+ ResizeTopLeft,
+ ResizeTop,
+ ResizeTopRight,
+ ResizeRight,
+ ResizeBottomRight,
+ ResizeBottom,
+ ResizeBottomLeft,
+ ResizeLeft
+ };
+
struct QWasmTitleBarOptions {
QRect rect;
Qt::WindowFlags flags;
@@ -91,6 +111,7 @@ public:
QPalette palette;
QString titleBarOptionsString;
QWasmCompositor::SubControls subControls;
+ QIcon windowIcon;
};
struct QWasmFrameOptions {
@@ -109,28 +130,35 @@ public:
void lower(QWasmWindow *window);
void setParent(QWasmWindow *window, QWasmWindow *parent);
- void flush(QWasmWindow *surface, const QRegion &region);
-
int windowCount() const;
- void redrawWindowContent();
- void requestRedraw();
-
QWindow *windowAt(QPoint globalPoint, int padding = 0) const;
QWindow *keyWindow() const;
- bool event(QEvent *event);
-
static QWasmTitleBarOptions makeTitleBarOptions(const QWasmWindow *window);
static QRect titlebarRect(QWasmTitleBarOptions tb, QWasmCompositor::SubControls subcontrol);
QWasmScreen *screen();
QOpenGLContext *context();
+ enum UpdateRequestDeliveryType { ExposeEventDelivery, UpdateRequestDelivery };
+ void requestUpdateAllWindows();
+ void requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType = ExposeEventDelivery);
+ void requestUpdate();
+ void deliverUpdateRequests();
+ void deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType);
+ void handleBackingStoreFlush();
+ bool processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent);
+ bool processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent);
+ bool processWheel(int eventType, const EmscriptenWheelEvent *wheelEvent);
+ int handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent);
+ void resizeWindow(QWindow *window, QWasmCompositor::ResizeMode mode, QRect startRect, QPoint amount);
+
private slots:
void frame();
private:
+ void initEventHandlers();
void notifyTopWindowChanged(QWasmWindow *window);
void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
@@ -151,6 +179,25 @@ private:
bool m_isEnabled;
QSize m_targetSize;
qreal m_targetDevicePixelRatio;
+ QMap<QWasmWindow *, UpdateRequestDeliveryType> m_requestUpdateWindows;
+ bool m_requestUpdateAllWindows = false;
+ int m_requestAnimationFrameId = -1;
+ bool m_inDeliverUpdateRequest = false;
+
+ QPointer<QWindow> draggedWindow;
+ QPointer<QWindow> pressedWindow;
+ QPointer<QWindow> lastWindow;
+ Qt::MouseButtons pressedButtons;
+
+ QWasmCompositor::ResizeMode resizeMode;
+ QPoint resizePoint;
+ QRect resizeStartRect;
+ QPointingDevice *touchDevice;
+
+ QMap <int, QPointF> pressedTouchIds;
+
+ QCursor overriddenCursor;
+ bool isCursorOverridden = false;
static QPalette makeWindowPalette();
@@ -159,6 +206,15 @@ private:
void drawShadePanel(QWasmTitleBarOptions options, QPainter *painter);
void drawItemPixmap(QPainter *painter, const QRect &rect,
int alignment, const QPixmap &pixmap) const;
+
+ static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData);
+ static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
+ static int focus_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
+ static int wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData);
+
+ static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);
+
+ QWasmEventTranslator *eventTranslator;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QWasmCompositor::SubControls)
diff --git a/src/plugins/platforms/wasm/qwasmcursor.cpp b/src/plugins/platforms/wasm/qwasmcursor.cpp
index 61204517ce..a9336bdc8f 100644
--- a/src/plugins/platforms/wasm/qwasmcursor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcursor.cpp
@@ -41,25 +41,25 @@ using namespace emscripten;
void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
{
- if (!windowCursor || !window)
+ if (!window)
return;
QScreen *screen = window->screen();
if (!screen)
return;
- // Bitmap and custom cursors are not implemented (will fall back to "auto")
- if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor)
- qWarning() << "QWasmCursor: bitmap and custom cursors are not supported";
+ if (windowCursor) {
- QByteArray htmlCursorName = cursorShapeToHtml(windowCursor->shape());
+ // Bitmap and custom cursors are not implemented (will fall back to "auto")
+ if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor)
+ qWarning() << "QWasmCursor: bitmap and custom cursors are not supported";
+
+ htmlCursorName = cursorShapeToHtml(windowCursor->shape());
+ }
if (htmlCursorName.isEmpty())
- htmlCursorName = "auto";
+ htmlCursorName = "default";
- // Set cursor on the canvas
- val canvas = QWasmScreen::get(screen)->canvas();
- val canvasStyle = canvas["style"];
- canvasStyle.set("cursor", val(htmlCursorName.constData()));
+ setWasmCursor(screen, htmlCursorName);
}
QByteArray QWasmCursor::cursorShapeToHtml(Qt::CursorShape shape)
@@ -139,3 +139,23 @@ QByteArray QWasmCursor::cursorShapeToHtml(Qt::CursorShape shape)
return cursorName;
}
+
+void QWasmCursor::setWasmCursor(QScreen *screen, const QByteArray &name)
+{
+ // Set cursor on the canvas
+ val canvas = QWasmScreen::get(screen)->canvas();
+ val canvasStyle = canvas["style"];
+ canvasStyle.set("cursor", val(name.constData()));
+}
+
+void QWasmCursor::setOverrideWasmCursor(QCursor *windowCursor, QScreen *screen)
+{
+ QWasmCursor *wCursor = static_cast<QWasmCursor *>(QWasmScreen::get(screen)->cursor());
+ wCursor->setWasmCursor(screen, wCursor->cursorShapeToHtml(windowCursor->shape()));
+}
+
+void QWasmCursor::clearOverrideWasmCursor(QScreen *screen)
+{
+ QWasmCursor *wCursor = static_cast<QWasmCursor *>(QWasmScreen::get(screen)->cursor());
+ wCursor->setWasmCursor(screen, wCursor->htmlCursorName);
+}
diff --git a/src/plugins/platforms/wasm/qwasmcursor.h b/src/plugins/platforms/wasm/qwasmcursor.h
index 516e07aa31..b8aef841b4 100644
--- a/src/plugins/platforms/wasm/qwasmcursor.h
+++ b/src/plugins/platforms/wasm/qwasmcursor.h
@@ -38,6 +38,11 @@ public:
void changeCursor(QCursor *windowCursor, QWindow *window) override;
QByteArray cursorShapeToHtml(Qt::CursorShape shape);
+ static void setOverrideWasmCursor(QCursor *windowCursor, QScreen *screen);
+ static void clearOverrideWasmCursor(QScreen *screen);
+private:
+ QByteArray htmlCursorName;
+ void setWasmCursor(QScreen *screen, const QByteArray &name);
};
#endif
diff --git a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
index 8f9eeecbb0..d4b4bce462 100644
--- a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
@@ -29,197 +29,11 @@
#include "qwasmeventdispatcher.h"
-#include <QtCore/qcoreapplication.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
-#include <emscripten.h>
-
-#if QT_CONFIG(thread)
-#if (__EMSCRIPTEN_major__ > 1 || __EMSCRIPTEN_minor__ > 38 || __EMSCRIPTEN_minor__ == 38 && __EMSCRIPTEN_tiny__ >= 22)
-# define EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
-#endif
-#endif
-
-#ifdef EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
-#include <emscripten/threading.h>
-#endif
-
-class QWasmEventDispatcherPrivate : public QEventDispatcherUNIXPrivate
-{
-
-};
-
-QWasmEventDispatcher *g_htmlEventDispatcher;
-
-QWasmEventDispatcher::QWasmEventDispatcher(QObject *parent)
- : QUnixEventDispatcherQPA(parent)
-{
-
- g_htmlEventDispatcher = this;
-}
-
-QWasmEventDispatcher::~QWasmEventDispatcher()
-{
- g_htmlEventDispatcher = nullptr;
-}
-
-bool QWasmEventDispatcher::registerRequestUpdateCallback(std::function<void(void)> callback)
-{
- if (!g_htmlEventDispatcher || !g_htmlEventDispatcher->m_hasMainLoop)
- return false;
-
- g_htmlEventDispatcher->m_requestUpdateCallbacks.append(callback);
- emscripten_resume_main_loop();
- return true;
-}
-
-void QWasmEventDispatcher::maintainTimers()
-{
- if (!g_htmlEventDispatcher || !g_htmlEventDispatcher->m_hasMainLoop)
- return;
-
- g_htmlEventDispatcher->doMaintainTimers();
-}
-
-bool QWasmEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
-{
- // WaitForMoreEvents is not supported (except for in combination with EventLoopExec below),
- // and we don't want the unix event dispatcher base class to attempt to wait either.
- flags &= ~QEventLoop::WaitForMoreEvents;
-
- // Handle normal processEvents.
- if (!(flags & QEventLoop::EventLoopExec))
- return QUnixEventDispatcherQPA::processEvents(flags);
-
- if (flags & QEventLoop::DialogExec) {
- qWarning() << "Warning: dialog exec() is not supported on Qt for WebAssembly, please use"
- << "show() instead. When using exec() the dialog will show, the user can interact"
- << "with it and the appropriate signals will be emitted on close. However, the"
- << "exec() call never returns, stack content at the time of the exec() call"
- << "is leaked, and the exec() call may interfere with input event processing";
-
- emscripten_sleep(1); // This call never returns
- }
-
- // Handle processEvents from QEventLoop::exec():
- //
- // At this point the application has created its root objects on
- // the stack and has called app.exec() which has called into this
- // function via QEventLoop.
- //
- // The application now expects that exec() will not return until
- // app exit time. However, the browser expects that we return
- // control to it periodically, also after initial setup in main().
-
- // EventLoopExec for nested event loops is not supported.
- Q_ASSERT(!m_hasMainLoop);
- m_hasMainLoop = true;
-
- // Call emscripten_set_main_loop_arg() with a callback which processes
- // events. Also set simulateInfiniteLoop to true which makes emscripten
- // return control to the browser without unwinding the C++ stack.
- auto callback = [](void *eventDispatcher) {
- QWasmEventDispatcher *that = static_cast<QWasmEventDispatcher *>(eventDispatcher);
-
- // Save and clear updateRequest callbacks so we can register new ones
- auto requestUpdateCallbacksCopy = that->m_requestUpdateCallbacks;
- that->m_requestUpdateCallbacks.clear();
-
- // Repaint all windows
- for (auto callback : qAsConst(requestUpdateCallbacksCopy))
- callback();
-
- // Pause main loop if no updates were requested. Updates will be
- // restarted again by registerRequestUpdateCallback().
- if (that->m_requestUpdateCallbacks.isEmpty())
- emscripten_pause_main_loop();
-
- that->doMaintainTimers();
- };
- int fps = 0; // update using requestAnimationFrame
- int simulateInfiniteLoop = 1;
- emscripten_set_main_loop_arg(callback, this, fps, simulateInfiniteLoop);
-
- // Note: the above call never returns, not even at app exit
- return false;
-}
-
-void QWasmEventDispatcher::doMaintainTimers()
-{
- Q_D(QWasmEventDispatcher);
-
- // This function schedules native timers in order to wake up to
- // process events and activate Qt timers. This is done using the
- // emscripten_async_call() API which schedules a new timer.
- // There is unfortunately no way to cancel or update a current
- // native timer.
-
- // Schedule a zero-timer to continue processing any pending events.
- extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
- if (!m_hasZeroTimer && (qGlobalPostedEventsCount() || QWindowSystemInterface::windowSystemEventsQueued())) {
- auto callback = [](void *eventDispatcher) {
- QWasmEventDispatcher *that = static_cast<QWasmEventDispatcher *>(eventDispatcher);
- that->m_hasZeroTimer = false;
- that->QUnixEventDispatcherQPA::processEvents(QEventLoop::AllEvents);
-
- // Processing events may have posted new events or created new timers
- that->doMaintainTimers();
- };
-
- emscripten_async_call(callback, this, 0);
- m_hasZeroTimer = true;
- return;
- }
-
- auto timespecToNanosec = [](timespec ts) -> uint64_t { return ts.tv_sec * 1000 + ts.tv_nsec / (1000 * 1000); };
-
- // Get current time and time-to-first-Qt-timer. This polls for system
- // time, and we use this time as the current time for the duration of this call.
- timespec toWait;
- bool hasTimers = d->timerList.timerWait(toWait);
- if (!hasTimers)
- return; // no timer needed
-
- uint64_t currentTime = timespecToNanosec(d->timerList.currentTime);
- uint64_t toWaitDuration = timespecToNanosec(toWait);
-
- // The currently scheduled timer target is stored in m_currentTargetTime.
- // We can re-use it if the new target is equivalent or later.
- uint64_t newTargetTime = currentTime + toWaitDuration;
- if (newTargetTime >= m_currentTargetTime)
- return; // existing timer is good
-
- // Schedule a native timer with a callback which processes events (and timers)
- auto callback = [](void *eventDispatcher) {
- QWasmEventDispatcher *that = static_cast<QWasmEventDispatcher *>(eventDispatcher);
- that->m_currentTargetTime = std::numeric_limits<uint64_t>::max();
- that->QUnixEventDispatcherQPA::processEvents(QEventLoop::AllEvents);
-
- // Processing events may have posted new events or created new timers
- that->doMaintainTimers();
- };
- emscripten_async_call(callback, this, toWaitDuration);
- m_currentTargetTime = newTargetTime;
-}
-
-void QWasmEventDispatcher::wakeUp()
-{
-#ifdef EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
- if (!emscripten_is_main_runtime_thread() && m_hasMainLoop) {
-
- // Make two-step async call to mainThreadWakeUp in order to make sure the
- // call is made at a point where the main thread is idle.
- void (*intermediate)(void *) = [](void *eventdispatcher){
- emscripten_async_call(QWasmEventDispatcher::mainThreadWakeUp, eventdispatcher, 0);
- };
- emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, (void *)intermediate, this);
- }
-#endif
- QEventDispatcherUNIX::wakeUp();
-}
-
-void QWasmEventDispatcher::mainThreadWakeUp(void *eventDispatcher)
+// Note: All event dispatcher functionality is implemented in QEventDispatcherWasm
+// in QtCore, except for processWindowSystemEvents() below which uses API from QtGui.
+void QWasmEventDispatcher::processWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
{
- emscripten_resume_main_loop(); // Service possible requestUpdate Calls
- static_cast<QWasmEventDispatcher *>(eventDispatcher)->processEvents(QEventLoop::AllEvents);
+ QWindowSystemInterface::sendWindowSystemEvents(flags);
}
diff --git a/src/plugins/platforms/wasm/qwasmeventdispatcher.h b/src/plugins/platforms/wasm/qwasmeventdispatcher.h
index 8420f2cf1a..0ce3e6fce1 100644
--- a/src/plugins/platforms/wasm/qwasmeventdispatcher.h
+++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.h
@@ -30,35 +30,14 @@
#ifndef QWASMEVENTDISPATCHER_H
#define QWASMEVENTDISPATCHER_H
-#include <QtCore/qhash.h>
-#include <QtCore/qloggingcategory.h>
-#include <QtGui/private/qunixeventdispatcher_qpa_p.h>
+#include <QtCore/private/qeventdispatcher_wasm_p.h>
QT_BEGIN_NAMESPACE
-class QWasmEventDispatcherPrivate;
-
-class QWasmEventDispatcher : public QUnixEventDispatcherQPA
+class QWasmEventDispatcher : public QEventDispatcherWasm
{
- Q_DECLARE_PRIVATE(QWasmEventDispatcher)
-public:
- explicit QWasmEventDispatcher(QObject *parent = nullptr);
- ~QWasmEventDispatcher();
-
- static bool registerRequestUpdateCallback(std::function<void(void)> callback);
- static void maintainTimers();
-
protected:
- bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
- void doMaintainTimers();
- void wakeUp() override;
- static void mainThreadWakeUp(void *eventDispatcher);
-
-private:
- bool m_hasMainLoop = false;
- bool m_hasZeroTimer = false;
- uint64_t m_currentTargetTime = std::numeric_limits<uint64_t>::max();
- QList<std::function<void(void)>> m_requestUpdateCallbacks;
+ void processWindowSystemEvents(QEventLoop::ProcessEventsFlags flags) override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
index 1344af3816..0285ace1e6 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
@@ -33,7 +33,7 @@
#include "qwasmintegration.h"
#include "qwasmclipboard.h"
#include "qwasmstring.h"
-
+#include "qwasmcursor.h"
#include <QtGui/qevent.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtCore/qcoreapplication.h>
@@ -177,101 +177,12 @@ static constexpr const auto DeadKeyShiftTbl = qMakeArray(
>::Data{}
);
-// macOS CTRL <-> META switching. We most likely want to enable
-// the existing switching code in QtGui, but for now do it here.
-static bool g_usePlatformMacSpecifics = false;
-
-bool g_useNaturalScrolling = true; // natural scrolling is default on linux/windows
-
-static void mouseWheelEvent(emscripten::val event) {
-
- emscripten::val wheelInterted = event["webkitDirectionInvertedFromDevice"];
-
- if (wheelInterted.as<bool>()) {
- g_useNaturalScrolling = true;
- }
-}
-
-EMSCRIPTEN_BINDINGS(qtMouseModule) {
- function("qtMouseWheelEvent", &mouseWheelEvent);
-}
-
-QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen)
- : QObject(screen)
- , draggedWindow(nullptr)
- , lastWindow(nullptr)
- , pressedButtons(Qt::NoButton)
- , resizeMode(QWasmWindow::ResizeNone)
+QWasmEventTranslator::QWasmEventTranslator() : QObject()
{
- touchDevice = new QPointingDevice("touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
- QPointingDevice::PointerType::Finger,
- QPointingDevice::Capability::Position | QPointingDevice::Capability::Area | QPointingDevice::Capability::NormalizedPosition,
- 10, 0);
- QWindowSystemInterface::registerInputDevice(touchDevice);
-
- initEventHandlers();
}
QWasmEventTranslator::~QWasmEventTranslator()
{
- // deregister event handlers
- QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8();
- emscripten_set_keydown_callback(canvasSelector.constData(), 0, 0, NULL);
- emscripten_set_keyup_callback(canvasSelector.constData(), 0, 0, NULL);
-
- emscripten_set_mousedown_callback(canvasSelector.constData(), 0, 0, NULL);
- emscripten_set_mouseup_callback(canvasSelector.constData(), 0, 0, NULL);
- emscripten_set_mousemove_callback(canvasSelector.constData(), 0, 0, NULL);
-
- emscripten_set_focus_callback(canvasSelector.constData(), 0, 0, NULL);
-
- emscripten_set_wheel_callback(canvasSelector.constData(), 0, 0, NULL);
-
- emscripten_set_touchstart_callback(canvasSelector.constData(), 0, 0, NULL);
- emscripten_set_touchend_callback(canvasSelector.constData(), 0, 0, NULL);
- emscripten_set_touchmove_callback(canvasSelector.constData(), 0, 0, NULL);
- emscripten_set_touchcancel_callback(canvasSelector.constData(), 0, 0, NULL);
-}
-
-void QWasmEventTranslator::initEventHandlers()
-{
- QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8();
-
- // 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")));
- g_usePlatformMacSpecifics = (platform == MacOSPlatform);
-
- if (platform == MacOSPlatform) {
- g_useNaturalScrolling = false; // make this !default on macOS
-
- if (!emscripten::val::global("window")["safari"].isUndefined()) {
- val canvas = screen()->canvas();
- canvas.call<void>("addEventListener",
- val("wheel"),
- val::module_property("qtMouseWheelEvent"));
- }
- }
-
- emscripten_set_keydown_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb);
- emscripten_set_keyup_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb);
-
- emscripten_set_mousedown_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb);
- emscripten_set_mouseup_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb);
- emscripten_set_mousemove_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb);
-
- emscripten_set_focus_callback(canvasSelector.constData(), (void *)this, 1, &focus_cb);
-
- emscripten_set_wheel_callback(canvasSelector.constData(), (void *)this, 1, &wheel_cb);
-
- emscripten_set_touchstart_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
- emscripten_set_touchend_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
- emscripten_set_touchmove_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
- emscripten_set_touchcancel_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback);
}
template <typename Event>
@@ -294,13 +205,15 @@ QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translatKeyModifier(const Eve
else
keyModifier |= Qt::MetaModifier;
}
+
return keyModifier;
}
-QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent)
+QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translateKeyboardEventModifier(const EmscriptenKeyboardEvent *event)
{
- QFlags<Qt::KeyboardModifier> keyModifier = translatKeyModifier(keyEvent);
- if (keyEvent->location == DOM_KEY_LOCATION_NUMPAD) {
+ QFlags<Qt::KeyboardModifier> keyModifier = translatKeyModifier(event);
+
+ if (event->location == DOM_KEY_LOCATION_NUMPAD) {
keyModifier |= Qt::KeypadModifier;
}
@@ -312,17 +225,9 @@ QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translateMouseEventModifier(c
return translatKeyModifier(mouseEvent);
}
-int QWasmEventTranslator::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
-{
- QWasmEventTranslator *wasmTranslator = reinterpret_cast<QWasmEventTranslator *>(userData);
- bool accepted = wasmTranslator->processKeyboard(eventType, keyEvent);
-
- return accepted ? 1 : 0;
-}
-
-QWasmScreen *QWasmEventTranslator::screen()
+QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translateTouchEventModifier(const EmscriptenTouchEvent *touchEvent)
{
- return static_cast<QWasmScreen *>(parent());
+ return translatKeyModifier(touchEvent);
}
Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey)
@@ -367,324 +272,6 @@ Qt::MouseButton QWasmEventTranslator::translateMouseButton(unsigned short button
return Qt::NoButton;
}
-int QWasmEventTranslator::mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
-{
- QWasmEventTranslator *translator = (QWasmEventTranslator*)userData;
- bool accepted = translator->processMouse(eventType,mouseEvent);
- QWasmEventDispatcher::maintainTimers();
- return accepted;
-}
-
-void resizeWindow(QWindow *window, QWasmWindow::ResizeMode mode,
- QRect startRect, QPoint amount)
-{
- if (mode == QWasmWindow::ResizeNone)
- return;
-
- bool top = mode == QWasmWindow::ResizeTopLeft ||
- mode == QWasmWindow::ResizeTop ||
- mode == QWasmWindow::ResizeTopRight;
-
- bool bottom = mode == QWasmWindow::ResizeBottomLeft ||
- mode == QWasmWindow::ResizeBottom ||
- mode == QWasmWindow::ResizeBottomRight;
-
- bool left = mode == QWasmWindow::ResizeLeft ||
- mode == QWasmWindow::ResizeTopLeft ||
- mode == QWasmWindow::ResizeBottomLeft;
-
- bool right = mode == QWasmWindow::ResizeRight ||
- mode == QWasmWindow::ResizeTopRight ||
- mode == QWasmWindow::ResizeBottomRight;
-
- int x1 = startRect.left();
- int y1 = startRect.top();
- int x2 = startRect.right();
- int y2 = startRect.bottom();
-
- if (left)
- x1 += amount.x();
- if (top)
- y1 += amount.y();
- if (right)
- x2 += amount.x();
- if (bottom)
- y2 += amount.y();
-
- int w = x2-x1;
- int h = y2-y1;
-
- if (w < window->minimumWidth()) {
- if (left)
- x1 -= window->minimumWidth() - w;
-
- w = window->minimumWidth();
- }
-
- if (h < window->minimumHeight()) {
- if (top)
- y1 -= window->minimumHeight() - h;
-
- h = window->minimumHeight();
- }
-
- window->setGeometry(x1, y1, w, h);
-}
-
-bool QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent)
-{
- QPoint targetPoint(mouseEvent->targetX, mouseEvent->targetY);
- QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
-
- QEvent::Type buttonEventType = QEvent::None;
- Qt::MouseButton button = translateMouseButton(mouseEvent->button);
- Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent);
-
- QWindow *window2 = nullptr;
- if (resizeMode == QWasmWindow::ResizeNone)
- window2 = screen()->compositor()->windowAt(globalPoint, 5);
-
- if (lastWindow && lastWindow->cursor() != Qt::ArrowCursor) {
- lastWindow->setCursor(Qt::ArrowCursor);
- }
- if (window2 == nullptr) {
- window2 = lastWindow;
- } else {
- lastWindow = window2;
- }
-
- QPoint localPoint = window2->mapFromGlobal(globalPoint);
- bool interior = window2->geometry().contains(globalPoint);
-
- QWasmWindow *htmlWindow = static_cast<QWasmWindow*>(window2->handle());
- switch (eventType) {
- case EMSCRIPTEN_EVENT_MOUSEDOWN:
- {
- if (window2)
- window2->requestActivate();
-
- pressedButtons.setFlag(button);
-
- // button overview:
- // 0 = primary mouse button, usually left click
- // 1 = middle mouse button, usually mouse wheel
- // 2 = right mouse button, usually right click
- // from: https://w3c.github.io/uievents/#dom-mouseevent-button
- if (mouseEvent->button == 0) {
- pressedWindow = window2;
- buttonEventType = QEvent::MouseButtonPress;
- if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) {
- if (htmlWindow && window2->flags().testFlag(Qt::WindowTitleHint) && htmlWindow->isPointOnTitle(globalPoint))
- draggedWindow = window2;
- else if (htmlWindow && htmlWindow->isPointOnResizeRegion(globalPoint)) {
- draggedWindow = window2;
- resizeMode = htmlWindow->resizeModeAtPoint(globalPoint);
- resizePoint = globalPoint;
- resizeStartRect = window2->geometry();
- }
- }
- }
-
- htmlWindow->injectMousePressed(localPoint, globalPoint, button, modifiers);
- break;
- }
- case EMSCRIPTEN_EVENT_MOUSEUP:
- {
- pressedButtons.setFlag(translateMouseButton(mouseEvent->button), false);
- buttonEventType = QEvent::MouseButtonRelease;
- QWasmWindow *oldWindow = nullptr;
-
- if (mouseEvent->button == 0 && pressedWindow) {
- oldWindow = static_cast<QWasmWindow*>(pressedWindow->handle());
- pressedWindow = nullptr;
- }
-
- if (mouseEvent->button == 0) {
- draggedWindow = nullptr;
- resizeMode = QWasmWindow::ResizeNone;
- }
-
- if (oldWindow)
- oldWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers);
- else
- htmlWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers);
- break;
- }
- case EMSCRIPTEN_EVENT_MOUSEMOVE: // drag event
- {
- buttonEventType = QEvent::MouseMove;
-
- if (htmlWindow && htmlWindow->isPointOnResizeRegion(globalPoint))
- window2->setCursor(cursorForMode(htmlWindow->resizeModeAtPoint(globalPoint)));
-
- if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) {
- if (resizeMode == QWasmWindow::ResizeNone && draggedWindow) {
- draggedWindow->setX(draggedWindow->x() + mouseEvent->movementX);
- draggedWindow->setY(draggedWindow->y() + mouseEvent->movementY);
- }
-
- if (resizeMode != QWasmWindow::ResizeNone && !(htmlWindow->m_windowState & Qt::WindowFullScreen)) {
- QPoint delta = QPoint(mouseEvent->targetX, mouseEvent->targetY) - resizePoint;
- resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta);
- }
- }
- break;
- }
- default: // MOUSELEAVE MOUSEENTER
- break;
- };
- if (!window2 && buttonEventType == QEvent::MouseButtonRelease) {
- window2 = lastWindow;
- lastWindow = nullptr;
- interior = true;
- }
- bool accepted = true;
- if (window2 && interior) {
- accepted = QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(
- window2, getTimestamp(), localPoint, globalPoint, pressedButtons, button, buttonEventType, modifiers);
- }
- return accepted;
-}
-
-int QWasmEventTranslator::focus_cb(int /*eventType*/, const EmscriptenFocusEvent */*focusEvent*/, void */*userData*/)
-{
- return 0;
-}
-
-int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
-{
- Q_UNUSED(eventType);
-
- QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
- EmscriptenMouseEvent mouseEvent = wheelEvent->mouse;
-
- int scrollFactor = 0;
- switch (wheelEvent->deltaMode) {
- case DOM_DELTA_PIXEL://chrome safari
- scrollFactor = 1;
- break;
- case DOM_DELTA_LINE: //firefox
- scrollFactor = 12;
- break;
- case DOM_DELTA_PAGE:
- scrollFactor = 20;
- break;
- };
-
- if (g_useNaturalScrolling) //macOS platform has document oriented scrolling
- scrollFactor = -scrollFactor;
-
- QWasmEventTranslator *translator = (QWasmEventTranslator*)userData;
- Qt::KeyboardModifiers modifiers = translator->translateMouseEventModifier(&mouseEvent);
- QPoint targetPoint(mouseEvent.targetX, mouseEvent.targetY);
- QPoint globalPoint = eventTranslator->screen()->geometry().topLeft() + targetPoint;
-
- QWindow *window2 = eventTranslator->screen()->compositor()->windowAt(globalPoint, 5);
- if (!window2)
- return 0;
- QPoint localPoint = window2->mapFromGlobal(globalPoint);
-
- QPoint pixelDelta;
-
- if (wheelEvent->deltaY != 0) pixelDelta.setY(wheelEvent->deltaY * scrollFactor);
- if (wheelEvent->deltaX != 0) pixelDelta.setX(wheelEvent->deltaX * scrollFactor);
-
- bool accepted = QWindowSystemInterface::handleWheelEvent(window2, getTimestamp(), localPoint,
- globalPoint, QPoint(), pixelDelta, modifiers);
- QWasmEventDispatcher::maintainTimers();
- return static_cast<int>(accepted);
-}
-
-int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
-{
- auto translator = reinterpret_cast<QWasmEventTranslator*>(userData);
- return translator->handleTouch(eventType, touchEvent);
-}
-
-int QWasmEventTranslator::handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent)
-{
- QList<QWindowSystemInterface::TouchPoint> touchPointList;
- touchPointList.reserve(touchEvent->numTouches);
- QWindow *window2;
-
- for (int i = 0; i < touchEvent->numTouches; i++) {
-
- const EmscriptenTouchPoint *touches = &touchEvent->touches[i];
-
- QPoint targetPoint(touches->targetX, touches->targetY);
- QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
-
- window2 = this->screen()->compositor()->windowAt(globalPoint, 5);
- if (window2 == nullptr)
- continue;
-
- QWindowSystemInterface::TouchPoint touchPoint;
-
- touchPoint.area = QRect(0, 0, 8, 8);
- touchPoint.id = touches->identifier;
- touchPoint.pressure = 1.0;
-
- touchPoint.area.moveCenter(globalPoint);
-
- const auto tp = pressedTouchIds.constFind(touchPoint.id);
- if (tp != pressedTouchIds.constEnd())
- touchPoint.normalPosition = tp.value();
-
- QPointF localPoint = QPointF(window2->mapFromGlobal(globalPoint));
- QPointF normalPosition(localPoint.x() / window2->width(),
- localPoint.y() / window2->height());
-
- const bool stationaryTouchPoint = (normalPosition == touchPoint.normalPosition);
- touchPoint.normalPosition = normalPosition;
-
- switch (eventType) {
- case EMSCRIPTEN_EVENT_TOUCHSTART:
- if (tp != pressedTouchIds.constEnd()) {
- touchPoint.state = (stationaryTouchPoint
- ? QEventPoint::State::Stationary
- : QEventPoint::State::Updated);
- } else {
- touchPoint.state = QEventPoint::State::Pressed;
- }
- pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
-
- break;
- case EMSCRIPTEN_EVENT_TOUCHEND:
- touchPoint.state = QEventPoint::State::Released;
- pressedTouchIds.remove(touchPoint.id);
- break;
- case EMSCRIPTEN_EVENT_TOUCHMOVE:
- touchPoint.state = (stationaryTouchPoint
- ? QEventPoint::State::Stationary
- : QEventPoint::State::Updated);
-
- pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
- break;
- default:
- break;
- }
-
- touchPointList.append(touchPoint);
- }
-
- QFlags<Qt::KeyboardModifier> keyModifier = translatKeyModifier(touchEvent);
-
- bool accepted = QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(
- window2, getTimestamp(), touchDevice, touchPointList, keyModifier);
-
- if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL)
- accepted = QWindowSystemInterface::handleTouchCancelEvent(window2, getTimestamp(), touchDevice, keyModifier);
-
- QWasmEventDispatcher::maintainTimers();
-
- return static_cast<int>(accepted);
-}
-
-quint64 QWasmEventTranslator::getTimestamp()
-{
- return emscripten_performance_now();
-}
-
struct KeyMapping { Qt::Key from, to; };
constexpr KeyMapping tildeKeyTable[] = { // ~
@@ -739,12 +326,12 @@ static Qt::Key find(const KeyMapping (&map)[N], Qt::Key key) noexcept
return find_impl(map, map + N, key);
}
-Qt::Key QWasmEventTranslator::translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey)
+Qt::Key QWasmEventTranslator::translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey, bool is_mac)
{
Qt::Key wasmKey = Qt::Key_unknown;
if (deadKey == Qt::Key_QuoteLeft ) {
- if (g_usePlatformMacSpecifics) { // ` macOS: Key_Dead_Grave
+ if (is_mac) { // ` macOS: Key_Dead_Grave
wasmKey = find(graveKeyTable, accentBaseKey);
} else {
wasmKey = find(diaeresisKeyTable, accentBaseKey);
@@ -779,123 +366,83 @@ Qt::Key QWasmEventTranslator::translateDeadKey(Qt::Key deadKey, Qt::Key accentBa
return wasmKey;
}
-bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent)
+QCursor QWasmEventTranslator::cursorForMode(QWasmCompositor::ResizeMode m)
+{
+ switch (m) {
+ case QWasmCompositor::ResizeTopLeft:
+ case QWasmCompositor::ResizeBottomRight:
+ return Qt::SizeFDiagCursor;
+ case QWasmCompositor::ResizeBottomLeft:
+ case QWasmCompositor::ResizeTopRight:
+ return Qt::SizeBDiagCursor;
+ case QWasmCompositor::ResizeTop:
+ case QWasmCompositor::ResizeBottom:
+ return Qt::SizeVerCursor;
+ case QWasmCompositor::ResizeLeft:
+ case QWasmCompositor::ResizeRight:
+ return Qt::SizeHorCursor;
+ case QWasmCompositor::ResizeNone:
+ return Qt::ArrowCursor;
+ }
+ return Qt::ArrowCursor;
+}
+
+QString QWasmEventTranslator::getKeyText(const EmscriptenKeyboardEvent *keyEvent)
{
+ QString keyText;
Qt::Key qtKey = translateEmscriptKey(keyEvent);
+ //Qt::KeyboardModifiers modifiers = translateKeyboardEventModifier(keyEvent);
- Qt::KeyboardModifiers modifiers = translateKeyboardEventModifier(keyEvent);
+ if (m_emDeadKey != Qt::Key_unknown) {
+ Qt::Key transformedKey = translateDeadKey(m_emDeadKey, qtKey);
- QString keyText;
- QEvent::Type keyType = QEvent::None;
- switch (eventType) {
- case EMSCRIPTEN_EVENT_KEYPRESS:
- case EMSCRIPTEN_EVENT_KEYDOWN: // down
- keyType = QEvent::KeyPress;
-
- if (m_emDeadKey != Qt::Key_unknown) {
-
- Qt::Key transformedKey = translateDeadKey(m_emDeadKey, qtKey);
-
- if (transformedKey != Qt::Key_unknown)
- qtKey = transformedKey;
-
- if (keyEvent->shiftKey == 0) {
- for (auto it = KeyTbl.cbegin(); it != KeyTbl.end(); ++it) {
- if (it != KeyTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
- keyText = it->em;
- m_emDeadKey = Qt::Key_unknown;
- break;
- }
+ if (transformedKey != Qt::Key_unknown)
+ qtKey = transformedKey;
+
+ if (keyEvent->shiftKey == 0) {
+ for (auto it = KeyTbl.cbegin(); it != KeyTbl.end(); ++it) {
+ if (it != KeyTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
+ keyText = it->em;
+ m_emDeadKey = Qt::Key_unknown;
+ break;
}
- } else {
- for (auto it = DeadKeyShiftTbl.cbegin(); it != DeadKeyShiftTbl.end(); ++it) {
- if (it != DeadKeyShiftTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
- keyText = it->em;
- m_emDeadKey = Qt::Key_unknown;
- break;
- }
+ }
+ } else {
+ for (auto it = DeadKeyShiftTbl.cbegin(); it != DeadKeyShiftTbl.end(); ++it) {
+ if (it != DeadKeyShiftTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
+ keyText = it->em;
+ m_emDeadKey = Qt::Key_unknown;
+ break;
}
}
}
- if (qstrncmp(keyEvent->key, "Dead", 4) == 0 || qtKey == Qt::Key_AltGr) {
- qtKey = translateEmscriptKey(keyEvent);
- m_emStickyDeadKey = true;
- if (keyEvent->shiftKey == 1 && qtKey == Qt::Key_QuoteLeft)
- qtKey = Qt::Key_AsciiTilde;
- m_emDeadKey = qtKey;
- }
- break;
- case EMSCRIPTEN_EVENT_KEYUP: // up
- keyType = QEvent::KeyRelease;
- if (m_emStickyDeadKey && qtKey != Qt::Key_Alt) {
- m_emStickyDeadKey = false;
- }
- break;
- default:
- break;
- };
-
- if (keyType == QEvent::None)
- return 0;
+ }
- QFlags<Qt::KeyboardModifier> mods = translateKeyboardEventModifier(keyEvent);
+ return keyText;
+}
- // 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;
- }
+Qt::Key QWasmEventTranslator::getKey(const EmscriptenKeyboardEvent *keyEvent)
+{
+ Qt::Key qtKey = translateEmscriptKey(keyEvent);
- bool accepted = false;
-
- if (keyType == QEvent::KeyPress &&
- mods.testFlag(Qt::ControlModifier)
- && qtKey == Qt::Key_V) {
- QWasmIntegration::get()->getWasmClipboard()->readTextFromClipboard();
- } else {
- if (keyText.isEmpty())
- keyText = QString(keyEvent->key);
- if (keyText.size() > 1)
- keyText.clear();
- accepted = QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
- 0, keyType, qtKey, modifiers, keyText);
- }
- if (keyType == QEvent::KeyPress &&
- mods.testFlag(Qt::ControlModifier)
- && qtKey == Qt::Key_C) {
- QWasmIntegration::get()->getWasmClipboard()->writeTextToClipboard();
+ if (qstrncmp(keyEvent->key, "Dead", 4) == 0 || qtKey == Qt::Key_AltGr) {
+ qtKey = translateEmscriptKey(keyEvent);
+ m_emStickyDeadKey = true;
+ if (keyEvent->shiftKey == 1 && qtKey == Qt::Key_QuoteLeft)
+ qtKey = Qt::Key_AsciiTilde;
+ m_emDeadKey = qtKey;
}
- QWasmEventDispatcher::maintainTimers();
-
- return accepted;
+ return qtKey;
}
-QCursor QWasmEventTranslator::cursorForMode(QWasmWindow::ResizeMode m)
+void QWasmEventTranslator::setStickyDeadKey(const EmscriptenKeyboardEvent *keyEvent)
{
- switch (m) {
- case QWasmWindow::ResizeTopLeft:
- case QWasmWindow::ResizeBottomRight:
- return Qt::SizeFDiagCursor;
- break;
- case QWasmWindow::ResizeBottomLeft:
- case QWasmWindow::ResizeTopRight:
- return Qt::SizeBDiagCursor;
- break;
- case QWasmWindow::ResizeTop:
- case QWasmWindow::ResizeBottom:
- return Qt::SizeVerCursor;
- break;
- case QWasmWindow::ResizeLeft:
- case QWasmWindow::ResizeRight:
- return Qt::SizeHorCursor;
- break;
- case QWasmWindow::ResizeNone:
- return Qt::ArrowCursor;
- break;
+ Qt::Key qtKey = translateEmscriptKey(keyEvent);
+
+ if (m_emStickyDeadKey && qtKey != Qt::Key_Alt) {
+ m_emStickyDeadKey = false;
}
- return Qt::ArrowCursor;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h
index 3e772583af..341971d79f 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.h
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h
@@ -49,57 +49,37 @@ class QWasmEventTranslator : public QObject
public:
- explicit QWasmEventTranslator(QWasmScreen *screen);
+ explicit QWasmEventTranslator();
~QWasmEventTranslator();
- static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData);
- static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
- static int focus_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
- static int wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData);
-
- static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);
-
- void processEvents();
- void initEventHandlers();
- int handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent);
-
-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);
+
+ static Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey);
QFlags<Qt::KeyboardModifier> translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent);
QFlags<Qt::KeyboardModifier> translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent);
- Qt::MouseButton translateMouseButton(unsigned short button);
+ QFlags<Qt::KeyboardModifier> translateTouchEventModifier(const EmscriptenTouchEvent *touchEvent);
+ static Qt::MouseButton translateMouseButton(unsigned short button);
+ static QCursor cursorForMode(QWasmCompositor::ResizeMode mode);
- bool processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent);
- bool processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent);
+ QString getKeyText(const EmscriptenKeyboardEvent *keyEvent);
+ Qt::Key getKey(const EmscriptenKeyboardEvent *keyEvent);
+ void setStickyDeadKey(const EmscriptenKeyboardEvent *keyEvent);
- Qt::Key translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey);
+ void setIsMac(bool is_mac) {g_usePlatformMacSpecifics = is_mac;};
- QMap <int, QPointF> pressedTouchIds;
+Q_SIGNALS:
+ void getWindowAt(const QPoint &point, QWindow **window);
+private:
+ bool g_usePlatformMacSpecifics = false;
+ static Qt::Key translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey, bool is_mac = false);
private:
- QWindow *draggedWindow;
- QWindow *pressedWindow;
- QWindow *lastWindow;
- Qt::MouseButtons pressedButtons;
-
- QWasmWindow::ResizeMode resizeMode;
- QPoint resizePoint;
- QRect resizeStartRect;
-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
- QPointingDevice *touchDevice;
-#else
- QTouchDevice *touchDevice;
-#endif
static quint64 getTimestamp();
Qt::Key m_emDeadKey = Qt::Key_unknown;
bool m_emStickyDeadKey = false;
- QCursor cursorForMode(QWasmWindow::ResizeMode mode);
+
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp
index 3dfd8cfe12..031128563e 100644
--- a/src/plugins/platforms/wasm/qwasmintegration.cpp
+++ b/src/plugins/platforms/wasm/qwasmintegration.cpp
@@ -328,4 +328,9 @@ void QWasmIntegration::resizeAllScreens()
canvasAndScreen.second->updateQScreenAndCanvasRenderSize();
}
+quint64 QWasmIntegration::getTimestamp()
+{
+ return emscripten_performance_now();
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h
index 16c48e1ea1..46fab8e818 100644
--- a/src/plugins/platforms/wasm/qwasmintegration.h
+++ b/src/plugins/platforms/wasm/qwasmintegration.h
@@ -89,6 +89,7 @@ public:
void resizeAllScreens();
void updateDpi();
void removeBackingStore(QWindow* window);
+ static quint64 getTimestamp();
private:
mutable QWasmFontDatabase *m_fontDb;
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
index 0b11415fd3..eb77dc65b9 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.cpp
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -55,7 +55,7 @@ const char * QWasmScreen::m_canvasResizeObserverCallbackContextPropertyName = "d
QWasmScreen::QWasmScreen(const emscripten::val &canvas)
: m_canvas(canvas)
, m_compositor(new QWasmCompositor(this))
- , m_eventTranslator(new QWasmEventTranslator(this))
+ , m_eventTranslator(new QWasmEventTranslator())
{
updateQScreenAndCanvasRenderSize();
m_canvas.call<void>("focus");
@@ -216,7 +216,7 @@ void QWasmScreen::updateQScreenAndCanvasRenderSize()
QPoint position(rect["left"].as<int>() - offset.x(), rect["top"].as<int>() - offset.y());
setGeometry(QRect(position, cssSize.toSize()));
- m_compositor->redrawWindowContent();
+ m_compositor->requestUpdateAllWindows();
}
void QWasmScreen::canvasResizeObserverCallback(emscripten::val entries, emscripten::val)
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index aac7413994..a25b5a262c 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -63,6 +63,8 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt
QWasmWindow::~QWasmWindow()
{
m_compositor->removeWindow(this);
+ if (m_requestAnimationFrameId > -1)
+ emscripten_cancel_animation_frame(m_requestAnimationFrameId);
}
void QWasmWindow::destroy()
@@ -109,8 +111,6 @@ void QWasmWindow::setGeometry(const QRect &rect)
}
QWindowSystemInterface::handleGeometryChange(window(), r);
QPlatformWindow::setGeometry(r);
-
- QWindowSystemInterface::flushWindowSystemEvents();
invalidate();
}
@@ -126,7 +126,6 @@ void QWasmWindow::setVisible(bool visible)
else if (m_windowState & Qt::WindowMaximized)
newGeom = platformScreen()->availableGeometry();
}
- QPlatformWindow::setVisible(visible);
m_compositor->setVisible(this, visible);
@@ -171,7 +170,12 @@ WId QWasmWindow::winId() const
void QWasmWindow::propagateSizeHints()
{
-// get rid of base class warning
+ QRect rect = windowGeometry();
+ if (rect.size().width() < windowMinimumSize().width()
+ && rect.size().height() < windowMinimumSize().height()) {
+ rect.setSize(windowMinimumSize());
+ setGeometry(rect);
+ }
}
void QWasmWindow::injectMousePressed(const QPoint &local, const QPoint &global,
@@ -270,7 +274,7 @@ bool QWasmWindow::isPointOnResizeRegion(QPoint point) const
return resizeRegion().contains(point);
}
-QWasmWindow::ResizeMode QWasmWindow::resizeModeAtPoint(QPoint point) const
+QWasmCompositor::ResizeMode QWasmWindow::resizeModeAtPoint(QPoint point) const
{
QPoint p1 = window()->frameGeometry().topLeft() - QPoint(5, 5);
QPoint p2 = window()->frameGeometry().bottomRight() + QPoint(5, 5);
@@ -287,28 +291,28 @@ QWasmWindow::ResizeMode QWasmWindow::resizeModeAtPoint(QPoint point) const
if (top.contains(point)) {
// Top
if (left.contains(point))
- return ResizeTopLeft;
+ return QWasmCompositor::ResizeTopLeft;
if (center.contains(point))
- return ResizeTop;
+ return QWasmCompositor::ResizeTop;
if (right.contains(point))
- return ResizeTopRight;
+ return QWasmCompositor::ResizeTopRight;
} else if (middle.contains(point)) {
// Middle
if (left.contains(point))
- return ResizeLeft;
+ return QWasmCompositor::ResizeLeft;
if (right.contains(point))
- return ResizeRight;
+ return QWasmCompositor::ResizeRight;
} else if (bottom.contains(point)) {
// Bottom
if (left.contains(point))
- return ResizeBottomLeft;
+ return QWasmCompositor::ResizeBottomLeft;
if (center.contains(point))
- return ResizeBottom;
+ return QWasmCompositor::ResizeBottom;
if (right.contains(point))
- return ResizeBottomRight;
+ return QWasmCompositor::ResizeBottomRight;
}
- return ResizeNone;
+ return QWasmCompositor::ResizeNone;
}
QRect getSubControlRect(const QWasmWindow *window, QWasmCompositor::SubControls subControl)
@@ -359,7 +363,7 @@ QRegion QWasmWindow::titleControlRegion() const
void QWasmWindow::invalidate()
{
- m_compositor->requestRedraw();
+ m_compositor->requestUpdateWindow(this);
}
QWasmCompositor::SubControls QWasmWindow::activeSubControl() const
@@ -390,34 +394,43 @@ qreal QWasmWindow::devicePixelRatio() const
void QWasmWindow::requestUpdate()
{
- QPointer<QWindow> windowPointer(window());
- bool registered = QWasmEventDispatcher::registerRequestUpdateCallback([=](){
- if (windowPointer.isNull())
- return;
-
- deliverUpdateRequest();
- });
+ if (m_compositor) {
+ m_compositor->requestUpdateWindow(this, QWasmCompositor::UpdateRequestDelivery);
+ return;
+ }
- if (!registered)
- QPlatformWindow::requestUpdate();
+ static auto frame = [](double time, void *context) -> int {
+ Q_UNUSED(time);
+ QWasmWindow *window = static_cast<QWasmWindow *>(context);
+ window->m_requestAnimationFrameId = -1;
+ window->deliverUpdateRequest();
+ return 0;
+ };
+ m_requestAnimationFrameId = emscripten_request_animation_frame(frame, this);
}
bool QWasmWindow::hasTitleBar() const
{
- auto flags = window()->flags();
+ Qt::WindowFlags flags = window()->flags();
return !(m_windowState & Qt::WindowFullScreen)
&& flags.testFlag(Qt::WindowTitleHint)
- && !flags.testFlag(Qt::ToolTip)
+ && !(windowIsPopupType(flags))
&& m_needsCompositor;
}
-bool QWasmWindow::windowIsPopupType(Qt::WindowType type) const
+bool QWasmWindow::windowIsPopupType(Qt::WindowFlags flags) const
{
- if (type == Qt::Widget)
- type = window()->type();
- if (type != Qt::Tool)
- return true;
- return false;
+ if (flags.testFlag(Qt::Tool))
+ return false; // Qt::Tool has the Popup bit set but isn't
+
+ return (flags.testFlag(Qt::Popup));
+}
+
+void QWasmWindow::requestActivateWindow()
+{
+ if (window()->isTopLevel())
+ raise();
+ QPlatformWindow::requestActivateWindow();
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index 24b56a28fc..686a26430e 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -39,23 +39,9 @@
QT_BEGIN_NAMESPACE
-class QWasmCompositor;
-
class QWasmWindow : public QPlatformWindow
{
public:
- enum ResizeMode {
- ResizeNone,
- ResizeTopLeft,
- ResizeTop,
- ResizeTopRight,
- ResizeRight,
- ResizeBottomRight,
- ResizeBottom,
- ResizeBottomLeft,
- ResizeLeft
- };
-
QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore);
~QWasmWindow();
void destroy();
@@ -74,6 +60,7 @@ public:
QRect normalGeometry() const override;
qreal devicePixelRatio() const override;
void requestUpdate() override;
+ void requestActivateWindow() override;
QWasmScreen *platformScreen() const;
void setBackingStore(QWasmBackingStore *store) { m_backingStore = store; }
@@ -91,7 +78,7 @@ public:
QRegion resizeRegion() const;
bool isPointOnTitle(QPoint point) const;
bool isPointOnResizeRegion(QPoint point) const;
- ResizeMode resizeModeAtPoint(QPoint point) const;
+ QWasmCompositor::ResizeMode resizeModeAtPoint(QPoint point) const;
QRect maxButtonRect() const;
QRect minButtonRect() const;
QRect closeButtonRect() const;
@@ -121,9 +108,10 @@ protected:
WId m_winid = 0;
bool m_hasTitle = false;
bool m_needsCompositor = false;
+ long m_requestAnimationFrameId = -1;
friend class QWasmCompositor;
friend class QWasmEventTranslator;
- bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const;
+ bool windowIsPopupType(Qt::WindowFlags flags) const;
};
QT_END_NAMESPACE
#endif // QWASMWINDOW_H
diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt
index 880fa79ef5..d71d7e547f 100644
--- a/src/plugins/platforms/windows/CMakeLists.txt
+++ b/src/plugins/platforms/windows/CMakeLists.txt
@@ -59,6 +59,10 @@ qt_internal_add_plugin(QWindowsIntegrationPlugin
winmm
winspool
wtsapi32
+ shcore
+ comdlg32
+ d3d9
+ runtimeobject
)
# Resources:
diff --git a/src/plugins/platforms/windows/cursors.qrc b/src/plugins/platforms/windows/cursors.qrc
deleted file mode 100644
index fded527aac..0000000000
--- a/src/plugins/platforms/windows/cursors.qrc
+++ /dev/null
@@ -1,25 +0,0 @@
-<RCC>
- <qresource prefix="/qt-project.org/windows/cursors">
- <file>images/closedhandcursor_32.png</file>
- <file>images/closedhandcursor_48.png</file>
- <file>images/closedhandcursor_64.png</file>
- <file>images/dragcopycursor_32.png</file>
- <file>images/dragcopycursor_48.png</file>
- <file>images/dragcopycursor_64.png</file>
- <file>images/draglinkcursor_32.png</file>
- <file>images/draglinkcursor_48.png</file>
- <file>images/draglinkcursor_64.png</file>
- <file>images/dragmovecursor_32.png</file>
- <file>images/dragmovecursor_48.png</file>
- <file>images/dragmovecursor_64.png</file>
- <file>images/openhandcursor_32.png</file>
- <file>images/openhandcursor_48.png</file>
- <file>images/openhandcursor_64.png</file>
- <file>images/splithcursor_32.png</file>
- <file>images/splithcursor_48.png</file>
- <file>images/splithcursor_64.png</file>
- <file>images/splitvcursor_32.png</file>
- <file>images/splitvcursor_48.png</file>
- <file>images/splitvcursor_64.png</file>
- </qresource>
-</RCC>
diff --git a/src/plugins/platforms/windows/openglblacklists.qrc b/src/plugins/platforms/windows/openglblacklists.qrc
deleted file mode 100644
index 9f0c186c21..0000000000
--- a/src/plugins/platforms/windows/openglblacklists.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<RCC>
- <qresource prefix="/qt-project.org/windows/openglblacklists">
- <file alias="default.json">openglblacklists/default.json</file>
- </qresource>
-</RCC>
diff --git a/src/plugins/platforms/windows/qwin10helpers.cpp b/src/plugins/platforms/windows/qwin10helpers.cpp
index 9a7fce9cd5..589311a902 100644
--- a/src/plugins/platforms/windows/qwin10helpers.cpp
+++ b/src/plugins/platforms/windows/qwin10helpers.cpp
@@ -40,13 +40,13 @@
#include "qwin10helpers.h"
#include <QtCore/qdebug.h>
-#include <QtCore/qoperatingsystemversion.h>
-#include <QtCore/private/qsystemlibrary_p.h>
+#include <winstring.h>
+#include <roapi.h>
#if defined(Q_CC_MINGW) || defined(Q_CC_CLANG)
# define HAS_UI_VIEW_SETTINGS_INTEROP
// Present from MSVC2015 + SDK 10 onwards
-#elif (!defined(Q_CC_MSVC) || _MSC_VER >= 1900) && NTDDI_VERSION >= 0xa000000
+#elif (!defined(Q_CC_MSVC) || _MSC_VER >= 1900) && WINVER >= 0x0A00
# define HAS_UI_VIEW_SETTINGS_INTEROP
# define HAS_UI_VIEW_SETTINGS
#endif
@@ -96,56 +96,23 @@ public:
QT_BEGIN_NAMESPACE
-// Starting from Windows 10
-struct QWindowsComBaseDLL
-{
- bool init();
- bool isValid() const
- {
- return roGetActivationFactory != nullptr && windowsCreateStringReference != nullptr;
- }
-
- typedef HRESULT (WINAPI *RoGetActivationFactory)(HSTRING, REFIID, void **);
- typedef HRESULT (WINAPI *WindowsCreateStringReference)(PCWSTR, UINT32, HSTRING_HEADER *, HSTRING *);
-
- RoGetActivationFactory roGetActivationFactory = nullptr;
- WindowsCreateStringReference windowsCreateStringReference = nullptr;
-};
-
-static QWindowsComBaseDLL baseComDll;
-
-bool QWindowsComBaseDLL::init()
-{
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 && !isValid()) {
- QSystemLibrary library(QStringLiteral("combase"));
- roGetActivationFactory =
- reinterpret_cast<RoGetActivationFactory>(library.resolve("RoGetActivationFactory"));
- windowsCreateStringReference =
- reinterpret_cast<WindowsCreateStringReference>(library.resolve("WindowsCreateStringReference"));
- }
- return isValid();
-}
-
// Return tablet mode, note: Does not work for GetDesktopWindow().
bool qt_windowsIsTabletMode(HWND hwnd)
{
bool result = false;
- if (!baseComDll.init())
- return false;
-
const wchar_t uiViewSettingsId[] = L"Windows.UI.ViewManagement.UIViewSettings";
HSTRING_HEADER uiViewSettingsIdRefHeader;
HSTRING uiViewSettingsIdHs = nullptr;
const auto uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1);
- if (FAILED(baseComDll.windowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs)))
+ if (FAILED(WindowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs)))
return false;
IUIViewSettingsInterop *uiViewSettingsInterop = nullptr;
// __uuidof(IUIViewSettingsInterop);
const GUID uiViewSettingsInteropRefId = {0x3694dbf9, 0x8f68, 0x44be,{0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6}};
- HRESULT hr = baseComDll.roGetActivationFactory(uiViewSettingsIdHs, uiViewSettingsInteropRefId,
+ HRESULT hr = RoGetActivationFactory(uiViewSettingsIdHs, uiViewSettingsInteropRefId,
reinterpret_cast<void **>(&uiViewSettingsInterop));
if (FAILED(hr))
return false;
diff --git a/src/plugins/platforms/windows/qwindowsclipboard.cpp b/src/plugins/platforms/windows/qwindowsclipboard.cpp
index 01377a55e0..73111a13fb 100644
--- a/src/plugins/platforms/windows/qwindowsclipboard.cpp
+++ b/src/plugins/platforms/windows/qwindowsclipboard.cpp
@@ -191,12 +191,9 @@ void QWindowsClipboard::registerViewer()
createDummyWindow(QStringLiteral("ClipboardView"), L"QtClipboardView",
qClipboardViewerWndProc, WS_OVERLAPPED);
- // Try format listener API (Vista onwards) first.
- if (QWindowsContext::user32dll.addClipboardFormatListener && QWindowsContext::user32dll.removeClipboardFormatListener) {
- m_formatListenerRegistered = QWindowsContext::user32dll.addClipboardFormatListener(m_clipboardViewer);
- if (!m_formatListenerRegistered)
- qErrnoWarning("AddClipboardFormatListener() failed.");
- }
+ m_formatListenerRegistered = AddClipboardFormatListener(m_clipboardViewer);
+ if (!m_formatListenerRegistered)
+ qErrnoWarning("AddClipboardFormatListener() failed.");
if (!m_formatListenerRegistered)
m_nextClipboardViewer = SetClipboardViewer(m_clipboardViewer);
@@ -210,7 +207,7 @@ void QWindowsClipboard::unregisterViewer()
{
if (m_clipboardViewer) {
if (m_formatListenerRegistered) {
- QWindowsContext::user32dll.removeClipboardFormatListener(m_clipboardViewer);
+ RemoveClipboardFormatListener(m_clipboardViewer);
m_formatListenerRegistered = false;
} else {
ChangeClipboardChain(m_clipboardViewer, m_nextClipboardViewer);
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 1c75f7f7d2..fe5b017b27 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -77,11 +77,9 @@
#include <QtCore/qlibraryinfo.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qdebug.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/quuid.h>
-#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/private/qwinregistry_p.h>
#include <QtGui/private/qwindowsguieventdispatcher_p.h>
@@ -92,6 +90,7 @@
#include <comdef.h>
#include <dbt.h>
#include <wtsapi32.h>
+#include <shellscalingapi.h>
QT_BEGIN_NAMESPACE
@@ -153,17 +152,15 @@ static inline bool sessionManagerInteractionBlocked() { return false; }
static inline int windowDpiAwareness(HWND hwnd)
{
- return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext
- ? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext(QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd))
- : -1;
+ return static_cast<int>(GetAwarenessFromDpiAwarenessContext(GetWindowDpiAwarenessContext(hwnd)));
}
// Note: This only works within WM_NCCREATE
static bool enableNonClientDpiScaling(HWND hwnd)
{
bool result = false;
- if (QWindowsContext::user32dll.enableNonClientDpiScaling && windowDpiAwareness(hwnd) == 2) {
- result = QWindowsContext::user32dll.enableNonClientDpiScaling(hwnd) != FALSE;
+ if (windowDpiAwareness(hwnd) == 2) {
+ result = EnableNonClientDpiScaling(hwnd) != FALSE;
if (!result) {
const DWORD errorCode = GetLastError();
qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)",
@@ -173,73 +170,6 @@ static bool enableNonClientDpiScaling(HWND hwnd)
return result;
}
-/*!
- \class QWindowsUser32DLL
- \brief Struct that contains dynamically resolved symbols of User32.dll.
-
- The stub libraries shipped with the MinGW compiler miss some of the
- functions. They need to be retrieved dynamically.
-
- In addition, touch-related functions are available only from Windows onwards.
- These need to resolved dynamically for Q_CC_MSVC as well.
-
- \sa QWindowsShell32DLL
-
- \internal
-*/
-
-void QWindowsUser32DLL::init()
-{
- QSystemLibrary library(QStringLiteral("user32"));
- setProcessDPIAware = (SetProcessDPIAware)library.resolve("SetProcessDPIAware");
- setProcessDpiAwarenessContext = (SetProcessDpiAwarenessContext)library.resolve("SetProcessDpiAwarenessContext");
-
- addClipboardFormatListener = (AddClipboardFormatListener)library.resolve("AddClipboardFormatListener");
- removeClipboardFormatListener = (RemoveClipboardFormatListener)library.resolve("RemoveClipboardFormatListener");
-
- getDisplayAutoRotationPreferences = (GetDisplayAutoRotationPreferences)library.resolve("GetDisplayAutoRotationPreferences");
- setDisplayAutoRotationPreferences = (SetDisplayAutoRotationPreferences)library.resolve("SetDisplayAutoRotationPreferences");
-
- enableMouseInPointer = (EnableMouseInPointer)library.resolve("EnableMouseInPointer");
- getPointerType = (GetPointerType)library.resolve("GetPointerType");
- getPointerInfo = (GetPointerInfo)library.resolve("GetPointerInfo");
- getPointerDeviceRects = (GetPointerDeviceRects)library.resolve("GetPointerDeviceRects");
- getPointerTouchInfo = (GetPointerTouchInfo)library.resolve("GetPointerTouchInfo");
- getPointerFrameTouchInfo = (GetPointerFrameTouchInfo)library.resolve("GetPointerFrameTouchInfo");
- getPointerFrameTouchInfoHistory = (GetPointerFrameTouchInfoHistory)library.resolve("GetPointerFrameTouchInfoHistory");
- getPointerPenInfo = (GetPointerPenInfo)library.resolve("GetPointerPenInfo");
- getPointerPenInfoHistory = (GetPointerPenInfoHistory)library.resolve("GetPointerPenInfoHistory");
- skipPointerFrameMessages = (SkipPointerFrameMessages)library.resolve("SkipPointerFrameMessages");
-
- if (QOperatingSystemVersion::current()
- >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) {
- adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)library.resolve("AdjustWindowRectExForDpi");
- enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling");
- getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext");
- getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext");
- systemParametersInfoForDpi = (SystemParametersInfoForDpi)library.resolve("SystemParametersInfoForDpi");
- getDpiForWindow = (GetDpiForWindow)library.resolve("GetDpiForWindow");
- }
-}
-
-bool QWindowsUser32DLL::supportsPointerApi()
-{
- return enableMouseInPointer && getPointerType && getPointerInfo && getPointerDeviceRects
- && getPointerTouchInfo && getPointerFrameTouchInfo && getPointerFrameTouchInfoHistory
- && getPointerPenInfo && getPointerPenInfoHistory && skipPointerFrameMessages;
-}
-
-void QWindowsShcoreDLL::init()
-{
- QSystemLibrary library(QStringLiteral("SHCore"));
- getProcessDpiAwareness = (GetProcessDpiAwareness)library.resolve("GetProcessDpiAwareness");
- setProcessDpiAwareness = (SetProcessDpiAwareness)library.resolve("SetProcessDpiAwareness");
- getDpiForMonitor = (GetDpiForMonitor)library.resolve("GetDpiForMonitor");
-}
-
-QWindowsUser32DLL QWindowsContext::user32dll;
-QWindowsShcoreDLL QWindowsContext::shcoredll;
-
QWindowsContext *QWindowsContext::m_instance = nullptr;
/*!
@@ -251,14 +181,12 @@ QWindowsContext *QWindowsContext::m_instance = nullptr;
\internal
*/
-typedef QHash<HWND, QWindowsWindow *> HandleBaseWindowHash;
-
struct QWindowsContextPrivate {
QWindowsContextPrivate();
unsigned m_systemInfo = 0;
QSet<QString> m_registeredWindowClassNames;
- HandleBaseWindowHash m_windows;
+ QWindowsContext::HandleBaseWindowHash m_windows;
HDC m_displayContext = nullptr;
int m_defaultDPI = 96;
QWindowsKeyMapper m_keyMapper;
@@ -285,9 +213,6 @@ bool QWindowsContextPrivate::m_v2DpiAware = false;
QWindowsContextPrivate::QWindowsContextPrivate()
: m_oleInitializeResult(OleInitialize(nullptr))
{
- QWindowsContext::user32dll.init();
- QWindowsContext::shcoredll.init();
-
if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice())
m_systemInfo |= QWindowsContext::SI_SupportsTouch;
m_displayContext = GetDC(nullptr);
@@ -402,9 +327,6 @@ bool QWindowsContext::initPointer(unsigned integrationOptions)
if (integrationOptions & QWindowsIntegration::DontUseWMPointer)
return false;
- if (!QWindowsContext::user32dll.supportsPointerApi())
- return false;
-
d->m_systemInfo |= QWindowsContext::SI_SupportsPointer;
return true;
}
@@ -474,10 +396,9 @@ void QWindowsContext::setDetectAltGrModifier(bool a)
int QWindowsContext::processDpiAwareness()
{
- int result;
- if (QWindowsContext::shcoredll.getProcessDpiAwareness
- && SUCCEEDED(QWindowsContext::shcoredll.getProcessDpiAwareness(nullptr, &result))) {
- return result;
+ PROCESS_DPI_AWARENESS result;
+ if (SUCCEEDED(GetProcessDpiAwareness(nullptr, &result))) {
+ return static_cast<int>(result);
}
return -1;
}
@@ -485,27 +406,20 @@ int QWindowsContext::processDpiAwareness()
void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness)
{
qCDebug(lcQpaWindows) << __FUNCTION__ << dpiAwareness;
- if (QWindowsContext::shcoredll.isValid()) {
- const HRESULT hr = QWindowsContext::shcoredll.setProcessDpiAwareness(dpiAwareness);
- // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin).
- // Silence warning in that case unless debug is enabled.
- if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled())) {
- qWarning().noquote().nospace() << "SetProcessDpiAwareness("
- << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr)
- << ", using " << QWindowsContext::processDpiAwareness();
- }
- } else {
- if (dpiAwareness != QtWindows::ProcessDpiUnaware && QWindowsContext::user32dll.setProcessDPIAware) {
- if (!QWindowsContext::user32dll.setProcessDPIAware())
- qErrnoWarning("SetProcessDPIAware() failed");
- }
+ const HRESULT hr = SetProcessDpiAwareness(static_cast<PROCESS_DPI_AWARENESS>(dpiAwareness));
+ // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin).
+ // Silence warning in that case unless debug is enabled.
+ if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled())) {
+ qWarning().noquote().nospace() << "SetProcessDpiAwareness("
+ << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr)
+ << ", using " << QWindowsContext::processDpiAwareness();
}
}
void QWindowsContext::setProcessDpiV2Awareness()
{
qCDebug(lcQpaWindows) << __FUNCTION__;
- const BOOL ok = QWindowsContext::user32dll.setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+ const BOOL ok = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
if (ok) {
QWindowsContextPrivate::m_v2DpiAware = true;
} else {
@@ -544,6 +458,11 @@ QList<int> QWindowsContext::possibleKeys(const QKeyEvent *e) const
return d->m_keyMapper.possibleKeys(e);
}
+QWindowsContext::HandleBaseWindowHash &QWindowsContext::windows()
+{
+ return d->m_windows;
+}
+
QSharedPointer<QWindowCreationContext> QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx)
{
const QSharedPointer<QWindowCreationContext> old = d->m_creationContext;
@@ -1046,8 +965,8 @@ void QWindowsContext::forceNcCalcSize(HWND hwnd)
bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out,
unsigned dpi)
{
- const BOOL result = QWindowsContext::user32dll.systemParametersInfoForDpi != nullptr && dpi != 0
- ? QWindowsContext::user32dll.systemParametersInfoForDpi(action, param, out, 0, dpi)
+ const BOOL result = dpi != 0
+ ? SystemParametersInfoForDpi(action, param, out, 0, dpi)
: SystemParametersInfo(action, param, out, 0);
return result == TRUE;
}
@@ -1094,8 +1013,7 @@ bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window)
if (QWindowsContextPrivate::m_v2DpiAware)
return true;
- return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
- && window->isTopLevel()
+ return window->isTopLevel()
&& !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid()
#if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL
&& (window->surfaceType() != QSurface::OpenGLSurface
@@ -1348,9 +1266,9 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
case QtWindows::NonClientHitTest:
return platformWindow->handleNonClientHitTest(QPoint(msg.pt.x, msg.pt.y), result);
case QtWindows::GeometryChangingEvent:
- return platformWindow->QWindowsWindow::handleGeometryChanging(&msg);
+ return platformWindow->handleGeometryChanging(&msg);
case QtWindows::ExposeEvent:
- return platformWindow->handleWmPaint(hwnd, message, wParam, lParam);
+ return platformWindow->handleWmPaint(hwnd, message, wParam, lParam, result);
case QtWindows::NonClientMouseEvent:
if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled())
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
@@ -1542,7 +1460,7 @@ void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et,
}
if (nextActiveWindow != d->m_lastActiveWindow) {
d->m_lastActiveWindow = nextActiveWindow;
- QWindowSystemInterface::handleWindowActivated(nextActiveWindow);
+ QWindowSystemInterface::handleWindowActivated(nextActiveWindow, Qt::ActiveWindowFocusReason);
}
}
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 04328a0369..2b0bb47405 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -82,89 +82,11 @@ class QPoint;
class QKeyEvent;
class QPointingDevice;
-struct QWindowsUser32DLL
-{
- inline void init();
- inline bool supportsPointerApi();
-
- typedef BOOL (WINAPI *EnableMouseInPointer)(BOOL);
- typedef BOOL (WINAPI *GetPointerType)(UINT32, PVOID);
- typedef BOOL (WINAPI *GetPointerInfo)(UINT32, PVOID);
- typedef BOOL (WINAPI *GetPointerDeviceRects)(HANDLE, RECT *, RECT *);
- typedef BOOL (WINAPI *GetPointerTouchInfo)(UINT32, PVOID);
- typedef BOOL (WINAPI *GetPointerFrameTouchInfo)(UINT32, UINT32 *, PVOID);
- typedef BOOL (WINAPI *GetPointerFrameTouchInfoHistory)(UINT32, UINT32 *, UINT32 *, PVOID);
- typedef BOOL (WINAPI *GetPointerPenInfo)(UINT32, PVOID);
- typedef BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32, UINT32 *, PVOID);
- typedef BOOL (WINAPI *SkipPointerFrameMessages)(UINT32);
- typedef BOOL (WINAPI *SetProcessDPIAware)();
- typedef BOOL (WINAPI *SetProcessDpiAwarenessContext)(HANDLE);
- typedef BOOL (WINAPI *AddClipboardFormatListener)(HWND);
- typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND);
- typedef BOOL (WINAPI *GetDisplayAutoRotationPreferences)(DWORD *);
- typedef BOOL (WINAPI *SetDisplayAutoRotationPreferences)(DWORD);
- typedef BOOL (WINAPI *AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT);
- typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND);
- typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND);
- typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int);
- typedef BOOL (WINAPI *SystemParametersInfoForDpi)(UINT, UINT, PVOID, UINT, UINT);
- typedef int (WINAPI *GetDpiForWindow)(HWND);
-
- // Windows pointer functions (Windows 8 or later).
- EnableMouseInPointer enableMouseInPointer = nullptr;
- GetPointerType getPointerType = nullptr;
- GetPointerInfo getPointerInfo = nullptr;
- GetPointerDeviceRects getPointerDeviceRects = nullptr;
- GetPointerTouchInfo getPointerTouchInfo = nullptr;
- GetPointerFrameTouchInfo getPointerFrameTouchInfo = nullptr;
- GetPointerFrameTouchInfoHistory getPointerFrameTouchInfoHistory = nullptr;
- GetPointerPenInfo getPointerPenInfo = nullptr;
- GetPointerPenInfoHistory getPointerPenInfoHistory = nullptr;
- SkipPointerFrameMessages skipPointerFrameMessages = nullptr;
-
- // Windows Vista onwards
- SetProcessDPIAware setProcessDPIAware = nullptr;
-
- // Windows 10 version 1607 onwards
- GetDpiForWindow getDpiForWindow = nullptr;
-
- // Windows 10 version 1703 onwards
- SetProcessDpiAwarenessContext setProcessDpiAwarenessContext = nullptr;
-
- // Clipboard listeners are present on Windows Vista onwards
- // but missing in MinGW 4.9 stub libs. Can be removed in MinGW 5.
- AddClipboardFormatListener addClipboardFormatListener = nullptr;
- RemoveClipboardFormatListener removeClipboardFormatListener = nullptr;
-
- // Rotation API
- GetDisplayAutoRotationPreferences getDisplayAutoRotationPreferences = nullptr;
- SetDisplayAutoRotationPreferences setDisplayAutoRotationPreferences = nullptr;
-
- AdjustWindowRectExForDpi adjustWindowRectExForDpi = nullptr;
- EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr;
- GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr;
- GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr;
- SystemParametersInfoForDpi systemParametersInfoForDpi = nullptr;
-};
-
-// Shell scaling library (Windows 8.1 onwards)
-struct QWindowsShcoreDLL {
- void init();
- inline bool isValid() const { return getProcessDpiAwareness && setProcessDpiAwareness && getDpiForMonitor; }
-
- typedef HRESULT (WINAPI *GetProcessDpiAwareness)(HANDLE,int *);
- typedef HRESULT (WINAPI *SetProcessDpiAwareness)(int);
- typedef HRESULT (WINAPI *GetDpiForMonitor)(HMONITOR,int,UINT *,UINT *);
-
- GetProcessDpiAwareness getProcessDpiAwareness = nullptr;
- SetProcessDpiAwareness setProcessDpiAwareness = nullptr;
- GetDpiForMonitor getDpiForMonitor = nullptr;
-};
-
class QWindowsContext
{
Q_DISABLE_COPY_MOVE(QWindowsContext)
public:
+ using HandleBaseWindowHash = QHash<HWND, QWindowsWindow *>;
enum SystemInfoFlags
{
@@ -247,15 +169,14 @@ public:
bool useRTLExtensions() const;
QList<int> possibleKeys(const QKeyEvent *e) const;
+ HandleBaseWindowHash &windows();
+
static bool isSessionLocked();
QWindowsMimeConverter &mimeConverter() const;
QWindowsScreenManager &screenManager();
QWindowsTabletSupport *tabletSupport() const;
- static QWindowsUser32DLL user32dll;
- static QWindowsShcoreDLL shcoredll;
-
static QByteArray comErrorString(HRESULT hr);
bool asyncExpose() const;
void setAsyncExpose(bool value);
diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp
index fe8b6f7085..ce92ce5662 100644
--- a/src/plugins/platforms/windows/qwindowscursor.cpp
+++ b/src/plugins/platforms/windows/qwindowscursor.cpp
@@ -652,6 +652,11 @@ void QWindowsCursor::clearOverrideCursor()
SetCursor(m_overriddenCursor);
m_overriddenCursor = m_overrideCursor = nullptr;
}
+ auto &windows = QWindowsContext::instance()->windows();
+ for (auto it = windows.cbegin(), end = windows.cend(); it != end; ++it) {
+ if (it.value()->screen() == m_screen)
+ it.value()->setFlag(QWindowsWindow::RestoreOverrideCursor);
+ }
}
QPoint QWindowsCursor::mousePosition()
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
index 1ddde93f90..e8399bd3d2 100644
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
@@ -40,7 +40,7 @@
#define QT_NO_URL_CAST_FROM_STRING 1
#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0601
+#define _WIN32_WINNT 0x0A00
#endif
#include "qwindowscombase.h"
@@ -70,7 +70,6 @@
#include <QtCore/qmutex.h>
#include <QtCore/quuid.h>
#include <QtCore/qtemporaryfile.h>
-#include <QtCore/private/qsystemlibrary_p.h>
#include <algorithm>
#include <vector>
@@ -1707,9 +1706,6 @@ public slots:
void close() override {}
private:
- typedef BOOL (APIENTRY *PtrGetOpenFileNameW)(LPOPENFILENAMEW);
- typedef BOOL (APIENTRY *PtrGetSaveFileNameW)(LPOPENFILENAMEW);
-
explicit QWindowsXpNativeFileDialog(const OptionsPtr &options, const QWindowsFileDialogSharedData &data);
void populateOpenFileName(OPENFILENAME *ofn, HWND owner) const;
QList<QUrl> execExistingDir(HWND owner);
@@ -1719,27 +1715,11 @@ private:
QString m_title;
QPlatformDialogHelper::DialogCode m_result;
QWindowsFileDialogSharedData m_data;
-
- static PtrGetOpenFileNameW m_getOpenFileNameW;
- static PtrGetSaveFileNameW m_getSaveFileNameW;
};
-QWindowsXpNativeFileDialog::PtrGetOpenFileNameW QWindowsXpNativeFileDialog::m_getOpenFileNameW = nullptr;
-QWindowsXpNativeFileDialog::PtrGetSaveFileNameW QWindowsXpNativeFileDialog::m_getSaveFileNameW = nullptr;
-
QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data)
{
- // GetOpenFileNameW() GetSaveFileName() are resolved
- // dynamically as not to create a dependency on Comdlg32, which
- // is used on XP only.
- if (!m_getOpenFileNameW) {
- QSystemLibrary library(QStringLiteral("Comdlg32"));
- m_getOpenFileNameW = (PtrGetOpenFileNameW)(library.resolve("GetOpenFileNameW"));
- m_getSaveFileNameW = (PtrGetSaveFileNameW)(library.resolve("GetSaveFileNameW"));
- }
- if (m_getOpenFileNameW && m_getSaveFileNameW)
- return new QWindowsXpNativeFileDialog(options, data);
- return nullptr;
+ return new QWindowsXpNativeFileDialog(options, data);
}
QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options,
@@ -1903,7 +1883,7 @@ QList<QUrl> QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedF
populateOpenFileName(&ofn, owner);
QList<QUrl> result;
const bool isSave = m_options->acceptMode() == QFileDialogOptions::AcceptSave;
- if (isSave ? m_getSaveFileNameW(&ofn) : m_getOpenFileNameW(&ofn)) {
+ if (isSave ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn)) {
*selectedFilterIndex = ofn.nFilterIndex - 1;
const QString dir = QDir::cleanPath(QString::fromWCharArray(ofn.lpstrFile));
result.push_back(QUrl::fromLocalFile(dir));
@@ -2045,8 +2025,6 @@ QWindowsNativeColorDialog::QWindowsNativeColorDialog(const SharedPointerColor &c
void QWindowsNativeColorDialog::doExec(HWND owner)
{
- typedef BOOL (WINAPI *ChooseColorWType)(LPCHOOSECOLORW);
-
CHOOSECOLOR chooseColor;
ZeroMemory(&chooseColor, sizeof(chooseColor));
chooseColor.lStructSize = sizeof(chooseColor);
@@ -2059,18 +2037,9 @@ void QWindowsNativeColorDialog::doExec(HWND owner)
m_customColors[c] = qColorToCOLORREF(QColor(qCustomColors[c]));
chooseColor.rgbResult = qColorToCOLORREF(*m_color);
chooseColor.Flags = CC_FULLOPEN | CC_RGBINIT;
- static ChooseColorWType chooseColorW = 0;
- if (!chooseColorW) {
- QSystemLibrary library(QStringLiteral("Comdlg32"));
- chooseColorW = (ChooseColorWType)library.resolve("ChooseColorW");
- }
- if (chooseColorW) {
- m_code = chooseColorW(&chooseColor) ?
- QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
- QWindowsDialogs::eatMouseMove();
- } else {
- m_code = QPlatformDialogHelper::Rejected;
- }
+ m_code = ChooseColorW(&chooseColor) ?
+ QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
+ QWindowsDialogs::eatMouseMove();
if (m_code == QPlatformDialogHelper::Accepted) {
*m_color = COLORREFToQColor(chooseColor.rgbResult);
for (int c= 0; c < customColorCount; ++c)
diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
index 03be0b9451..4cda4e1efe 100644
--- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
@@ -47,7 +47,6 @@
#include <QtCore/qobject.h>
#include <QtCore/qrect.h>
#include <QtCore/qtextboundaryfinder.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <QtGui/qevent.h>
#include <QtGui/qtextformat.h>
@@ -285,9 +284,7 @@ void QWindowsInputContext::showInputPanel()
// Only trigger the native OSK if the Qt OSK is not in use.
static bool imModuleEmpty = qEnvironmentVariableIsEmpty("QT_IM_MODULE");
bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard);
- if ((imModuleEmpty && !nativeVKDisabled)
- && QOperatingSystemVersion::current()
- >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 16299)) {
+ if (imModuleEmpty && !nativeVKDisabled) {
ShowCaret(platformWindow->handle());
} else {
HideCaret(platformWindow->handle());
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index 822c11d0f2..397b988e61 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -122,14 +122,6 @@ QT_BEGIN_NAMESPACE
MinGW-w64 provides more complete headers (compared to stock MinGW from mingw.org),
including a considerable part of the Windows SDK.
\endlist
-
- When using a function from the WinAPI, the minimum supported Windows version
- and Windows Embedded support should be checked. If the function is not supported
- on Windows XP or is not present in the MinGW-headers, it should be dynamically
- resolved. For this purpose, QWindowsContext has static structs like
- QWindowsUser32DLL and QWindowsShell32DLL. All function pointers should go to
- these structs to avoid lookups in several places.
-
*/
struct QWindowsIntegrationPrivate
@@ -245,10 +237,8 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr
initOpenGlBlacklistResources();
static bool dpiAwarenessSet = false;
- static bool hasDpiAwarenessContext = QWindowsContext::user32dll.setProcessDpiAwarenessContext != nullptr;
// Default to per-monitor-v2 awareness (if available)
- QtWindows::ProcessDpiAwareness dpiAwareness = hasDpiAwarenessContext ?
- QtWindows::ProcessPerMonitorV2DpiAware : QtWindows::ProcessPerMonitorDpiAware;
+ QtWindows::ProcessDpiAwareness dpiAwareness = QtWindows::ProcessPerMonitorV2DpiAware;
int tabletAbsoluteRange = -1;
DarkModeHandling darkModeHandling;
@@ -267,7 +257,7 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr
if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) {
// DpiAwareV2 requires using new API
- if (dpiAwareness == QtWindows::ProcessPerMonitorV2DpiAware && hasDpiAwarenessContext) {
+ if (dpiAwareness == QtWindows::ProcessPerMonitorV2DpiAware) {
m_context.setProcessDpiV2Awareness();
qCDebug(lcQpaWindows)
<< __FUNCTION__ << "DpiAwareness: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2";
diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp
index 3321845411..9c669c8fbc 100644
--- a/src/plugins/platforms/windows/qwindowsmime.cpp
+++ b/src/plugins/platforms/windows/qwindowsmime.cpp
@@ -670,7 +670,7 @@ bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeDat
auto *f = reinterpret_cast<wchar_t *>(files);
for (int i=0; i<fileNames.size(); i++) {
const auto l = size_t(fileNames.at(i).length());
- memcpy(f, fileNames.at(i).utf16(), l * sizeof(ushort));
+ memcpy(f, fileNames.at(i).data(), l * sizeof(ushort));
f += l;
*f++ = 0;
}
@@ -682,8 +682,8 @@ bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeDat
const auto urls = mimeData->urls();
QByteArray result;
if (!urls.isEmpty()) {
- QString url = urls.at(0).toString();
- result = QByteArray(reinterpret_cast<const char *>(url.utf16()),
+ const QString url = urls.at(0).toString();
+ result = QByteArray(reinterpret_cast<const char *>(url.data()),
url.length() * int(sizeof(ushort)));
}
result.append('\0');
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index 9fc04da245..b052dc53e8 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -130,7 +130,9 @@ QWindowsMouseHandler::QWindowsMouseHandler() = default;
const QPointingDevice *QWindowsMouseHandler::primaryMouse()
{
- static const auto result = QPointingDevice::primaryPointingDevice();
+ static QPointer<const QPointingDevice> result;
+ if (!result)
+ result = QPointingDevice::primaryPointingDevice();
return result;
}
diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp
index 0b1af47a65..59bdb38fad 100644
--- a/src/plugins/platforms/windows/qwindowsopengltester.cpp
+++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp
@@ -55,7 +55,6 @@
#endif
#include <QtCore/qt_windows.h>
-#include <private/qsystemlibrary_p.h>
#include <d3d9.h>
QT_BEGIN_NAMESPACE
@@ -94,19 +93,12 @@ public:
bool retrieveAdapterIdentifier(UINT n, D3DADAPTER_IDENTIFIER9 *adapterIdentifier) const;
private:
- QSystemLibrary m_d3d9lib;
IDirect3D9 *m_direct3D9 = nullptr;
};
-QDirect3D9Handle::QDirect3D9Handle() :
- m_d3d9lib(QStringLiteral("d3d9"))
+QDirect3D9Handle::QDirect3D9Handle()
{
- using PtrDirect3DCreate9 = IDirect3D9 *(WINAPI *)(UINT);
-
- if (m_d3d9lib.load()) {
- if (auto direct3DCreate9 = (PtrDirect3DCreate9)m_d3d9lib.resolve("Direct3DCreate9"))
- m_direct3D9 = direct3DCreate9(D3D_SDK_VERSION);
- }
+ m_direct3D9 = Direct3DCreate9(D3D_SDK_VERSION);
}
QDirect3D9Handle::~QDirect3D9Handle()
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index 46fbfcc8d6..dc74e28a12 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -37,11 +37,8 @@
**
****************************************************************************/
-#if defined(WINVER) && WINVER < 0x0603
-# undef WINVER
-#endif
-#if !defined(WINVER)
-# define WINVER 0x0603 // Enable pointer functions for MinGW
+#ifndef WINVER
+# define WINVER 0x0A00 // Enable pointer functions for MinGW
#endif
#include "qwindowspointerhandler.h"
@@ -62,7 +59,6 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qloggingcategory.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/qqueue.h>
#include <algorithm>
@@ -90,7 +86,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
*result = 0;
const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
- if (!QWindowsContext::user32dll.getPointerType(pointerId, &m_pointerType)) {
+ if (!GetPointerType(pointerId, &m_pointerType)) {
qWarning() << "GetPointerType() failed:" << qt_error_string();
return false;
}
@@ -104,12 +100,12 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
}
case QT_PT_TOUCH: {
quint32 pointerCount = 0;
- if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) {
+ if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) {
qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string();
return false;
}
QVarLengthArray<POINTER_TOUCH_INFO, 10> touchInfo(pointerCount);
- if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) {
+ if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) {
qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string();
return false;
}
@@ -122,10 +118,10 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
// dispatch any skipped frames if event compression is disabled by the app
if (historyCount > 1 && !QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) {
touchInfo.resize(pointerCount * historyCount);
- if (!QWindowsContext::user32dll.getPointerFrameTouchInfoHistory(pointerId,
- &historyCount,
- &pointerCount,
- touchInfo.data())) {
+ if (!GetPointerFrameTouchInfoHistory(pointerId,
+ &historyCount,
+ &pointerCount,
+ touchInfo.data())) {
qWarning() << "GetPointerFrameTouchInfoHistory() failed:" << qt_error_string();
return false;
}
@@ -143,7 +139,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
}
case QT_PT_PEN: {
POINTER_PEN_INFO penInfo;
- if (!QWindowsContext::user32dll.getPointerPenInfo(pointerId, &penInfo)) {
+ if (!GetPointerPenInfo(pointerId, &penInfo)) {
qWarning() << "GetPointerPenInfo() failed:" << qt_error_string();
return false;
}
@@ -155,9 +151,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
|| !QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents))) {
QVarLengthArray<POINTER_PEN_INFO, 10> penInfoHistory(historyCount);
- if (!QWindowsContext::user32dll.getPointerPenInfoHistory(pointerId,
- &historyCount,
- penInfoHistory.data())) {
+ if (!GetPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) {
qWarning() << "GetPointerPenInfoHistory() failed:" << qt_error_string();
return false;
}
@@ -463,6 +457,8 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
{
Q_UNUSED(hwnd);
+ auto *touchInfo = static_cast<POINTER_TOUCH_INFO *>(vTouchInfo);
+
if (et & QtWindows::NonClientEventFlag)
return false; // Let DefWindowProc() handle Non Client messages.
@@ -472,10 +468,19 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (msg.message == WM_POINTERCAPTURECHANGED) {
QWindowSystemInterface::handleTouchCancelEvent(window, m_touchDevice.data(),
QWindowsKeyMapper::queryKeyboardModifiers());
- m_lastTouchPositions.clear();
+ m_lastTouchPoints.clear();
return true;
}
+ if (msg.message == WM_POINTERLEAVE) {
+ for (quint32 i = 0; i < count; ++i) {
+ const quint32 pointerId = touchInfo[i].pointerInfo.pointerId;
+ int id = m_touchInputIDToTouchPointID.value(pointerId, -1);
+ if (id != -1)
+ m_lastTouchPoints.remove(id);
+ }
+ }
+
// Only handle down/up/update, ignore others like WM_POINTERENTER, WM_POINTERLEAVE, etc.
if (msg.message > WM_POINTERUP)
return false;
@@ -486,8 +491,6 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (!screen)
return false;
- auto *touchInfo = static_cast<POINTER_TOUCH_INFO *>(vTouchInfo);
-
const QRect screenGeometry = screen->geometry();
QList<QWindowSystemInterface::TouchPoint> touchPoints;
@@ -499,6 +502,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
<< " count=" << Qt::dec << count;
QEventPoint::States allStates;
+ QSet<int> inputIds;
for (quint32 i = 0; i < count; ++i) {
if (QWindowsContext::verbose > 1)
@@ -511,14 +515,17 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
const quint32 pointerId = touchInfo[i].pointerInfo.pointerId;
int id = m_touchInputIDToTouchPointID.value(pointerId, -1);
if (id == -1) {
+ // Start tracking after fingers touch the screen. Ignore bogus updates after touch is released.
+ if ((touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) == 0)
+ continue;
id = m_touchInputIDToTouchPointID.size();
m_touchInputIDToTouchPointID.insert(pointerId, id);
}
touchPoint.id = id;
touchPoint.pressure = (touchInfo[i].touchMask & TOUCH_MASK_PRESSURE) ?
touchInfo[i].pressure / 1024.0 : 1.0;
- if (m_lastTouchPositions.contains(touchPoint.id))
- touchPoint.normalPosition = m_lastTouchPositions.value(touchPoint.id);
+ if (m_lastTouchPoints.contains(touchPoint.id))
+ touchPoint.normalPosition = m_lastTouchPoints.value(touchPoint.id).normalPosition;
const QPointF screenPos = QPointF(touchInfo[i].pointerInfo.ptPixelLocation.x,
touchInfo[i].pointerInfo.ptPixelLocation.y);
@@ -534,22 +541,36 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) {
touchPoint.state = QEventPoint::State::Pressed;
- m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
+ m_lastTouchPoints.insert(touchPoint.id, touchPoint);
} else if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_UP) {
touchPoint.state = QEventPoint::State::Released;
- m_lastTouchPositions.remove(touchPoint.id);
+ m_lastTouchPoints.remove(touchPoint.id);
} else {
touchPoint.state = stationaryTouchPoint ? QEventPoint::State::Stationary : QEventPoint::State::Updated;
- m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
+ m_lastTouchPoints.insert(touchPoint.id, touchPoint);
}
allStates |= touchPoint.state;
touchPoints.append(touchPoint);
+ inputIds.insert(touchPoint.id);
// Avoid getting repeated messages for this frame if there are multiple pointerIds
- QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId);
+ SkipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId);
+ }
+
+ // Some devices send touches for each finger in a different message/frame, instead of consolidating
+ // them in the same frame as we were expecting. We account for missing unreleased touches here.
+ for (auto tp : qAsConst(m_lastTouchPoints)) {
+ if (!inputIds.contains(tp.id)) {
+ tp.state = QEventPoint::State::Stationary;
+ allStates |= tp.state;
+ touchPoints.append(tp);
+ }
}
+ if (touchPoints.count() == 0)
+ return false;
+
// all touch points released, forget the ids we've seen.
if (allStates == QEventPoint::State::Released)
m_touchInputIDToTouchPointID.clear();
@@ -580,7 +601,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
auto *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo);
RECT pRect, dRect;
- if (!QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect))
+ if (!GetPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect))
return false;
const auto systemId = (qint64)penInfo->pointerInfo.sourceDevice;
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.h b/src/plugins/platforms/windows/qwindowspointerhandler.h
index 5b6179b7fa..376902c530 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.h
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.h
@@ -48,6 +48,7 @@
#include <QtCore/qsharedpointer.h>
#include <QtCore/qhash.h>
#include <QtGui/qevent.h>
+#include <qpa/qwindowsysteminterface.h>
QT_BEGIN_NAMESPACE
@@ -88,7 +89,7 @@ private:
QList<QPointingDevicePtr> m_tabletDevices;
QPointingDevicePtr m_activeTabletDevice;
#endif
- QHash<int, QPointF> m_lastTouchPositions;
+ QHash<int, QWindowSystemInterface::TouchPoint> m_lastTouchPoints;
QHash<DWORD, int> m_touchInputIDToTouchPointID;
QPointer<QWindow> m_windowUnderPointer;
QPointer<QWindow> m_currentWindow;
diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp
index 2544dd6200..0c639eb710 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.cpp
+++ b/src/plugins/platforms/windows/qwindowsscreen.cpp
@@ -56,6 +56,8 @@
#include <QtCore/qdebug.h>
+#include <shellscalingapi.h>
+
QT_BEGIN_NAMESPACE
static inline QDpi deviceDPI(HDC hdc)
@@ -65,12 +67,10 @@ static inline QDpi deviceDPI(HDC hdc)
static inline QDpi monitorDPI(HMONITOR hMonitor)
{
- if (QWindowsContext::shcoredll.isValid()) {
- UINT dpiX;
- UINT dpiY;
- if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY)))
- return QDpi(dpiX, dpiY);
- }
+ UINT dpiX;
+ UINT dpiY;
+ if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
+ return QDpi(dpiX, dpiY);
return {0, 0};
}
@@ -151,8 +151,20 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
data->hMonitor = hMonitor;
data->geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
data->availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1));
- data->name = QString::fromWCharArray(info.szDevice);
- if (data->name == u"WinDisc") {
+ DISPLAYCONFIG_PATH_INFO pathInfo = {};
+ const bool hasPathInfo = getPathInfo(info, &pathInfo);
+ if (hasPathInfo) {
+ DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
+ deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
+ deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
+ deviceName.header.adapterId = pathInfo.targetInfo.adapterId;
+ deviceName.header.id = pathInfo.targetInfo.id;
+ if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS)
+ data->name = QString::fromWCharArray(deviceName.monitorFriendlyDeviceName);
+ }
+ if (data->name.isEmpty())
+ data->name = QString::fromWCharArray(info.szDevice);
+ if (wcscmp(info.szDevice, L"WinDisc") == 0) {
data->flags |= QWindowsScreenData::LockScreen;
} else {
if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) {
@@ -174,8 +186,7 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
// ### We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO,
// if we are going to use DISPLAYCONFIG lookups more.
- DISPLAYCONFIG_PATH_INFO pathInfo = {};
- if (getPathInfo(info, &pathInfo)) {
+ if (hasPathInfo) {
switch (pathInfo.targetInfo.rotation) {
case DISPLAYCONFIG_ROTATION_IDENTITY:
data->orientation = Qt::LandscapeOrientation;
@@ -429,62 +440,50 @@ QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScre
return result;
}
-enum OrientationPreference : DWORD // matching Win32 API ORIENTATION_PREFERENCE
-{
- orientationPreferenceNone = 0,
- orientationPreferenceLandscape = 0x1,
- orientationPreferencePortrait = 0x2,
- orientationPreferenceLandscapeFlipped = 0x4,
- orientationPreferencePortraitFlipped = 0x8
-};
-
bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o)
{
bool result = false;
- if (QWindowsContext::user32dll.setDisplayAutoRotationPreferences) {
- DWORD orientationPreference = 0;
- switch (o) {
- case Qt::PrimaryOrientation:
- orientationPreference = orientationPreferenceNone;
- break;
- case Qt::PortraitOrientation:
- orientationPreference = orientationPreferencePortrait;
- break;
- case Qt::LandscapeOrientation:
- orientationPreference = orientationPreferenceLandscape;
- break;
- case Qt::InvertedPortraitOrientation:
- orientationPreference = orientationPreferencePortraitFlipped;
- break;
- case Qt::InvertedLandscapeOrientation:
- orientationPreference = orientationPreferenceLandscapeFlipped;
- break;
- }
- result = QWindowsContext::user32dll.setDisplayAutoRotationPreferences(orientationPreference);
+ ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
+ switch (o) {
+ case Qt::PrimaryOrientation:
+ break;
+ case Qt::PortraitOrientation:
+ orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT;
+ break;
+ case Qt::LandscapeOrientation:
+ orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE;
+ break;
+ case Qt::InvertedPortraitOrientation:
+ orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED;
+ break;
+ case Qt::InvertedLandscapeOrientation:
+ orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
+ break;
}
+ result = SetDisplayAutoRotationPreferences(orientationPreference);
return result;
}
Qt::ScreenOrientation QWindowsScreen::orientationPreference()
{
Qt::ScreenOrientation result = Qt::PrimaryOrientation;
- if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences) {
- DWORD orientationPreference = 0;
- if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences(&orientationPreference)) {
- switch (orientationPreference) {
- case orientationPreferenceLandscape:
- result = Qt::LandscapeOrientation;
- break;
- case orientationPreferencePortrait:
- result = Qt::PortraitOrientation;
- break;
- case orientationPreferenceLandscapeFlipped:
- result = Qt::InvertedLandscapeOrientation;
- break;
- case orientationPreferencePortraitFlipped:
- result = Qt::InvertedPortraitOrientation;
- break;
- }
+ ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
+ if (GetDisplayAutoRotationPreferences(&orientationPreference)) {
+ switch (orientationPreference) {
+ case ORIENTATION_PREFERENCE_NONE:
+ break;
+ case ORIENTATION_PREFERENCE_LANDSCAPE:
+ result = Qt::LandscapeOrientation;
+ break;
+ case ORIENTATION_PREFERENCE_PORTRAIT:
+ result = Qt::PortraitOrientation;
+ break;
+ case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED:
+ result = Qt::InvertedLandscapeOrientation;
+ break;
+ case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED:
+ result = Qt::InvertedPortraitOrientation;
+ break;
}
}
return result;
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
index 7c473e66b6..0a96d5a0f5 100644
--- a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
@@ -37,18 +37,12 @@
**
****************************************************************************/
-#if defined(WINVER) && WINVER < 0x0601
-# undef WINVER
-#endif
-#if !defined(WINVER)
-# define WINVER 0x0601 // required for NOTIFYICONDATA_V2_SIZE, ChangeWindowMessageFilterEx() (MinGW 5.3)
+#ifndef WINVER
+# define WINVER 0x0A00 // required for NOTIFYICONDATA_V2_SIZE, ChangeWindowMessageFilterEx() (MinGW 5.3)
#endif
-#if defined(NTDDI_VERSION) && NTDDI_VERSION < 0x06010000
-# undef NTDDI_VERSION
-#endif
-#if !defined(NTDDI_VERSION)
-# define NTDDI_VERSION 0x06010000 // required for Shell_NotifyIconGetRect (MinGW 5.3)
+#ifndef NTDDI_VERSION
+# define NTDDI_VERSION 0x0A00000B // required for Shell_NotifyIconGetRect (MinGW 5.3)
#endif
#include "qwindowssystemtrayicon.h"
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index 6fbf4183da..9ee6bbd79a 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -38,9 +38,8 @@
****************************************************************************/
// SHSTOCKICONINFO is only available since Vista
-#if _WIN32_WINNT < 0x0601
-# undef _WIN32_WINNT
-# define _WIN32_WINNT 0x0601
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0A00
#endif
#include "qwindowstheme.h"
@@ -64,7 +63,6 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
#include <QtCore/qtextstream.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qcache.h>
#include <QtCore/qthread.h>
@@ -1054,9 +1052,7 @@ bool QWindowsTheme::useNativeMenus()
bool QWindowsTheme::queryDarkMode()
{
- if (QOperatingSystemVersion::current()
- < QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 17763)
- || queryHighContrast()) {
+ if (queryHighContrast()) {
return false;
}
const auto setting = QWinRegistryKey(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)")
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 6e75b9aa1b..3efba0149c 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -37,11 +37,8 @@
**
****************************************************************************/
-#if defined(WINVER) && WINVER < 0x0601
-# undef WINVER
-#endif
-#if !defined(WINVER)
-# define WINVER 0x0601 // Enable touch functions for MinGW
+#ifndef WINVER
+# define WINVER 0x0A00 // Enable touch functions for MinGW
#endif
#include "qwindowswindow.h"
@@ -68,7 +65,6 @@
#include <QtGui/qwindow.h>
#include <QtGui/qregion.h>
#include <QtGui/qopenglcontext.h>
-#include <private/qsystemlibrary_p.h>
#include <private/qwindow_p.h> // QWINDOWSIZE_MAX
#include <private/qguiapplication_p.h>
#include <private/qhighdpiscaling_p.h>
@@ -76,7 +72,6 @@
#include <QtCore/qdebug.h>
#include <QtCore/qlibraryinfo.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <dwmapi.h>
@@ -84,6 +79,8 @@
#include "qwindowsvulkaninstance.h"
#endif
+#include <shellscalingapi.h>
+
QT_BEGIN_NAMESPACE
using QWindowCreationContextPtr = QSharedPointer<QWindowCreationContext>;
@@ -119,6 +116,34 @@ static QByteArray debugWinStyle(DWORD style)
rc += " WS_MINIMIZEBOX";
if (style & WS_MAXIMIZEBOX)
rc += " WS_MAXIMIZEBOX";
+ if (style & WS_BORDER)
+ rc += " WS_BORDER";
+ if (style & WS_CAPTION)
+ rc += " WS_CAPTION";
+ if (style & WS_CHILDWINDOW)
+ rc += " WS_CHILDWINDOW";
+ if (style & WS_DISABLED)
+ rc += " WS_DISABLED";
+ if (style & WS_GROUP)
+ rc += " WS_GROUP";
+ if (style & WS_HSCROLL)
+ rc += " WS_HSCROLL";
+ if (style & WS_ICONIC)
+ rc += " WS_ICONIC";
+ if (style & WS_MAXIMIZE)
+ rc += " WS_MAXIMIZE";
+ if (style & WS_MINIMIZE)
+ rc += " WS_MINIMIZE";
+ if (style & WS_SIZEBOX)
+ rc += " WS_SIZEBOX";
+ if (style & WS_TABSTOP)
+ rc += " WS_TABSTOP";
+ if (style & WS_TILED)
+ rc += " WS_TILED";
+ if (style & WS_VISIBLE)
+ rc += " WS_VISIBLE";
+ if (style & WS_VSCROLL)
+ rc += " WS_VSCROLL";
return rc;
}
@@ -138,6 +163,44 @@ static QByteArray debugWinExStyle(DWORD exStyle)
rc += " WS_EX_LAYOUTRTL";
if (exStyle & WS_EX_NOINHERITLAYOUT)
rc += " WS_EX_NOINHERITLAYOUT";
+ if (exStyle & WS_EX_ACCEPTFILES)
+ rc += " WS_EX_ACCEPTFILES";
+ if (exStyle & WS_EX_APPWINDOW)
+ rc += " WS_EX_APPWINDOW";
+ if (exStyle & WS_EX_CLIENTEDGE)
+ rc += " WS_EX_CLIENTEDGE";
+ if (exStyle & WS_EX_COMPOSITED)
+ rc += " WS_EX_COMPOSITED";
+ if (exStyle & WS_EX_CONTROLPARENT)
+ rc += " WS_EX_CONTROLPARENT";
+ if (exStyle & WS_EX_LEFT)
+ rc += " WS_EX_LEFT";
+ if (exStyle & WS_EX_LEFTSCROLLBAR)
+ rc += " WS_EX_LEFTSCROLLBAR";
+ if (exStyle & WS_EX_LTRREADING)
+ rc += " WS_EX_LTRREADING";
+ if (exStyle & WS_EX_MDICHILD)
+ rc += " WS_EX_MDICHILD";
+ if (exStyle & WS_EX_NOACTIVATE)
+ rc += " WS_EX_NOACTIVATE";
+ if (exStyle & WS_EX_NOPARENTNOTIFY)
+ rc += " WS_EX_NOPARENTNOTIFY";
+ if (exStyle & WS_EX_NOREDIRECTIONBITMAP)
+ rc += " WS_EX_NOREDIRECTIONBITMAP";
+ if (exStyle & WS_EX_RIGHT)
+ rc += " WS_EX_RIGHT";
+ if (exStyle & WS_EX_RIGHTSCROLLBAR)
+ rc += " WS_EX_RIGHTSCROLLBAR";
+ if (exStyle & WS_EX_RTLREADING)
+ rc += " WS_EX_RTLREADING";
+ if (exStyle & WS_EX_STATICEDGE)
+ rc += " WS_EX_STATICEDGE";
+ if (exStyle & WS_EX_TOPMOST)
+ rc += " WS_EX_TOPMOST";
+ if (exStyle & WS_EX_TRANSPARENT)
+ rc += " WS_EX_TRANSPARENT";
+ if (exStyle & WS_EX_WINDOWEDGE)
+ rc += " WS_EX_WINDOWEDGE";
return rc;
}
@@ -167,6 +230,14 @@ static QByteArray debugWinSwpPos(UINT flags)
rc += " SWP_NOZORDER";
if (flags & SWP_SHOWWINDOW)
rc += " SWP_SHOWWINDOW";
+ if (flags & SWP_ASYNCWINDOWPOS)
+ rc += " SWP_ASYNCWINDOWPOS";
+ if (flags & SWP_DEFERERASE)
+ rc += " SWP_DEFERERASE";
+ if (flags & SWP_DRAWFRAME)
+ rc += " SWP_DRAWFRAME";
+ if (flags & SWP_NOREPOSITION)
+ rc += " SWP_NOREPOSITION";
return rc;
}
@@ -439,18 +510,14 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo
static QMargins invisibleMargins(QPoint screenPoint)
{
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) {
- POINT pt = {screenPoint.x(), screenPoint.y()};
- if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
- if (QWindowsContext::shcoredll.isValid()) {
- UINT dpiX;
- UINT dpiY;
- if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY))) {
- const qreal sc = (dpiX - 96) / 96.0;
- const int gap = 7 + qRound(5*sc) - int(sc);
- return QMargins(gap, 0, gap, gap);
- }
- }
+ POINT pt = {screenPoint.x(), screenPoint.y()};
+ if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
+ UINT dpiX;
+ UINT dpiY;
+ if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) {
+ const qreal sc = (dpiX - 96) / 96.0;
+ const int gap = 7 + qRound(5*sc) - int(sc);
+ return QMargins(gap, 0, gap, gap);
}
}
return QMargins();
@@ -933,12 +1000,9 @@ QMargins QWindowsGeometryHint::frameOnPrimaryScreen(HWND hwnd)
QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle, qreal dpi)
{
- if (QWindowsContext::user32dll.adjustWindowRectExForDpi == nullptr)
- return frameOnPrimaryScreen(style, exStyle);
RECT rect = {0,0,0,0};
style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
- if (QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
- unsigned(qRound(dpi))) == FALSE) {
+ if (AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, unsigned(qRound(dpi))) == FALSE) {
qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__);
}
const QMargins result(qAbs(rect.left), qAbs(rect.top),
@@ -968,8 +1032,7 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry,
{
if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
return {};
- if (!QWindowsContext::user32dll.adjustWindowRectExForDpi
- || QWindowsScreenManager::isSingleScreen()
+ if (QWindowsScreenManager::isSingleScreen()
|| !QWindowsContext::shouldHaveNonClientDpiScaling(w)) {
return frameOnPrimaryScreen(style, exStyle);
}
@@ -1420,8 +1483,7 @@ void QWindowsWindow::initialize()
if (obtainedScreen && screen() != obtainedScreen)
QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen());
}
- QWindowsWindow::setSavedDpi(QWindowsContext::user32dll.getDpiForWindow ?
- QWindowsContext::user32dll.getDpiForWindow(handle()) : 96);
+ QWindowsWindow::setSavedDpi(GetDpiForWindow(handle()));
}
QSurfaceFormat QWindowsWindow::format() const
@@ -1888,6 +1950,8 @@ void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
// Scale child QPlatformWindow size. Windows sends WM_DPICHANGE to top-level windows only.
for (QWindow *childWindow : window()->findChildren<QWindow *>()) {
QWindowsWindow *platformChildWindow = static_cast<QWindowsWindow *>(childWindow->handle());
+ if (!platformChildWindow)
+ continue;
QRect currentGeometry = platformChildWindow->geometry();
QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale);
platformChildWindow->setGeometry(scaledGeometry);
@@ -2167,10 +2231,12 @@ static inline bool isSoftwareGl()
}
bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message,
- WPARAM, LPARAM)
+ WPARAM, LPARAM, LRESULT *result)
{
- if (message == WM_ERASEBKGND) // Backing store - ignored.
+ if (message == WM_ERASEBKGND) { // Backing store - ignored.
+ *result = 1;
return true;
+ }
// QTBUG-75455: Suppress WM_PAINT sent to invisible windows when setting WS_EX_LAYERED
if (!window()->isVisible() && (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0)
return false;
@@ -2482,6 +2548,11 @@ void QWindowsWindow::propagateSizeHints()
bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins)
{
auto *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
+
+ // Tell Windows to discard the entire contents of the client area, as re-using
+ // parts of the client area would lead to jitter during resize.
+ windowPos->flags |= SWP_NOCOPYBITS;
+
if ((windowPos->flags & SWP_NOZORDER) == 0) {
if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) {
QWindow *parentWindow = qWindow->parent();
@@ -2494,7 +2565,7 @@ bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *
}
if (!qWindow->isTopLevel()) // Implement hasHeightForWidth().
return false;
- if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE)))
+ if (windowPos->flags & SWP_NOSIZE)
return false;
const QRect suggestedFrameGeometry(windowPos->x, windowPos->y,
windowPos->cx, windowPos->cy);
@@ -2899,7 +2970,14 @@ void QWindowsWindow::applyCursor()
void QWindowsWindow::setCursor(const CursorHandlePtr &c)
{
#ifndef QT_NO_CURSOR
- if (c->handle() != m_cursor->handle()) {
+ bool changed = c->handle() != m_cursor->handle();
+ // QTBUG-98856: Cursors can get out of sync after restoring override
+ // cursors on native windows. Force an update.
+ if (testFlag(RestoreOverrideCursor)) {
+ clearFlag(RestoreOverrideCursor);
+ changed = true;
+ }
+ if (changed) {
const bool apply = applyNewCursor(window());
qCDebug(lcQpaWindows) << window() << __FUNCTION__
<< c->handle() << " doApply=" << apply;
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 8b9e304e9b..5e8478ca9c 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -238,7 +238,8 @@ public:
VulkanSurface = 0x400000,
ResizeMoveActive = 0x800000,
DisableNonClientScaling = 0x1000000,
- Direct3DSurface = 0x2000000
+ Direct3DSurface = 0x2000000,
+ RestoreOverrideCursor = 0x4000000
};
QWindowsWindow(QWindow *window, const QWindowsWindowData &data);
@@ -312,7 +313,7 @@ public:
void setStyle(unsigned s) const;
void setExStyle(unsigned s) const;
- bool handleWmPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+ bool handleWmPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
void handleMoved();
void handleResized(int wParam);
diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp
index d69d969783..8412663e3c 100644
--- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp
@@ -50,23 +50,8 @@ QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
(QXcbGlIntegrationFactoryInterface_iid, QLatin1String("/xcbglintegrations"), Qt::CaseInsensitive))
-#if QT_CONFIG(library)
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader,
- (QXcbGlIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive))
-#endif // QT_CONFIG(library)
-
-QXcbGlIntegration *QXcbGlIntegrationFactory::create(const QString &platform, const QString &pluginPath)
+QXcbGlIntegration *QXcbGlIntegrationFactory::create(const QString &platform)
{
-#if QT_CONFIG(library)
- // Try loading the plugin from pluginPath first:
- if (!pluginPath.isEmpty()) {
- QCoreApplication::addLibraryPath(pluginPath);
- if (QXcbGlIntegration *ret = qLoadPlugin<QXcbGlIntegration, QXcbGlIntegrationPlugin>(directLoader(), platform))
- return ret;
- }
-#else
- Q_UNUSED(pluginPath);
-#endif
return qLoadPlugin<QXcbGlIntegration, QXcbGlIntegrationPlugin>(loader(), platform);
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h
index c4aa0a3d8d..30ddeb2297 100644
--- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h
+++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h
@@ -49,7 +49,7 @@ class QXcbGlIntegration;
class QXcbGlIntegrationFactory
{
public:
- static QXcbGlIntegration *create(const QString &name, const QString &platformPluginPath = QString());
+ static QXcbGlIntegration *create(const QString &name);
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
index 5f07b10f04..f3630d57b9 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
@@ -67,12 +67,7 @@ bool QXcbEglIntegration::initialize(QXcbConnection *connection)
const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
- if (extensions && (strstr(extensions, "EGL_EXT_platform_xcb") || strstr(extensions, "EGL_MESA_platform_xcb"))) {
- QEGLStreamConvenience streamFuncs;
- m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_XCB_KHR,
- reinterpret_cast<void *>(connection->xcb_connection()),
- nullptr);
- } else if (extensions && strstr(extensions, "EGL_EXT_platform_x11")) {
+ if (extensions && strstr(extensions, "EGL_EXT_platform_x11")) {
QEGLStreamConvenience streamFuncs;
m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_X11_KHR,
xlib_display(),
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
index c021388c5b..45fbe75333 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
@@ -58,7 +58,7 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual()
{
QXcbScreen *scr = xcbScreen();
if (!scr)
- return nullptr;
+ return QXcbWindow::createVisual();
qCDebug(lcQpaGl) << "Requested format before FBConfig/Visual selection:" << m_format;
@@ -71,10 +71,13 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual()
flags |= QGLX_SUPPORTS_SRGB;
}
+ const auto formatBackup = m_format;
XVisualInfo *visualInfo = qglx_findVisualInfo(dpy, scr->screenNumber(), &m_format, GLX_WINDOW_BIT, flags);
if (!visualInfo) {
- qWarning() << "No XVisualInfo for format" << m_format;
- return nullptr;
+ qCDebug(lcQpaGl) << "No XVisualInfo for format" << m_format;
+ // restore initial format before requesting it again
+ m_format = formatBackup;
+ return QXcbWindow::createVisual();
}
const xcb_visualtype_t *xcb_visualtype = scr->visualForId(visualInfo->visualid);
XFree(visualInfo);
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
index 4ef2d53343..94edd87eb7 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -479,24 +479,28 @@ bool QXcbBackingStoreImage::scroll(const QRegion &area, int dx, int dy)
const QRect bounds(QPoint(), size());
const QRegion scrollArea(area & bounds);
const QPoint delta(dx, dy);
+ const QRegion destinationRegion = scrollArea.translated(delta).intersected(bounds);
if (m_clientSideScroll) {
if (m_qimage.isNull())
return false;
if (hasShm())
- preparePaint(scrollArea);
+ preparePaint(destinationRegion);
for (const QRect &rect : scrollArea)
qt_scrollRectInImage(m_qimage, rect, delta);
} else {
- if (hasShm())
- shmPutImage(m_xcb_pixmap, m_pendingFlush.intersected(scrollArea));
- else
- flushPixmap(scrollArea);
-
ensureGC(m_xcb_pixmap);
+ if (hasShm()) {
+ QRegion partialFlushRegion = m_pendingFlush.intersected(scrollArea);
+ shmPutImage(m_xcb_pixmap, partialFlushRegion);
+ m_pendingFlush -= partialFlushRegion;
+ } else {
+ flushPixmap(scrollArea);
+ }
+
for (const QRect &src : scrollArea) {
const QRect dst = src.translated(delta).intersected(bounds);
xcb_copy_area(xcb_connection(),
@@ -507,14 +511,13 @@ bool QXcbBackingStoreImage::scroll(const QRegion &area, int dx, int dy)
dst.x(), dst.y(),
dst.width(), dst.height());
}
- }
- m_scrolledRegion |= scrollArea.translated(delta).intersected(bounds);
- if (hasShm()) {
- m_pendingFlush -= scrollArea;
- m_pendingFlush -= m_scrolledRegion;
+ if (hasShm())
+ m_pendingFlush -= destinationRegion;
}
+ m_scrolledRegion |= destinationRegion;
+
return true;
}
@@ -847,7 +850,13 @@ QImage QXcbBackingStore::toImage() const
return QImage();
m_image->flushScrolledRegion(true);
- return *m_image->image();
+
+ QImage image = *m_image->image();
+
+ // Return an image that does not share QImageData with the original image,
+ // even if they both point to the same data of the m_xcb_image, otherwise
+ // painting to m_qimage would detach it from the m_xcb_image data.
+ return QImage(image.constBits(), image.width(), image.height(), image.format());
}
QPlatformGraphicsBuffer *QXcbBackingStore::graphicsBuffer() const
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index d6257c70b5..191d5af26c 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -773,7 +773,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
case XCB_XKB_NEW_KEYBOARD_NOTIFY: {
xcb_xkb_new_keyboard_notify_event_t *ev = &xkb_event->new_keyboard_notify;
if (ev->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
- m_keyboard->updateKeymap();
+ m_keyboard->updateKeymap(ev);
break;
}
default:
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index 13917ed1df..943f380d5c 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -299,6 +299,8 @@ private:
TouchDeviceData *populateTouchDevices(void *info, QXcbScrollingDevicePrivate *scrollingDeviceP);
TouchDeviceData *touchDeviceForId(int id);
void xi2HandleEvent(xcb_ge_event_t *event);
+ void xi2HandleGesturePinchEvent(void *event);
+ void xi2HandleGestureSwipeEvent(void *event);
void xi2HandleHierarchyEvent(void *event);
void xi2HandleDeviceChangedEvent(void *event);
void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
@@ -373,6 +375,10 @@ private:
QXcbWindow *m_mouseGrabber = nullptr;
QXcbWindow *m_mousePressWindow = nullptr;
+#if QT_CONFIG(gestures)
+ qreal m_lastPinchScale = 0;
+#endif
+
xcb_window_t m_clientLeader = 0;
QByteArray m_startupId;
QXcbSystemTrayTracker *m_systemTrayTracker = nullptr;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
index c4f24ca0ee..921ddf574b 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
@@ -358,7 +358,9 @@ void QXcbBasicConnection::initializeXInput2()
return;
}
- auto xinputQuery = Q_XCB_REPLY(xcb_input_xi_query_version, m_xcbConnection, 2, 2);
+ // depending on whether bundled xcb is used we may support different XCB protocol versions.
+ auto xinputQuery = Q_XCB_REPLY(xcb_input_xi_query_version, m_xcbConnection,
+ 2, XCB_INPUT_MINOR_VERSION);
if (!xinputQuery || xinputQuery->major_version != 2) {
qCWarning(lcQpaXcb, "X server does not support XInput 2");
return;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.h b/src/plugins/platforms/xcb/qxcbconnection_basic.h
index d6eb962f56..baadcb840b 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_basic.h
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.h
@@ -103,6 +103,7 @@ public:
bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
+ bool isAtLeastXI24() const { return m_xi2Enabled && m_xi2Minor >= 4; }
bool isXIEvent(xcb_generic_event_t *event) const;
bool isXIType(xcb_generic_event_t *event, uint16_t type) const;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
index 22285b836d..0a45b1b0c9 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
@@ -191,7 +191,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
}
}
- qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
+ qCDebug(lcQpaScreen) << "updateScreens: primary output is" << qAsConst(m_screens).first()->name();
}
}
@@ -379,7 +379,7 @@ void QXcbConnection::initializeScreens(bool initialized)
}
if (!m_screens.isEmpty())
- qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
+ qCDebug(lcQpaScreen) << "initializeScreens: primary output is" << qAsConst(m_screens).first()->name();
}
}
@@ -534,11 +534,6 @@ void QXcbConnection::initializeScreensFromMonitor(xcb_screen_iterator_t *it, int
siblings << screen;
- if (primaryScreenNumber() == xcbScreenNumber) {
- primaryScreen = screen;
- primaryScreen->setPrimary(true);
- }
-
xcb_randr_monitor_info_next(&monitor_iter);
}
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 4c39c7ee3e..5ddf545d51 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -51,11 +51,17 @@
#include <xcb/xinput.h>
+#define QT_XCB_HAS_TOUCHPAD_GESTURES (XCB_INPUT_MINOR_VERSION >= 4)
+
using qt_xcb_input_device_event_t = xcb_input_button_press_event_t;
+#if QT_XCB_HAS_TOUCHPAD_GESTURES
+using qt_xcb_input_pinch_event_t = xcb_input_gesture_pinch_begin_event_t;
+using qt_xcb_input_swipe_event_t = xcb_input_gesture_swipe_begin_event_t;
+#endif
struct qt_xcb_input_event_mask_t {
xcb_input_event_mask_t header;
- alignas(4) uint8_t mask[4] = {};
+ alignas(4) uint8_t mask[8] = {}; // up to 2 units of 4 bytes
};
static inline void setXcbMask(uint8_t* mask, int bit)
@@ -96,9 +102,19 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
setXcbMask(mask.mask, XCB_INPUT_TOUCH_UPDATE);
setXcbMask(mask.mask, XCB_INPUT_TOUCH_END);
}
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ if (isAtLeastXI24()) {
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_PINCH_BEGIN);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_PINCH_UPDATE);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_PINCH_END);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_SWIPE_BEGIN);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_SWIPE_UPDATE);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_SWIPE_END);
+ }
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
mask.header.deviceid = XCB_INPUT_DEVICE_ALL;
- mask.header.mask_len = 1;
+ mask.header.mask_len = 2;
xcb_void_cookie_t cookie =
xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &mask.header);
xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
@@ -335,6 +351,9 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting,
qCDebug(lcQpaXInputDevices) << " it's a keyboard";
break;
case XCB_INPUT_DEVICE_CLASS_TYPE_TOUCH:
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ case XCB_INPUT_DEVICE_CLASS_TYPE_GESTURE:
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
// will be handled in populateTouchDevices()
break;
default:
@@ -555,6 +574,18 @@ QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info
}
break;
}
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ case XCB_INPUT_DEVICE_CLASS_TYPE_GESTURE: {
+ // Note that gesture devices can only be touchpads (i.e. dependent devices in XInput
+ // naming convention). According to XI 2.4, the same device can't have touch and
+ // gesture device classes.
+ auto *gci = reinterpret_cast<xcb_input_gesture_class_t *>(classinfo);
+ maxTouchPoints = gci->num_touches;
+ qCDebug(lcQpaXInputDevices, " has gesture class");
+ type = QInputDevice::DeviceType::TouchPad;
+ break;
+ }
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
case XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR: {
auto *vci = reinterpret_cast<xcb_input_valuator_class_t *>(classinfo);
const QXcbAtom::Atom valuatorAtom = qatom(vci->label);
@@ -680,6 +711,18 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
break;
}
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ case XCB_INPUT_GESTURE_PINCH_BEGIN:
+ case XCB_INPUT_GESTURE_PINCH_UPDATE:
+ case XCB_INPUT_GESTURE_PINCH_END:
+ xi2HandleGesturePinchEvent(event);
+ return;
+ case XCB_INPUT_GESTURE_SWIPE_BEGIN:
+ case XCB_INPUT_GESTURE_SWIPE_UPDATE:
+ case XCB_INPUT_GESTURE_SWIPE_END:
+ xi2HandleGestureSwipeEvent(event);
+ return;
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
case XCB_INPUT_ENTER:
case XCB_INPUT_LEAVE: {
xiEnterEvent = reinterpret_cast<xcb_input_enter_event_t *>(event);
@@ -991,12 +1034,22 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
setXcbMask(mask, XCB_INPUT_TOUCH_UPDATE);
setXcbMask(mask, XCB_INPUT_TOUCH_END);
}
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ if (isAtLeastXI24()) {
+ setXcbMask(mask, XCB_INPUT_GESTURE_PINCH_BEGIN);
+ setXcbMask(mask, XCB_INPUT_GESTURE_PINCH_UPDATE);
+ setXcbMask(mask, XCB_INPUT_GESTURE_PINCH_END);
+ setXcbMask(mask, XCB_INPUT_GESTURE_SWIPE_BEGIN);
+ setXcbMask(mask, XCB_INPUT_GESTURE_SWIPE_UPDATE);
+ setXcbMask(mask, XCB_INPUT_GESTURE_SWIPE_END);
+ }
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
for (int id : qAsConst(m_xiMasterPointerIds)) {
xcb_generic_error_t *error = nullptr;
auto cookie = xcb_input_xi_grab_device(xcb_connection(), w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id,
XCB_INPUT_GRAB_MODE_22_ASYNC, XCB_INPUT_GRAB_MODE_22_ASYNC,
- false, 1, reinterpret_cast<uint32_t *>(mask));
+ false, 2, reinterpret_cast<uint32_t *>(mask));
auto *reply = xcb_input_xi_grab_device_reply(xcb_connection(), cookie, &error);
if (error) {
qCDebug(lcQpaXInput, "failed to grab events for device %d on window %x"
@@ -1041,6 +1094,163 @@ void QXcbConnection::xi2HandleHierarchyEvent(void *event)
xi2SetupDevices();
}
+#if QT_XCB_HAS_TOUCHPAD_GESTURES
+void QXcbConnection::xi2HandleGesturePinchEvent(void *event)
+{
+ auto *xiEvent = reinterpret_cast<qt_xcb_input_pinch_event_t *>(event);
+
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
+ qCDebug(lcQpaXInputEvents, "XI2 gesture event type %d seq %d fingers %d pos %6.1f, "
+ "%6.1f root pos %6.1f, %6.1f delta_angle %6.1f scale %6.1f on window %x",
+ xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
+ fixed1616ToReal(xiEvent->event_x), fixed1616ToReal(xiEvent->event_y),
+ fixed1616ToReal(xiEvent->root_x), fixed1616ToReal(xiEvent->root_y),
+ fixed1616ToReal(xiEvent->delta_angle), fixed1616ToReal(xiEvent->scale),
+ xiEvent->event);
+ }
+ QXcbWindow *platformWindow = platformWindowFromId(xiEvent->event);
+ if (!platformWindow)
+ return;
+
+ setTime(xiEvent->time);
+
+ TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
+ Q_ASSERT(dev);
+
+ uint32_t fingerCount = xiEvent->detail;
+
+ switch (xiEvent->event_type) {
+ case XCB_INPUT_GESTURE_PINCH_BEGIN:
+ // Gestures must be accepted when we are grabbing gesture events. Otherwise the entire
+ // sequence will get replayed when the grab ends.
+ if (m_xiGrab) {
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
+ XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
+ }
+ m_lastPinchScale = 1.0;
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::BeginNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+
+ case XCB_INPUT_GESTURE_PINCH_UPDATE: {
+ qreal rotationDelta = fixed1616ToReal(xiEvent->delta_angle);
+ qreal scale = fixed1616ToReal(xiEvent->scale);
+ qreal scaleDelta = scale - m_lastPinchScale;
+ m_lastPinchScale = scale;
+
+ QPointF delta = QPointF(fixed1616ToReal(xiEvent->delta_x),
+ fixed1616ToReal(xiEvent->delta_y));
+
+ if (!delta.isNull()) {
+ QWindowSystemInterface::handleGestureEventWithValueAndDelta(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::PanNativeGesture, 0, delta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ if (rotationDelta != 0) {
+ QWindowSystemInterface::handleGestureEventWithRealValue(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::RotateNativeGesture,
+ rotationDelta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ if (scaleDelta != 0) {
+ QWindowSystemInterface::handleGestureEventWithRealValue(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::ZoomNativeGesture,
+ scaleDelta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ break;
+ }
+ case XCB_INPUT_GESTURE_PINCH_END:
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::EndNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+ }
+}
+
+void QXcbConnection::xi2HandleGestureSwipeEvent(void *event)
+{
+ auto *xiEvent = reinterpret_cast<qt_xcb_input_swipe_event_t *>(event);
+
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
+ qCDebug(lcQpaXInputEvents, "XI2 gesture event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
+ xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
+ fixed1616ToReal(xiEvent->event_x), fixed1616ToReal(xiEvent->event_y),
+ fixed1616ToReal(xiEvent->root_x), fixed1616ToReal(xiEvent->root_y),
+ xiEvent->event);
+ }
+ QXcbWindow *platformWindow = platformWindowFromId(xiEvent->event);
+ if (!platformWindow)
+ return;
+
+ setTime(xiEvent->time);
+
+ TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
+ Q_ASSERT(dev);
+
+ uint32_t fingerCount = xiEvent->detail;
+
+ switch (xiEvent->event_type) {
+ case XCB_INPUT_GESTURE_SWIPE_BEGIN:
+ // Gestures must be accepted when we are grabbing gesture events. Otherwise the entire
+ // sequence will get replayed when the grab ends.
+ if (m_xiGrab) {
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
+ XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
+ }
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::BeginNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+ case XCB_INPUT_GESTURE_SWIPE_UPDATE: {
+ QPointF delta = QPointF(fixed1616ToReal(xiEvent->delta_x),
+ fixed1616ToReal(xiEvent->delta_y));
+
+ if (xiEvent->delta_x != 0 || xiEvent->delta_y != 0) {
+ QWindowSystemInterface::handleGestureEventWithValueAndDelta(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::PanNativeGesture, 0, delta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ break;
+ }
+ case XCB_INPUT_GESTURE_SWIPE_END:
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::EndNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+ }
+}
+
+#else // QT_XCB_HAS_TOUCHPAD_GESTURES
+void QXcbConnection::xi2HandleGesturePinchEvent(void*) {}
+void QXcbConnection::xi2HandleGestureSwipeEvent(void*) {}
+#endif
+
void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
{
auto *xiEvent = reinterpret_cast<xcb_input_device_changed_event_t *>(event);
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 9ab804ca1b..48abbbf74c 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -365,6 +365,17 @@ void QXcbKeyboard::updateKeymap(xcb_mapping_notify_event_t *event)
updateKeymap();
}
+void QXcbKeyboard::updateKeymap(xcb_xkb_new_keyboard_notify_event_t *event)
+{
+ if (!event)
+ return;
+
+ if (event->deviceID != event->oldDeviceID)
+ m_config = false;
+
+ updateKeymap();
+}
+
void QXcbKeyboard::updateKeymap()
{
KeysymModifierMap keysymMods;
@@ -372,8 +383,6 @@ void QXcbKeyboard::updateKeymap()
keysymMods = keysymsToModifiers();
updateModifiers(keysymMods);
- m_config = true;
-
if (!m_xkbContext) {
m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES));
if (!m_xkbContext) {
@@ -389,8 +398,13 @@ void QXcbKeyboard::updateKeymap()
if (connection()->hasXKB()) {
m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), xcb_connection(),
core_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS));
- if (m_xkbKeymap)
- m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), xcb_connection(), core_device_id));
+ if (m_xkbKeymap) {
+ if (m_config)
+ m_xkbState.reset(xkb_state_new(m_xkbKeymap.get()));
+ else
+ m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), xcb_connection(), core_device_id));
+
+ }
} else {
m_xkbKeymap.reset(keymapFromCore(keysymMods));
if (m_xkbKeymap)
@@ -411,6 +425,8 @@ void QXcbKeyboard::updateKeymap()
updateXKBMods();
QXkbCommon::verifyHasLatinLayout(m_xkbKeymap.get());
+
+ m_config = true;
}
QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index cf89acff6d..aaf4577985 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -69,6 +69,7 @@ public:
Qt::KeyboardModifiers translateModifiers(int s) const;
void updateKeymap(xcb_mapping_notify_event_t *event);
+ void updateKeymap(xcb_xkb_new_keyboard_notify_event_t *event);
void updateKeymap();
QList<int> possibleKeys(const QKeyEvent *event) const;
diff --git a/src/plugins/platforms/xcb/qxcbmime.cpp b/src/plugins/platforms/xcb/qxcbmime.cpp
index 738d0e3b91..62149e7f6b 100644
--- a/src/plugins/platforms/xcb/qxcbmime.cpp
+++ b/src/plugins/platforms/xcb/qxcbmime.cpp
@@ -113,7 +113,7 @@ bool QXcbMime::mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeDa
if (atomName == QLatin1String("text/uri-list")
&& connection->atomName(a) == "text/x-moz-url") {
const QString mozUri = QLatin1String(data->split('\n').constFirst()) + QLatin1Char('\n');
- *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()),
+ *data = QByteArray(reinterpret_cast<const char *>(mozUri.data()),
mozUri.length() * 2);
} else if (atomName == QLatin1String("application/x-color"))
*dataFormat = 16;
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 6524397cef..a474e569e9 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -705,9 +705,7 @@ void QXcbScreen::setMonitor(xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp
QByteArray ba = connection()->atomName(monitorInfo->name);
m_outputName = getName(monitorInfo);
-
- if (monitorInfo->primary)
- m_primary = true;
+ m_primary = monitorInfo->primary;
m_cursor = new QXcbCursor(connection(), this);
}
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 00bca0def9..c9b6c10e05 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -246,7 +246,7 @@ enum : quint32 {
| XCB_EVENT_MASK_POINTER_MOTION,
transparentForInputEventMask = baseEventMask
- | XCB_EVENT_MASK_VISIBILITY_CHANGE | XCB_EVENT_MASK_RESIZE_REDIRECT
+ | XCB_EVENT_MASK_VISIBILITY_CHANGE
| XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
| XCB_EVENT_MASK_COLOR_MAP_CHANGE | XCB_EVENT_MASK_OWNER_GRAB_BUTTON
};
@@ -1846,7 +1846,7 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
if (event->window == m_window) {
m_mapped = false;
QWindowSystemInterface::handleExposeEvent(window(), QRegion());
- if (!m_isWmManagedWindow) {
+ if (!m_isWmManagedWindow || parent()) {
m_wmStateValid = true;
handleDeferredTasks();
}
@@ -2147,6 +2147,7 @@ void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, con
Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source)
{
m_lastPointerPosition = local;
+ m_lastPointerGlobalPosition = global;
connection()->setTime(time);
Qt::MouseButton button = type == QEvent::MouseMove ? Qt::NoButton : connection()->button();
QWindowSystemInterface::handleMouseEvent(window(), time, local, global,
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index f7bed7f67b..01aabfc429 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -175,6 +175,9 @@ public:
QXcbScreen *xcbScreen() const;
+ QPoint lastPointerPosition() const { return m_lastPointerPosition; }
+ QPoint lastPointerGlobalPosition() const { return m_lastPointerGlobalPosition; }
+
bool startSystemMoveResize(const QPoint &pos, int edges);
void doStartSystemMoveResize(const QPoint &globalPos, int edges);
@@ -271,6 +274,7 @@ protected:
QRegion m_exposeRegion;
QSize m_oldWindowSize;
QPoint m_lastPointerPosition;
+ QPoint m_lastPointerGlobalPosition;
xcb_visualid_t m_visualId = 0;
// Last sent state. Initialized to an invalid state, on purpose.