Platform APIs in Qt 6
November 10, 2020 by Tor Arne Vestbø | Comments
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);anotherContext->setShareContext(adoptedContext);
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())) windowsWindow->setHasBorderInFullScreen(true);
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:
Comments
Subscribe to our newsletter
Subscribe Newsletter
Try Qt 6.9 Now!
Download the latest release here: www.qt.io/download.
Qt 6.9 is now available, with new features and improvements for application developers and device creators.
We're Hiring
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.
Commenting for this post has ended.
How come Qt 6 still uses moc instead of taking verdigris or implementing its own template-based moc replacement? https://github.com/woboq/verdigris
1.1.
Status in readme has several features it misses.1.2.
Using insted ofpublic slots: void mySlot(int x);
→void mySlot(int x); W_SLOT(mySlot)
is code duplication, you have to write (and rename in case of refactoring) same slot twice. And this is incompatible with existing code. So this is not a replacement.2.
That code can't replace existing moc, so no, moc is still needed.I have written a blog post about cmake for qmake people: https://screen-play.app/blog/qmake_to_cmake/
Could you please document the right way to compile the "host Qt" specified by
-qt-host-path
so we only build the needed tools likemoc
anduic
, instead of compiling all of Qt? For anyone like me who only wants to cross-compile Qt and not have a host version, this will save us a lot of time.qmake was an excellent build system. but because of lacking of document and lots of undocumented hacks. I gradually switch to cmake..
Cmake is one of the worst build systems I've ever seen as soon as you need to do anything beyond simply listing your headers, SRC and INCLUDEPATH. I hope qmake will continue on, perhaps someone will maintain its fork. Contrary to CMake, qmake is the easiest to use build system I know and it easily supports complicated configuration and a bit of scripting. I use it even for non-qt project for its simplicity and flexibility.
Yes, the CMake language is pure horror, but I'd claim that adding own rules is not more difficult than with qmake.
Try Qbs. Its even more flexible and uses a much more powerful language than qmake: https://www.qt.io/blog/qbs-1-17-released
I have created generic CMake scripts which allow to create C++ (with or without Qt) apps and libs very easily: https://gitlab.com/energokoder/energo-budowa
Last time I tried CMake with Qt on iOS I couldn't get far, is it supported on iOS already? Are there some pages with tips?
Yes, it's supported. Maybe the "Cross compiling for iOS" section in qtbase/cmake/README.md is helpful.
Are this instructions still valid? I've downloaded full archive (http://download.qt.io/development_releases/qt/6.0/6.0.0-alpha/single/qt-everywhere-src-6.0.0-alpha.tar.xz) and after
export LLVM_INSTALL_DIR=/usr/lib64/cmake/clang
./configure -cmake -prefix ~/opt/qt6a
cmake --build . --parallel
got:
ninja: error: loading 'build.ninja': No such file or directory
Also, test
vulkan-server-buffer
fails, because#define VK_USE_PLATFORM_WAYLAND_KHR 1
#include <vulkan/vulkan.h>
also needs
wayland-client.h
but there is no dependency for wayland-client.Solved problem by direct cmake call — everything is fine with build.ninja then (also made other build directory):
cmake -GNinja -DCMAKE_INSTALL_PREFIX=<prefix> ../qt-everywhere-src-6.0.0-alpha
offtopic: This commento plugin is buggy — editing ruins formatting — and cant reply to self.
The instructions here are still valid. If you encounter a bug, please report it via the bug tracker.
Off-topic: Sorry for getting this late back to you. Apparently I'm not getting email notifications for comments. There seems to be something off with this commenting facility.
It is the most important in Qt 6! Great job.
The are a lot of about cross-compiling. But as I know the most interesting in 2021 is compiling Qt on ARM64 directly. May be you add some unit-test of building system for OrangePi boards and etc?