Platform APIs in Qt 6

While Qt solves many of the typical tasks of writing an application, there are always corner cases that Qt can not cover, or where it makes more sense to build a feature on top of the platform specific APIs, or another toolkit. One of the tasks we wanted to address for Qt 6 was to clean up and coordinate the various mechanisms we had for accessing platform-specific functionality.

We'll now  go through the result of this work in Qt 6. The full documentation is available in the documentation snapshots, as part of the new Platform Integration section.

Type Conversions

Many of Qt's basic data types, such as QString, QPoint, or QImage, provide conversions from and to the native equivalent types.

For example, to get the current user's username on Apple platforms:

NSProcessInfo *processInfo = NSProcessInfo.processInfo;
QString userName = QString::fromNSString(processInfo.userName)

For a complete list of all type conversions, see the Type Conversions overview.

Window Embedding

Windows created by the underlying platform APIs may be used as both parent containers for Qt windows, or embedded into Qt windows as child windows.

The former is useful if the application is mainly written using the native platform APIs, but where parts of the application use Qt, for example to draw a specialized UI. To embed Qt into the window hierarchy of the native application, use QWindow::winId() to get the native handle for the Qt window, and then use the native APIs to re-parent the window into the native UI.

The latter is useful if the native platform, or another toolkit, exposes a specialized control as a native window. By using QWindow::fromWinId() to wrap the native window handle in a QWindow, the window can then be re-parented into the Qt window hierarchy as any other QWindow. To re-parent this QWindow into a Qt Widget based UI, use the widgets-specific QWidget::createWindowContainer() function.

Event Handling

Most event handling use-cases in Qt are sufficiently covered by the cross platform event delivery, via QWindow::event() and friends, or through QObject::installEventFilter().

In cases where this is not enough, Qt provides access to the delivery of the native events. A global event filter that receives all native events can be installed by using QCoreApplication::installNativeEventFilter(), while per-window native events can be handled in QWindow::nativeEvent().

Note: Interfering with the native event flow may put Qt in an inconsistent state. These APIs should primarily be used to augment Qt's existing event handling, for example for events Qt doesn't handle yet.

Native Interfaces

Platform specific functionality not covered by the APIs mentioned above are handled by the new generic native interface mechanism. This mechanism replaces the platform headers user-facing API, as well as the QPA-level QPlatformNativeInterface API. The interfaces provide access to native or platform specific APIs of the classes they extend.

The interfaces live in the QNativeInterface namespace, and cover use-cases such as accessing underlying native handles, adopting existing native handles, or providing platform specific APIs.

The majority of the old platform header APIs can be found in the QNativeInterface::Private namespace, since these were largely used by other internal code. Over time we'll expose more of these APIs based on feedback and use-cases.

Accessing underlying native handles

In situations where a feature of the native platform is not exposed in Qt, it can be helpful to access the native handles maintained by Qt, and use those to call the native APIs instead.

For example, to access the underlying NSOpenGLContext of an QOpenGLContext on macOS, via the QNativeInterface::QCocoaGLContext native interface:

using namespace QNativeInterface;
if (auto *cocoaGLContext = glContext->nativeInterface<QCocoaGLContext>())
[cocoaGLContext->nativeContext() makeCurrentContext];

The native interface is accessed through the QOpenGLContext::nativeInterface() accessor, which ensures that the requested interface is available, and otherwise returns nullptr. The underlying NSOpenGLContext is then accessed through the nativeContext() accessor.

Adopting existing native handles

Similarly to the window embedding use-case, there are situations where the native platform, or another toolkit, has created a native handle that you would like to pass on to Qt — wrapping the existing handle instead of creating a new one.

For example, to adopt an existing NSOpenGLContext, and use that to share resources with a context created by Qt:

using namespace QNativeInterface;
QOpenGLContext *adoptedContext = QCocoaGLContext::fromNativeContext(nsOpenGLContext);

The adopted context is created by a platform specific factory function in the QNativeInterface::QCocoaGLContext native interface.

Accessing platform specific APIs

In some cases an API is too platform specific to be included in the cross platform Qt classes, but is still useful to include. These APIs are available either in the same way as when accessing the underlying native handles, through the nativeInterface() accessor, or directly as static function in the native interface.

For example, to obtain the OpenGL module handle on Windows:

using namespace QNativeInterface;
HMODULE moduleHandle
= QWGLContext::openGLModuleHandle();

Or to tweak the border behavior of a window on Windows, via its platform window handle:

using namespace QNativeInterface::Private;
if (auto *windowsWindow = dynamic_cast<QWindowsWindow*>(window->handle()))

Source and Binary Compatibility

One important thing to note is that are no source or binary compatibility guarantees for the native interface APIs, meaning that an application using these interfaces is only guaranteed to work with the Qt version it was developed against. This allows us to adjust and add to these APIs as needed -- making them more flexible in tracking the underlying native functionality.

Extras modules

As some of you have noticed, the "extras" modules are not part of the initial Qt 6.0 release. This is related to the work described in this blog post, as we still need to go through these modules to survey:

  • Whether any features are deprecated and can be removed
  • Whether any features have more modern replacements that we should advocate instead
  • Whether any features can be better solved by integrating directly with the native APIs
  • Whether any features fit better in the API paradigms described earlier, for example as native interfaces

The end goal would ideally be that we don't need any standalone "extras" module, but rather that the functionality is available directly in the relevant modules, e.g. QtGui or QtDeclarative.  If you want to track this work you can follow QTBUG-83251.

Blog Topics: