August 9, 2010

Uwe Kindler Uwe Kindler
Lab Rat
53 posts

QEventDispatcherWin32 does not handle WM_INPUT messages properly - BUG?

 

Hi,

I need to integrate 3D Connexion Space Navigator [3dconnexion.com] into my Qt Win32 application. 3D Connexion recommends that developers implement the 3D mouse integration with using Raw Input API. To use the Raw input API, you first need to register the input device and assign a window handle of the window that should receive WM_INPUT messages. Because I would like to treat 3D mouse events in the same way like QMouseEvent I need to assign the internal window of the QEventDispatcherWin32. This internal message window receives all Win32 messages and forwards the messages to the event dispatcher object where an application can install an event filter to handle the messages. Because Qt does not provide any way to get the handle of the internal core window, I wrote my own function to get this handle:

  1. HWND QSpaceNavigator::getNativeAppWindow()
  2. {
  3.  HWND tempHwnd = 0;
  4.  // Grab the first window handle that Windows finds:
  5.  tempHwnd = FindWindow(0, 0);
  6.  HINSTANCE hInstance = qWinAppInst();
  7.  do
  8.  {
  9.   // Check if no parent for this window
  10.   if (GetParent(tempHwnd) == 0)
  11.   {
  12.    // check if window application instance is our Qt application
  13.    HINSTANCE AppInstance = (HINSTANCE) GetWindowLong(tempHwnd,
  14.        GWL_HINSTANCE);
  15.    if (AppInstance == hInstance)
  16.    {
  17. #ifdef GWLP_USERDATA
  18.     QAbstractEventDispatcher *q = (QAbstractEventDispatcher *)GetWindowLongPtr(
  19.      tempHwnd,GWLP_USERDATA);
  20. #else
  21.      tempHwnd, GWL_USERDATA);
  22. #endif
  23.     if (q == QAbstractEventDispatcher::instance())
  24.     {
  25.      return tempHwnd;
  26.     }
  27.    }
  28.   }
  29.   tempHwnd = GetWindow(tempHwnd, GW_HWNDNEXT);
  30.  }
  31.  while (tempHwnd != 0);
  32.  return tempHwnd;
  33. }

The function getNativeAppWindow() works fine. I checked this by printing the name of the window and shows QEventDispatcherWin32_Internal_Widget…. Now I used this window handle to register it with my raw input device:

  1. bool QSpaceNavigator::initializeRawInput()
  2. {
  3.  static RAWINPUTDEVICE SpaceNavigatorDevice =
  4.  { 0x01, 0x08, RIDEV_INPUTSINK, 0x00 };
  5.  
  6.  const uint32_t NumberOfDevices = 1;
  7.  const uint32_t DeviceStructSize = sizeof(SpaceNavigatorDevice);
  8.  
  9.  SpaceNavigatorDevice.hwndTarget = getNativeAppWindow();
  10.  std::cout << "EventDispatcher HWND: " << SpaceNavigatorDevice.hwndTarget;
  11.  
  12.  if(RegisterRawInputDevices(&SpaceNavigatorDevice, NumberOfDevices,
  13.      DeviceStructSize))
  14.  {
  15.   std::cout << "Raw input device registered successfully!" << std::endl;
  16.   return true;
  17.  }
  18.  return false;
  19. }

Now I used the function:

  1. QAbstractEventDispatcher::setEventFilter ( EventFilter filter )

to install my own event filter that receives and handles WM_INPUT messages:

  1. bool spaceNavigatorEventFilter(void* msg, long* result)
  2. {
  3.  std::cout << "Message received " << static_cast<PMSG>(message)->message << std::endl;
  4.  std::cout << "Window: " << static_cast<PMSG>(message)->hwnd << std::endl;
  5.  return QSpaceNavigator::instance()->processSpaceNavigatorEvent(static_cast<PMSG>(msg));
  6. }

So normally this should work properly and spaceNavigatorEventFilter() function should receive all events and then handles WM_INPUT events of space navigator device.

I created a small test application based on QMainWindow to check if this filter works. In the main window I placed two QSpinBoxes. When I start the application and move my mouse over the main window, I can see a lot of mouse messages on my console printed by std::cout lines in my event filter. That shows that the event filter works. When I move my 3D space navigator nothing happens – no messages arrive in my event filter. Now, if I move my normal mouse again, a burst of messages from 3D space navigator arrive in the message filter – that means only if I move my mouse, the WM_INPUT messages that are sent from the 3D space navigator previously arrive in my event filter.

If I click into one of the two QSpinBoxes, that means one spin box gets the input focus, then I can move the space navigator and I can see that WM_INPUT messages from the 3D device arrive in my event filter. If I print the hwnd field of the WM_INPUT message, I can see that the window handle is the window handle of the QEventDispatcherWin32 internal window. That shows, that the raw input device is properly registered with the internal event dispatcher window.

My question that may only get answered from a troll that knows the Qt event dispatching in detail: Why do normal mouse messages arrive in my event filter all the time I move my mouse and why do WM_INPUT messages of the windows raw API arrive only in my event filter if a widget has the input focus in the active window? This is quite strange and makes it impossible to use RAW input API with Qt because it would force the user to first click into a widget that can receive input focus before he can use the 3D space navigator.

Sorry for the long post but I spent two days now trying to integrate 3D space navigator in my Qt application and did not find a working solution yet. Is there any best practice how to integrate Win32 raw input API devices into Qt?

11 replies

August 9, 2010

Thomas Zander Thomas Zander
Lab Rat
219 posts

As far as I know there is an ‘spnav’ library that handles this (my project searches for spnav.h) and the open source app suite ‘koffice’ uses this to handle the navigator so its known to work.

See http://websvn.kde.org/trunk/koffice/plugins/spacenavigator/ for example code.

August 9, 2010

Uwe Kindler Uwe Kindler
Lab Rat
53 posts

Thank you for this hint. Unfortunately the http://spacenav.sourceforge.net/spnav-win32.html [spacenav.sourceforge.net] driver is no option for us for two reasons:

1. It is not in production quality.

2. It requires http://sourceforge.net/projects/hidlibrary [sourceforge.net] which is also in beta state and requires .NET Framework 2.0. We would like to avoid any dependency to .NET Framework.

I will try to work around the issue by creating a second message pump in a different native Win32 thread that has its own message window and its own windows procedure. But this still does not answer the question why QEventDispatcherWin32 does not handle WM_INPUT messages properly.

September 28, 2010

David Dibben David Dibben
Lab Rat
8 posts

I have been having the same problem trying to get the 3D Connexion mouse working and I think I have discovered where the problem is in QEventDispatcherWin32.

I am using Qt 4.6.2 with VisualStudio 2005 on Windows XP. The problem I was having was that the WM_INPUT messages were not being received by window/event filter the until some other event such as a mouse or timer event occurred. I could move the 3D mouse and nothing would happen then move the normal mouse and all the 3D mouse events would arrive.

I traced the problem to

  1. MsgWaitForMultipleObjectsEX(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);

In the processEvents function of QEventDispatcherWin32. This was blocking and not waking on WM_INPUT events.

The windows documentation for this notes that QS_ALLINPUT, which includes QS_INPUT, “does not include QS_RAWINPUT in Windows2000.” Therefore I think the problem is that the binary Qt installer is built to work on all windows platforms, so during the compile _WIN32_WINNT is defined to something less that WinXP (0×501) so that raw-input messages are not handled.

I re-compiled Qt passing -D _WIN32_WINNT=0×501 to configure and now the WM_INPUT messages are being passed to the event filter when the 3D mouse is moved.

So the options seem to be

  • Recompile Qt with _WIN32_WINNT=0×501 so that XP (and later) functions are enabled
    or
  • Use a timer to unblock the event loop at regular intervals so that the messages get processed. I am not sure yet what effect this will have on the smoothness of the mouse operation.

I also found that just using the winId() function of the main window for the HWND to use for registering the device also worked fine.

September 28, 2010

Uwe Kindler Uwe Kindler
Lab Rat
53 posts

Hi David,

hey thank you very much for this solution. We use a self-compiled Qt installation here so it should be no problem to recompile it with _WIN32_WINNT=0×501.

We worked around the problem by creating a native Win32 DLL (without Qt) that creates an internal win32 message window in its own thread that receives the messages from Space Navigator device. Then we wrote a Qt wrapper for this DLL that translates the messages into Qt events an created a QTDx device.

But your solution makes it much easier to use Space Navigator from Qt – thank you :O)

Uwe

February 2, 2011

BennM BennM
Lab Rat
5 posts

Hi,

can anybody of you post an example code which shows how to implement the spacenavigator in qt?

Thank you very much

February 5, 2011

David Dibben David Dibben
Lab Rat
8 posts

I wrote an slightly updated version of my comments above and added some sample code of a Qt class to get the data from the mouse on Windows at:

http://www.codegardening.com/2011/02/using-3dconnexion-mouse-with-qt.html [codegardening.com]

With Qt 4.7.1 it seems to work without having to re-complile the Qt.

February 6, 2011

BennM BennM
Lab Rat
5 posts

@David Dibben: Thank you very much for the sample code. I tried to compile it with the qt eclipse integration but it stops during compiling the file “Mouse3DInput.cpp” in line around 345 “pri = NEXTRAWINPUTBLOCK;” complaining that “NEXTRAWINPUTBLOCK was not declared in this scope”.

I added the line in 3DMouse.pro

  1. LIBS +=  -lUser32 -LC:\\programs\\Microsoft SDKs\\Windows\\v6.0A\\Lib

added user32.lib and the paths to the lib und include directories of Microsofts SDKs.

Is there anything missing?

[EDIT: code formatting, Volker]

February 7, 2011

David Dibben David Dibben
Lab Rat
8 posts

NEXTRAWINPUTBLOCK is a windows macro, defined in winuser.h which should be included with windows.h
See – http://msdn.microsoft.com/en-us/library/ms645593(v=vs.85).aspx

Which compiler are you using? I am guessing that it is mingw. I don’t have experience using g++ on Windows – I compiled the sample using MS VC++ 2008 Express.

The windows headers should be available with mingw as far as I know, but the windows headers are full of conditional statements so it is possible that something has to be defined for the macros to show up correctly.

February 8, 2011

BennM BennM
Lab Rat
5 posts

Yes I use mingw and did the following changes in your sourcecode:

I added this in Mouse3DInput.cpp

  1. #define RAWINPUT_ALIGN(x)   (((x) + sizeof(DWORD) - 1) & ~(sizeof(DWORD) - 1))
  2. #define NEXTRAWINPUTBLOCK(ptr) ((PRAWINPUT)RAWINPUT_ALIGN((ULONG_PTR)((PBYTE)(ptr) + (ptr)->header.dwSize)))

and this

  1. #include "C:\Qt\2010.05\mingw\include\windows.h"
  2. #include "C:\Qt\2010.05\mingw\include\winuser.h"

Then I found a difference between Microsoft’s WinUser.h and mingw’s winuser.h (see following):

  1. typedef struct tagRAWHID {
  2.  DWORD dwSizeHid;
  3.  DWORD dwCount;
  4.  BYTE bRawData;
  5. } RAWHID,*PRAWHID,*LPRAWHID;
I changed it to
  1. bRawData[1]
like in Microsoft’s WinUser.h. I don’t know why it works with the range of one, because in the sourcecode you’ll find the command
  1. pRawInput->data.hid.bRawData[1] == 0x01

so it must be at least 2, right?

Additionally I added

  1. CONFIG += console
  2.  
  3. DEFINES += _WIN32_WINNT="0x0501"
  4. DEFINES += _WIN32_WINDOWS="0x0501"
  5.  
  6. INCLUDEPATH += "C:\\Qt\\2010.05\\mingw\\include" \
  7.  
  8. LIBS +=  -luser32 -L"C:\\Qt\\2010.05\\mingw\\lib"

to 3DMouse.pro, but I am not shure if I need all of that.

After that I was able to compile it. At the moment I am not sure if the translation works correctly, because the data appears too fast in the textboxes, so I will change the program that it saves the data in a textfile.

February 18, 2011

BennM BennM
Lab Rat
5 posts

Hi,

I recorded some data which my program (original written by David Dibben) received from the spacenavigator. I made translational movements at first – followed by rotations. There is only noise on the x-axis and no reaction on y- and z-axis. But you can see the translational movements in the last three plots (plots of the rotations). Does anybody has any idea what’s going wrong?

plot

The horizontal axis shows the number of received events – it’s not the time!

March 2, 2011

BennM BennM
Lab Rat
5 posts

Hi,

I switched to Visual Studio and your code worked out of the box.

Thank you for the sample code, David Dibben!

 
  ‹‹ Problem with link resources to executable file      Windows 7 64-bit Platform: Qt ONLY debugs my app in assembler ››

You must log in to post a reply. Not a member yet? Register here!