Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QCursor::setPos sets wrong X coordinates
Forum Updated to NodeBB v4.3 + New Features

QCursor::setPos sets wrong X coordinates

Scheduled Pinned Locked Moved Unsolved General and Desktop
7 Posts 2 Posters 1.4k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    ebatsin
    wrote on last edited by ebatsin
    #1

    Hi,

    I am trying to update an existing application to support DPI scaling and I have issues with code that update the cursor position using QCursor::setPos.

    My configuration:

    • Linux
    • Two 4K screens at 200% scaling.
    • My Primary Display is the right one, secondary the left one.
    • Qt's Primary display is also the right one (application opens on the right one by default).
    • My screens are reversed in my OS settings (screen 1 is the right one, screen 2 is the left one as recognized by my GPU)

    The code that moves the cursor works when the application is on the right screen (primary screen) but does not work when the application is on the secondary screen.

    It boils down to the following code:

    const auto offset = ...; // Some code that computes an offset
    const auto currentPos = cursor->pos();
    const auto newPos = currentPos + offset;
    cursor->setPos(newPos);
    

    When run on my primary screen, I get:

    • currentPos = QPoint(4419, 111)
    • newPos = QPoint(5750, 111)
    • After calling setPos, cursor->pos() = QPoint(5750, 111) (equal to what was expected)

    When run on my secondary screen, I get:

    • currentPos = QPoint(582, 70)
    • newPos = QPoint(1913, 70)
    • After calling setPos, cursor->pos() = QPoint(0, 70)

    Why is my Y coordinate kept but the X is reset to 0?
    If I run the same code on my screens in 1080p 100% scaling it works correctly so it seems something in QCursor::setPos does not accept my X coordinates when DPI scaling is active. What is weird is that 1913 would be valid coordinates whether or not DPI scaling is enabled since it falls into the screen anyways.

    Any idea what might go wrong here?
    Thanks

    edit:
    My application is in QML but the part that handles cursor movement is in C++ and a QCursor object.

    And here's the list of configurations on which I could repeat the issue. Issue always happen on the secondary display. All my tests have been done on computers with a right primary display and left secondary display.

    Arch Linux

    • DE: Gnome 41
    • Server: Xorg
    • Qt version: unmodified 5.15.2
    • Primary display: 4K @200% scaling
    • Secondary display: 4K @200% scaling

    Ubuntu 21.04

    • DE: Gnome 3.38.5
    • Server: Xorg
    • Qt version: unmodified 5.15.2
    • Primary display: 1440p @200% scaling
    • Secondary display: 1440p @200% scaling

    Windows 10

    • Qt version: unmodified 5.15.2
    • Primary display: 1920×1080 @150% scaling (didn't try @100% scaling, in hindsight I should have)
    • Secondary display: 4K @200% scaling
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Which distribution are you using ?
      Which desktop environment ?
      Which graphic server ? Xorg or Wayland ?
      Which version of Qt ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • E Offline
        E Offline
        ebatsin
        wrote on last edited by ebatsin
        #3

        Distro: Arch Linux
        DE: Gnome 41
        Server: Xorg
        Qt version: Unmodified 5.15.2

        I'll be able to check on an Ubuntu 21.04 with Wayland tomorrow.

        1 Reply Last reply
        0
        • E Offline
          E Offline
          ebatsin
          wrote on last edited by ebatsin
          #4

          Tried on two other machine:s

          First one:
          Distro: Ubuntu 21.04
          DE: Gnome 3.38.5
          Server: Xorg (thought that was Wayland yesterday, I was wrong)
          Qt version: Unmodified 5.15.2

          On this machines the primary display is still the right screen but screens are in the correct order in the OS's settings so this has nothing to do with this.

          Dual screen 1440p:

          • Tested without DPI scaling: No issue
          • Tested with 200% scaling: same issue that on my other computer when I move the window to my left (secondary) screen.

          Second one:
          OS: Windows 10
          Qt version: Unmodified 5.15.2

          On this machine the primary display is 1920×1080 and secondary display is 4K with 200% scaling. The issue happens on this configuration too on the secondary display.

          I've updated the top comment with all the configs I've tested so far and a disclaimer for the fact that even though the QCursor is handled in C++ my entire app is in QML

          1 Reply Last reply
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #5

            Might be a silly question but did you already went through the High DPI chapter in Qt's documentation ?

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            1 Reply Last reply
            0
            • E Offline
              E Offline
              ebatsin
              wrote on last edited by ebatsin
              #6

              I did, I am currently using the Qt::AA_EnableHighDpiScaling option only, without any environment variable modifier. And all my application is rendering correctly on both 1920×1080 and 4K screens. Still have issues with non-integer DPI scaling values but the issue here happens on 200% scaling so I don't think it's related (I didn't knew about the Passthrough option added in Qt 5.14 for DPI scaling values, I'll try that later).

              Something that might be important: the item over which I notice the issue is a QQuickFrameBufferObject, but I make sure to always work in pixel-independent coordinates when using QCursor (I am sure that the coordinates I give to QCursor::setPos are correct, as you can see in the values I logged in my first message)

              1 Reply Last reply
              0
              • E Offline
                E Offline
                ebatsin
                wrote on last edited by
                #7

                Here a small program that shows the issue. I couldn't find a way to upload files so I copy pasted the content below.

                main.cpp:

                #include "myitem.hpp"
                
                #include <QGuiApplication>
                #include <QQmlApplicationEngine>
                #include <QString>
                #include <QSurfaceFormat>
                #include <QUrl>
                #include <QtQml>
                
                int main(int argc, char** argv) {
                
                	{
                		QGuiApplication::setAttribute(Qt::ApplicationAttribute::AA_ShareOpenGLContexts);
                		QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
                		QGuiApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
                
                		// Surface
                		// Force OpenGL 3.3 core profile
                		QSurfaceFormat format;
                		format.setVersion(3, 3);
                		format.setProfile(QSurfaceFormat::CoreProfile);
                		QSurfaceFormat::setDefaultFormat(format);
                	}
                
                	QGuiApplication app(argc, argv);
                
                	qmlRegisterType<MyItem>("MyLib", 1, 0, "MyItem");
                
                	QQmlApplicationEngine engine;
                
                	engine.load(QUrl(QString("qrc:/qml/main.qml")));
                
                	return app.exec();
                }
                
                

                myitem.hpp

                #pragma once
                
                #include <QQuickItem>
                
                #include <memory>
                
                class QMouseEvent;
                
                class MyItem : public QQuickItem {
                	Q_OBJECT
                
                  public:
                	MyItem(QQuickItem* parent = nullptr);
                	~MyItem() override;
                
                  protected:
                	void mousePressEvent(QMouseEvent* event) override;
                	void mouseReleaseEvent(QMouseEvent* event) override;
                	void mouseMoveEvent(QMouseEvent* event) override;
                
                  private:
                	void handleMouseWarping(QMouseEvent& event);
                
                  private:
                	struct Private;
                	std::unique_ptr<Private> d;
                };
                

                myitem.cpp

                #include "myitem.hpp"
                
                #include <QCursor>
                #include <QMouseEvent>
                
                #include <iostream>
                
                struct MyItem::Private {
                	bool mouseWarpingEnabled{ false };
                	unsigned int mouseWarpingMargin{ 5u };
                	QCursor cursor;
                };
                
                MyItem::MyItem(QQuickItem* parent) : QQuickItem{ parent }, d{ new Private } {
                	setAcceptedMouseButtons(Qt::LeftButton);
                }
                
                MyItem::~MyItem() = default;
                
                void MyItem::mousePressEvent(QMouseEvent* event) {
                	d->mouseWarpingEnabled = true;
                }
                
                void MyItem::mouseReleaseEvent(QMouseEvent* event) {
                	d->mouseWarpingEnabled = false;
                }
                
                void MyItem::mouseMoveEvent(QMouseEvent* event) {
                	handleMouseWarping(*event);
                }
                
                // ==== PRIVATE IMPL
                
                void MyItem::handleMouseWarping(QMouseEvent& event) {
                	// Only manipulate QCursor coordinates to prevent having a desync between cursor and event information
                
                	if(!d->mouseWarpingEnabled) {
                		event.ignore();
                		return;
                	}
                
                	const auto globalPos = d->cursor.pos();
                	const auto topLeftRectPos = mapToGlobal({ 0, 0 });
                	const auto localPos = globalPos - topLeftRectPos;
                
                	QPoint offset{ 0, 0 };
                
                	if(localPos.x() < d->mouseWarpingMargin) {
                		offset.setX(width() - 2 * d->mouseWarpingMargin - 1);
                	}
                	else if(localPos.x() > width() - d->mouseWarpingMargin) {
                		offset.setX(-(width() - 2 * d->mouseWarpingMargin - 1));
                	}
                
                	if(localPos.y() < d->mouseWarpingMargin) {
                		offset.setY(height() - 2 * d->mouseWarpingMargin - 1);
                	}
                	else if(localPos.y() > height() - d->mouseWarpingMargin) {
                		offset.setY(-(height() - 2 * d->mouseWarpingMargin - 1));
                	}
                
                	if(offset.x() != 0 || offset.y() != 0) {
                		const auto newGlobalPos = globalPos + offset;
                		d->cursor.setPos(newGlobalPos);
                
                		const auto actualNewGlobalPos = d->cursor.pos();
                		std::cout << "Expected new coordinates: " << newGlobalPos.x() << ", " << newGlobalPos.y()
                		          << ". Actual new coordinates: " << actualNewGlobalPos.x() << ", " << actualNewGlobalPos.y() << std::endl;
                	}
                }
                
                

                main.qml

                import QtQuick 2.15
                import QtQuick.Controls 2.15
                
                import MyLib 1.0
                
                ApplicationWindow {
                    id : root
                    title : 'Mouse warping issue in 4K'
                    visible : true
                
                    width : 800
                    height : 400
                
                	color: Qt.hsla(0, 0, 0.13)
                
                	MyItem {
                		anchors.fill: parent
                
                		Text {
                			text: 'Press left-click down and move over to edge of window to warp to the other side'
                			anchors.centerIn: parent
                			color: Qt.hsla(0, 0, 0.89)
                		}
                	}
                }
                

                qml.qrc

                <RCC>
                    <qresource prefix="/qml">
                        <file>main.qml</file>
                    </qresource>
                </RCC>
                
                
                1 Reply Last reply
                0

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved