PDA

View Full Version : Qt5 QWidget::create() with Win32 HWND embedding not working after port from Qt4



Chris Lux
28th February 2015, 20:05
Hi,
the following code tries to embed a native Win32 HWND of a custom created OpenGL windows into a QWidget using the create method:



viewer_widget::viewer_widget(
QWidget* parent,
const viewer::viewer_attributes& view_attrib,
const wm::context::attribute_desc& ctx_attrib,
const wm::surface::format_desc& win_fmt)
: QWidget(parent)
{
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);

setAttribute(Qt::WA_NativeWindow, true);
setAttribute(Qt::WA_PaintOnScreen, true); // disables qt double buffering (seems X11 only since qt4.5, ...)
setAttribute(Qt::WA_NoSystemBackground, true);
setAutoFillBackground(false);

_viewer = make_shared<viewer>(math::vec2ui(100, 100), parent->winId(), view_attrib, ctx_attrib, win_fmt);

// ok set the native window as this widgets window...and hold thumbs
QWidget::create(_viewer->window()->window_handle(), true, true);
}


The viewer creates a native Win32 (or X11) window wit the parent of the QWidget as a parent. It also creates and initializes an OpenGL context. This was/is done for more control over the context creation and live time (I know that Qt5 is much improved in that regard). The QWidget::create() method now takes the HWND of the native window and embeds it into the current QWidget, so that event handling is completely done through Qt.

This works perfectly on Qt4 (latest used is Qt 4.8.6 x64 on Windows 7/8.1 x64 on Visual Studio 2013).

Now when porting to Qt5 the same code should still work according to the Qt5 documentation. It required minor changes to account for the change in the WId type. The QWidget::winId() methods still return the native HWND handles of the widgets, which I verified using spyxx.exe (Visual Studio Tools).

However, the code does not work anymore (using Qt 5.4.0 x64 on Windows 7/8.1 x64 on Visual Studio 2013). The native window is just not embedded. In Qt4 when inspecting the created QWidget its native handle (winId) after the create call was identical to the native HWND, which meant the embedding worked. Now using Qt5 the QWidget contains its own HWND which I could again confirm using spyxx.exe. Now there is the parent widget/window and two child widgets/windows where there should only be one child (the native one). I looked at the source code of the create() method for both Qt versions and I do not understand why it is not working anymore.

Ok, after the first night trying to figure this out I tried several other methods I could find on forums or the documentation:

QWindow::fromWinId(HWND) and QWidget::createWindowContainer(QWindow): This one seems to be the way Qt-Devs want me to do it:


_viewer = make_shared<viewer>(math::vec2ui(100, 100), par_hndl, view_attrib, ctx_attrib, win_fmt);
QWindow* native_wnd = QWindow::fromWinId((WId)_viewer->window()->window_handle());
QWidget* native_wdgt = QWidget::createWindowContainer(native_wnd);

QHBoxLayout* lo = new QHBoxLayout(this);
lo->setContentsMargins(0, 0, 0, 0);
lo->addWidget(native_wdgt);


This at least at least behaves almost the way I expect. I see my window and in the newly created widget the native window is embedded. BUT: I have found no way to get any mouse events/input from that newly created widget. I added an eventFilter without luck. I tried a QStackedLayout with a transparent top-level widget to catch the input, but this does not work as the widget created through createWindowContainer() is always on top of the windows stack. I tries creating a QWindow-derived class to intercept the nativeEvent() invocation, without luck...

I am at the end of my ideas to get this working as with Qt4. Is there anything I can to differently to get the old behavior back? The keyboard input I can track through the parent widget using a Qt::StrongFocus policy but the mouse input in complete swallows by the native window. I cannot move the the Qt5 OpenGL window code and need to use our custom OpenGL context tools as we are doing things with the contexts that Qt5 still does not fully support.

I could not yet try Qt5 on Linux, but the Qt4 code as seen above did work there.

I also asked the same question on StackOverflow: http://stackoverflow.com/questions/28786172/qt5-qwidgetcreate-with-win32-hwnd-embedding-not-longer-working-after-port-fr

anda_skoa
2nd March 2015, 12:46
Have you tried handling the events in the foreign window itself?
Since it is not part of Qt's windows the application is not notified about events sent to it by the window system, but it should receive them.

Another technique to integrate different OpenGL renderers with each other is to have one render into a frame buffer object and the other one rendering that into the visible scene.

Cheers,
_

Chris Lux
2nd March 2015, 13:00
Hi,
I thought about that. However, since this viewer will create a windows on Win32 and X11 I hoped to get the QWidget::create() version working again as it allows me to handle the input through Qt independent of the windowing system.

Is there anything that changed for the QWidget::create() method with Qt5 that I am unaware of?

Regards
-chris

anda_skoa
2nd March 2015, 20:11
The platform abstraction works differently in Qt5, e.g. only top level windows have a native handle now as far as I know.

The mechanism of QWindow::fromWinId() was primarily designed to embed windows created by other processes (so called foreign windows), which usually have their own event handling (different application, different event loop).

Since this is all pretty low level you might want to check with the Qt development list.

Cheers,
_