Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-55038

QCursor::setPos(const QPoint &p) fails when the point is not on the primary screen.

    XMLWordPrintable

Details

    Description

      Reopened because the response was not correct. The function clearly states "global screen position":

          [static] void QCursor::setPos(const QPoint &p)
      
          This is an overloaded function.
      
          Moves the cursor (hot spot) to the global screen position at point p.
      

      Even if the position is required to be on the primary screen, this function is moving the mouse cursor to the far left side of screen #2.

      There is confusion in some of the documentation on what a global screen coordinate is (it appears that the window geometry docs pre-date QScreen). The most commonly used function that I'm aware of is "QWidget::move()." which, for a top level window, moves a widget to a global position. In fact, internally, it contains code similar to the workaround code below. QWidget::move() is consistent with QWheelEvent::globalPos(), QCursor::globalPos(), QWidget::pos() and all other functions I've seen in Qt dealing with global positions. The only outlier is QCursor::setPos().

      Even this fails: QCursor::setPos(QCursor::pos()) because they are not consistent.

      If you simply do code like this:

      void MyWidget::wheelEvent(QWheelEvent *e)
      {
              QCursor::setPos(e->globalPos());
      }
      

      That code should not actually move the mouse. However, in Windows on a multiple monitor system with High DPI Support enabled, using Windows DPI Awareness level 1 (System DPI Aware), this code will cause the mouse cursor to fly off the screen on secondary monitors.

      I think that the problem is here:

      void QCursor::setPos(int x, int y)
      {
          QCursor::setPos(QGuiApplication::primaryScreen(), x, y);
      }
      

      Since the position is a global position, there is no reason to assume it is on the primary screen. (The code that follows makes a transformation incorrectly because it grabbed the wrong screen.) It seems that the code should mirror other code which figures out what screen it is on. I've used code like this locally:

              QPoint newPt = e->globalPos();
              const QList<QScreen *> screens = QGuiApplication::screens();
              for (int i = 0; i < screens.size(); ++i)
              {
                  QScreen *screen = screens[i];
                  if (screen->geometry().contains(newPt))
                  {
                      QCursor::setPos(screen, newPt);
                      break;
                  }
              }
      

      That seems to work properly, at least in my scenario.

      .h2 Environment:
      I have a computer with three monitors in Windows 10. Left monitor (2) is 1920x1200, set to 100% scaling in control panel. Center monitor (primary, 0) is 3840x2160, 200%. Right monitor (laptop, 1) is 1920x1080, 125%.

      If I loop through the screens, here's the data I see:

      Using system dpi aware mode
      
      Screen 0
      Geometry, 0,0, 1920, 1080.  Available, 0,0, 1920, 1040, 527mm x 296mm
      DevicePixelRatio = 2
      192 dpi
      
      Screen 1
      Geometry, 7680, 2150, 1536, 864.  Available 7680, 2150, 1536, 824. 344mm x 193mm
      DevicePixelRatio = 2
      192 dpi
      
      Screen 2
      Geometry, -3840, 870, 1920, 1200.  Available -3840, 870, 1920, 1160. 519mm x 324mm
      DevicePixelRatio = 2
      192 dpi
      

      Attachments

        Issue Links

          Activity

            People

              srutledg Shawn Rutledge
              roblefebvre Rob Lefebvre
              Votes:
              11 Vote for this issue
              Watchers:
              15 Start watching this issue

              Dates

                Created:
                Updated: