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

Mouse release event ignored sometimes

    XMLWordPrintable

Details

    Description

      Sometimes Qt ignores a mouse release event, after pressing a mouse button (using a standard mouse). This is rare and random, but boring. As I could reproduce the problem at a reasonable rate in my application, I could trace it down after a couple of hours.
      The problem is this one: Windows sometimes sends a WM_MOUSEMOVE event just before sending a WM_LBUTTONUP event, and the button state of the WM_MOUSEMOVE event indicates "no button pressed"... and this causes an error in Qt mouse state logic, as explained below:

      In file qapplication_win.cpp
      In function:
      QETWidget::translateMouseEvent

      There is this code called when a move event is received. This is the code that causes a problem.

      if (!(state & Qt::MouseButtonMask))
        qt_button_down = 0;
      

      Much further in the same function, when WM_LBUTTONUP is received, the call to:
      QApplicationPrivate::pickMouseReceiver
      returns NULL because qt_button_down is 0. And the consequence is that the button up event will be ignored later, by any code depending on it. So this is a clear bug (though this is primarly a Windows issue, but we have to live with it).

      In the same Qt file, in function QtWndProc
      there is the following code and comment, which shows Qt programmers are aware of this event issue:

          // Sometimes we only get a WM_MOUSEMOVE message
          // and sometimes we get both a WM_MOUSEMOVE and
          // a WM_LBUTTONDOWN/UP, this creates a spurious mouse
          // press/release event, using the PeekMessage
          // will help us fix this.  This leaves us with a
          // question:
          //    This effectively kills using the mouse AND the
          //    tablet simultaneously, well creates wacky input.
          //    Is this going to be a problem? (probably not)
          bool next_is_button = false;
          bool is_mouse_move = (message == WM_MOUSEMOVE);
          if (is_mouse_move) {
              MSG msg1;
              if (PeekMessage(&msg1, msg.hwnd, WM_MOUSEFIRST,
                              WM_MOUSELAST, PM_NOREMOVE))
                  next_is_button = (msg1.message == WM_LBUTTONUP
                                     || msg1.message == WM_LBUTTONDOWN);
          }
          if (!is_mouse_move || (is_mouse_move && !next_is_button))
              qt_tabletChokeMouse = false;
      

      Although I don't use a tablet, I have used the same approach to solve my problem:
      The code:

          if (!(state & Qt::MouseButtonMask))
              qt_button_down = 0;
      

      is replaced with:

          if (!(state & Qt::MouseButtonMask))
          {
              bool next_is_button_up = false;
              MSG msg1;
              if (PeekMessage(&msg1, msg.hwnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE))
                  next_is_button_up = (msg1.message == WM_LBUTTONUP);
              if (! next_is_button_up)
                  qt_button_down = 0;
          }
      

      "qt_button_down = 0;" is done anyway, further in the Qt chain, when the release event is handled (in QApplicationPrivate::sendMouseEvent).

      This solution has solved my problem.

      Although I don't use a Wacom tablet, I have some users reporting that my application misses some mouse release events. I don't know yet if my patch solves their problem. But for completness, I put a link to this report:
      http://bugreports.qt.nokia.com/browse/QTBUG-6127

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            Unassigned Unassigned
            jirauser26727 user-04d21 (Inactive)
            Votes:
            3 Vote for this issue
            Watchers:
            10 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes