The old code used a grid layout inside the QGroupBox, but the design
with labels in the first and edit-widgets in the second column lends
itself to a QFormLayout, which adapts the alignment of the widgets to
the platform style, so use that.
Amends 2690822428, which, however,
inherited all of the above from even older code.
Pick-to: 6.9 6.8
Change-Id: I528f0ce9d8cb7a997fbfabcdca887c059f571b38
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
- The old code used two QPushButtons in a QHBoxLayout to provide
Ok/Cancel buttons. This hard-codes the positions and text (and
icons) of these buttons, instead of adapting to the platform style.
The new code simply uses QDialogButtonBox, which is designed for
this purpose.
- Also, the old code connected the Ok button's clicked() signal to a
custom slot that then called QDialog::accept(). This means that the
code in the custom slot is not executed when the dialog is accepted
by other means (e.g. return press in one of the line edits
("auto-default"), though I'm not sure here).
The new code uses the idiomatic Qt way of overriding
QDialog::accept() instead, and connects the button-box's accepted()
signal to it. This is done in the .ui file, so it already works in
Designer preview.
- Finally, the old code made a manual connection from the Cancel
button to QDialog::reject().
The new code uses the Qt idiom of connecting in the .ui file
directly, using QDialogButtonBox::rejected() as the signal.
Amends 2690822428, which, however,
inherited all of the above from even older code.
Pick-to: 6.9 6.8
Change-Id: I83afd6156a0811e0c0f99f2480625ea6b69ff78b
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
The old code connected to the wrong signal and therefore had to write
a custom slot to perform the disabling of the group-box.
The new code simply connects the QCheckBox::toggled(bool) signal to
the directly-compatible QWidget::setDisabled(bool) slot, removing the
need for a custom slot.
Also move the connection into the .ui file, so it works already when
checking the form in QtDesigner.
Amends 2690822428, which, however, only
inherited the issues from older code.
Pick-to: 6.9 6.8
Change-Id: Ia834f92de270bb7b18981273188f6e5b6cd457a2
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Because we don't need one. I guess I was just lazy.
Amends b12fd1fa9d ("Long live
qEnvironmentVariable()").
Pick-to: 6.9 6.8
Change-Id: I5b9141c5a32f9d784e0bfffdc3e4d742790aebdd
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
QTranslator::load() used to:
- call find_translation(), which finds the file, ensures it's readable,
and returns file name
- pass the file name to do_load() which would proceed to open the
file
During the time between calling find_translation()/is_readable_file()
and calling do_load(), the underlying filesystem could have changed.
Solve the issue by making find_translation() call do_load() itself (as
suggested in the bug report). Also rename the method to
load_translation() and make it a member method so that it can call
do_load().
Remove the now redundant is_readable_file() static helper, since
QFile::open() in do_load() will do the necessary checks.
Note that there is a slight behavior change:
- with this commit we call do_load() on a variant of the translation
file name, if that fails we call it again on the next variant ...etc
- before this, if the file was readable, the code called do_load() once,
if it failed, it didn't try again.
This was suggested by Volker in code review as a good/welcome
side-effect.
Task-number: QTBUG-129777
Change-Id: Ie5fada60dd6267e67cda4272530db69a44f3ee55
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
As pointed out by Thiago the QT_DECLARE_METATYPE calls are redundant, so
remove them.
Task-number: QTBUG-132101
Pick-to: 6.9 6.8
Change-Id: I73800e70d3f270fb87941d4e053aa7ac5ed1841c
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
We can use hashLengthInternal() to check if the buffer is big enough.
This matches what the QCH::hash() method does, it also has an assert
that `result.size() == ba.size()`, so we can assume this works with
OpenSSL's EVP_MD_get_size() in EVP::finalizeUnchecked().
Amends c70c81b371.
Pick-to: 6.9 6.8
Change-Id: I64935f3d590ab243b361a0b764f011c388820e32
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Will be used in a subsequent commit in QString.
Change-Id: I3f2a15c2b15c6558b61e2365a2cabc9a989b0940
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Wrap them in QVERIFY(), as usual.
Found by QFile::open()-turned-nodiscard-come-6.10.
Amends e673e5a257 and
25c96d547b.
Pick-to: 6.9 6.8
Change-Id: Id39e5d9e500b524af8443cb57916a12f98bd7c23
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
We do use QCborValue and QCborStreamWriter in the bootstrap lib, but we
don't need QCborValue's serialization functionality. In fact, we only
need QCborValue because it is used as a backend to QJsonValue.
Drive-by mark the one out-streaming QDataStream method as depending on
QT_CONFIG(cborstreamreader) (which indicates no one is disabling that
feature).
Change-Id: I3e3a3baa0ce28b744adafffdf7dbf0ffe2911208
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
In the update from 6.7.2 to 6.7.3, commit `ea0f00d5d` changed how linker
flags like `ws2_32` are treated when generating pri/prl files.
Specifically, before the commit a flag like `ws2_32` was left
untouched. The above commit changed it such that such flags are
converted to `-lws2_32` (seemingly in order to better support FFmpeg,
according to the commit message). However, this change also affects
absolute paths if the file has no extension. That is, after the above
mentioned commit, an absolute path linker flag to, say, a dylib on macOS
without a suffix will result in prepending the `-l` flag. This will
result in errors during link time.
An example where this caused problems can be found in the nixpkgs PR
draft #367206 (https://github.com/NixOS/nixpkgs/pull/367206).
This adds a small check to ensure that `-l` is not prepended if the
linker flag is an absolute path without a suffix.
Change-Id: I2c7ce3aac6624e1a27c59af233e3da2c1ae7ba60
Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
The local appDir variable was only used to hold the return value of
another function call. But hardly any compiler will be able to look
through the QString atomic ref-counting to turn the non-Darwin code
paths into the tail-calls that they ought to be.
Remove the variable and return the result immediately. This removes
the move-assignment and dtor calls in the two changed lines and lets
RVO kick in, turning these calls into tail-calls on non-Darwin
platforms.
Amends 4ac872639e.
Pick-to: 6.9 6.8 6.5
Change-Id: Ieeefbd52fc983ab7aebcff6419965b206f374935
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
The tests peek into QEventLoopPrivate, which means that a UBSan build
needs access to QEventLoopPrivate's type_info, for which we need to
export the class.
Amends 3af20bd8eb, which fixed the weak
vtable that, presumably, made this test work before.
Pick-to: 6.9 6.8
Change-Id: I4104ba95093fa240c5ef7d9d5bf287c1652333f0
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
This patch series is in preparation of enabling QT_NO_SCOPED_POINTER
when building QtCore, a prerequisite for enabling this opt-out in leaf
modules.
This first part of the patch series ports objects whose use cannot
"leak" into other modules, e.g. because they are in .cpp files or are
private members and is thus SC (and, as demonstrated by various static
assertions we put into the source code over the years, BC).
The second patch will deal with objects in protected and public APIs,
and thus might be QUIP-6 SiC Type A.
Pick-to: 6.9
Task-number: QTBUG-132213
Change-Id: If4967f6e563a4e7d74550fad4c6d354fad1beef5
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
The previous commit removed the one use of QUuid and, through it,
QCryptographicHash in one of the three bootstrapped tools.
Change-Id: I31b7f9f6aa402709bad7fffd3a65e86be7f6d882
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
By bypassing the two completely and just using the SHA1 algorithm
directly. This removes the only use of either class in bootstrapped
tools.
The use of the null UUID as a namespace is non-standard, so I've
scheduled it for removal in Qt 7, which will also simplify the code. It
will be closer to Microsoft's suggestion in [1]. Until then, this keeps
the exact same algorithm as Qt 6.8's tracegen so the two versions
generate the same IDs.
[1] https://learn.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-tracelogging_define_provider#provider-name-and-id
Change-Id: I5056b93971c9b03d7d82fffd53bf27228074d9f4
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
The author most likely meant to use \image, based on the HTML render at
https://doc.qt.io/qt-6/qml-qt5compat-graphicaleffects-gaussianblur.html.
Also, this case generated invalid DocBook output.
Change-Id: Ibcd562b3847effe97945117a7a67fda33b00af90
Reviewed-by: Andreas Eliasson <andreas.eliasson@qt.io>
lgtm.com is part of GitHub and the original service has shut down.
Change-Id: I03f109408cd408811c60fffdaac42eaeb8c9a84c
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Dimitrios Apostolou <jimis@qt.io>
Add crash handling to avoid waiting until 10min timeout on each crash.
Force restart on each crash by killing emulator.
Make health check more robust to allow it to work even when emulator is
down. This is done via wait in separate process which cannot be done
with normal timeout command, as input to pipe that is not being read
by emulator will block forever.
Pick-to: 6.9
Change-Id: I86c3c86f936cc96d57b38983da48d1d73162399d
Reviewed-by: Simo Fält <simo.falt@qt.io>
The backend-provided DRM device might not be the right one,
so allow kms config to override it.
Pick-to: 6.9 6.8
Task-number: QTBUG-131887
Change-Id: Ied744ae7015eae64f4556f1528e0dbe8ae69d206
Reviewed-by: Pasi Petäjäjärvi <pasi.petajajarvi@qt.io>
With those tests split, tst_QCryptographicHash takes about 4ms.
When FEATURE_openssl_hash is enabled those tests take about 15s on
their own, but when openssl_hash is disabled they take about 2 minutes.
That makes running the tests locally a bit of a hassle when hacking
code ... test ... hack ... test.
This is with a debug build, GCC, `-O0 -g` flags.
Pick-to: 6.9
Change-Id: I8b8f5d1954feb1f9eb8115e27635610a41b42f47
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
QCommandLinkButton was using QIcon::pixmap() without specifying the
current device pixel ratio. Switch to QIcon::paint() to not have to
fiddle around with the dpr at all here.
As a drive-by remove a useless QPainter::save/restore call.
Pick-to: 6.9 6.8
Change-Id: I7e2492a09b28cb8a4f4cc60454733e0054fe1e9b
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
It's safer and clearer.
Also standardize on unique_ptr instead of QScopedPointer, because the
latter is not supposed to be movable, so the assignment in
QLibrarySettings::load() would not compile.
As a drive-by, scope a variable tighter (in an if condition).
Task-number: QTBUG-132213
Pick-to: 6.9 6.8
Change-Id: Iecdd910af4d06dbd03d2daf3ccf99dd4822f04ac
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
The previous patch to not modify the CT_SpinBox by QStyleSheet when
nothing was added for QStylesheet also removed the additional margins &
paddings handling for the size calculation. This patch re-adds this.
This amends 96adebed60.
Pick-to: 6.9 6.8
Task-number: QTBUG-130642
Fixes: QTBUG-132431
Change-Id: Iff1f0febeca90d3154e61fd80e4b359bc7766b84
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
Change the remaining calls to QIcon::pixmap() to pass a valid
devicePixelRatio parameter.
Pick-to: 6.9
Change-Id: I0e71e20c8109e296446e9e13cddba31d53c05df9
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Different styles provide different icons for SP_CommandLink so reload
the icon on every style change.
Pick-to: 6.9 6.8
Change-Id: I2d8bd706d4c1bca89b91c90f1bb90a796b38e0d3
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
And definitely not by non-const reference.
Discovered during upcoming fixes for native function registration.
Task-number: QTBUG-132410
Pick-to: 6.9 6.8
Change-Id: I0ce8519f5a1f9f3caffefc53e6d93b52509bc439
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
- Add operator bool() that checks whether the associated stream has
no error status. This operator makes it possible to use streams
and functions that return references to streams as loop conditions:
`while (stream >> text)`.
- Add testcase for testing the new operator.
- Update existing testcases that use or can use the status of stream
operations.
[ChangeLog][QtCore][QTextStream] Added implicit conversion to bool,
returning `status() == Ok`.
Task-number: QTBUG-52189
Change-Id: I21cbe02ec194078304bcfe606a6f2f572dd09c84
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
2135:5: warning: unused variable 'q' [-Wunused-variable]
2135 | Q_Q(QFileSystemModel);
| ^~~~~~~~~~~~~~~~~~~~~
The variable is only required for the feature filesystemwatcher.
Pick-to: 6.9 6.8
Change-Id: I5da48f94118179ec20fec7da7169b7f22cec17fe
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
QSaveFile records past write errors in writeData(), but often the
QFileDevice::writeData() calls it places will succeed because the data
is only being buffered. Instead, the failures are noticed only by
flush(), whose actions do not affect QSaveFilePrivate::writeError.
[ChangeLog][QtCore][QSaveFile] Fixed a bug that caused commit() to
return true and overwrite its intended target file even though it failed
to flush buffered data to the storage, which could cause data loss. This
issue can be worked around by calling flush() first and only calling
commit() if that returns success.
[ChangeLog][QtCore][QSaveFile] Fixed a bug that caused commit() to
return true even after a cancelWriting() call had been placed, if
writing directly to the target file (that is, only with
setDirectWriteFallback() set to true). Note that the state of the file
does not change under those conditions, only the value returned by the
function.
Drive-by clarify a comment from 6bf1674f1e51fd8b08783035cda7493ecd63b44
(Qt 4.6 "Don't drop errors from flush on QFile::close") which had me
chasing the wrong lead.
Fixes: QTBUG-132332
Pick-to: 6.9 6.8 6.5 5.15
Change-Id: I427df6fd02132d02be91fffd175579c35b9c06cc
Reviewed-by: David Faure <david.faure@kdab.com>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
This could cause conflicts with other users of these algorithms, notably
libb2 itself. Though if you're using libb2, you shouldn't be using the
un-optimized copy inside QtCore...
I've updated the SHA1 of the last commit in the repository, but there
were no changes to our sources.
[ChangeLog][Third-Party Code] Fixed a bug that caused the BLAKE2 symbols
to be visible from QtCore in a static build. If you need to use the
BLAKE2 hashing algorithm in your own code, either use QCryptographicHash
or import libb2 into your build environment. Using libb2 remains the
recommended solution for all systems, especially those for which it has
optimized (vectorized) implementations.
Fixes: QTBUG-132347
Pick-to: 6.9 6.8
Change-Id: I48003b58cef0d8bcc720fffdd89b0f151fd102e3
Reviewed-by: Linus Jahn <lnj@kaidan.im>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
It was used by the QT_TERMINATE_ON_EXCEPTION macro, introduced in 2012,
to support pre-C++11 noexcept semantics. That macro was removed for Qt
6.8 in commit 9b2ae564a5. This commit
amends that removing the definition of qTerminate() immediately in Qt
6.9 (it was an \internal function).
Pick-to: 6.9
Change-Id: I9682121c04fafb3676b0fffd9f5ac999e7603c84
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
This is more robust and lessens the impedance mismatch as seen by the
old code using std::unique_ptr<char[]>(expr).get().
The new code isn't impedance-mismatch-free, either
(UP{qstrdup(qba.data())}), but short of writing a small string class
whose internal buffer can be release()ed to comply with the archaic
QTest::toString() new[]'ed const char* return value, there's not much
we can do about it atm.
Also separate the internal from the "public" stuff in
QCborValueFormatter by adding private: and public: sections.
Pick-to: 6.9 6.8
Change-Id: I09a2ad1a75bb9eab3d01f2b5b60afc2d762da384
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Commit e176dd78fd replaced a `new
char[n]` with a std::make_unique<char[]>(n), probably on this author's
insistence.
But the two are not equivalent: make_unique() value-initializes, even
arrays, even of built-in type, which means that each buffer resize
writes each byte twice: first nulling out the whole buffer as part of
value-initialization, then in the memcpy() and any following read()s.
For buffers of several MiB, or even GiB in size, this is very costly.
Fix by adding and using a backport of C++20
make_unique_for_overwrite(), which performs the equivalent of the old
code (ie. default-, not value-initialization).
Also add q20::is_(un)bounded_array, which are needed for the
implementation of q20::make_unique_for_overwrite().
Amends e176dd78fd.
Pick-to: 6.9 6.8 6.5
Change-Id: I8865c7369e522ec475df122e3d00d6aba3b24561
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
It does.
While std::span, does, too, and so users should be using
std::as_const(), it's quite simple to avoid for QSpan, so we'll fix it
in QSpan. This patch adds the reproducer.
Task-number: QTBUG-132133
Pick-to: 6.9 6.8
Change-Id: I2e416fb7344830cd5e0d945cce61491cd6f4a7a5
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Since `QT_QPA_PLATFORM_PLUGIN_PATH` or `-platformpluginpath`
specifies the path to platform plugins, `extraSearchPath`
should be loaded first.
Pick-to: 6.8 6.9
Change-Id: I2e62fbf2021250ca864c669a7bbd7d56acd67d1e
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
GCC 9 doesn't accept [[unlikely]] between the (condition) and the
compound-statement of an if, in both C++17 and C++20 modes:
qdecompresshelper.cpp: In member function ‘qsizetype QDecompressHelper::readZLib(char*, qsizetype)’:
qcompilerdetection.h:1048:31: error: attributes at the beginning of statement are ignored [-Werror=attributes]
1048 | # define Q_UNLIKELY_BRANCH [[unlikely]]
| ^~~~~~~~~~~~
qdecompresshelper.cpp:597:54: note: in expansion of macro ‘Q_UNLIKELY_BRANCH’
597 | if (ret == Z_DATA_ERROR && !triedRawDeflate) Q_UNLIKELY_BRANCH {
| ^~~~~~~~~~~~~~~~~
See also https://stackoverflow.com/questions/51797959/how-to-use-c20s-likely-unlikely-attribute-in-if-else-statement
Put it into the compound-statement instead, then GCC 9 accepts it. The
two are equivalent, because [[likely]] marks a path, and there is no
selection statement between the two positions.
Amends 5ae84d0afb.
Pick-to: 6.9
Change-Id: Iac1970219c98a1c26e450dfe6bad6583e4d32c29
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
We don't like surprises in qNN, so use the real is_scoped_enum_v from
std instead of adding the _v version outselves, even in C++20.
Amends 03a7be3780 and
63a8f657c2.
Pick-to: 6.9
Change-Id: I7f9149678b95f7a59643152abf5a627e226cc058
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
The content rects for QSpinBoxes were smaller than QLineEdits rects for
UI elements that should have the same size. This patch increases the
content rect for QSpinBoxes.
Task-number: QTBUG-132261
Pick-to: 6.9 6.8
Change-Id: I59e752b719399c5845ab3ef81c6e271deda16273
Reviewed-by: Christian Ehrlicher <ch.ehrlicher@gmx.de>
Reduce the size of the subline for QLineEdit to fit the begin and end of
the rounded frame corner.
Task-number: QTBUG-132261
Pick-to: 6.9 6.8
Change-Id: I879f30c2bdc6601fef4738aeec71d7ab7bcee22c
Reviewed-by: Christian Ehrlicher <ch.ehrlicher@gmx.de>
Replace the internal PainterStateGuard class with the now public
QPainterStateGuard.
Pick-to: 6.9
Change-Id: I9c072ce6e45ddfe2d0a8aba789311193788dee5a
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Replace the internal QPainterStateSaver class with the now public
QPainterStateGuard.
Pick-to: 6.9
Change-Id: I56285cb469cec43071320e87ac378674fd99c06b
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Instead of using an array of fixed-length strings for the vendor names,
use qOffsetStringArray instead. This removes the need of using the
longest vendor name length as the size of the fixed-length strings,
which results in a massive waste of space. This saves 150KB of
(readonly) data.
Details: there are 2555 entries, each one hardcoded to be 78 byte long,
for a total of 199290 bytes. However the vendor names themselves amount
only to 50658 bytes.
Making all this data optional on desktop Linux (where the vendor data is
present anyhow on the system, in /usr/share/) is left for a future
change.
Change-Id: I17007865e741e3dab15dd2ab2feffbce4664fb37
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
The plugin lookup mechanism doesn't handle the situation, when the
plugin is available but it's private dependency(static plugin case)
is missing. In this case we currently silently bypass the dependency
lookup and create targets. So users see the confusing message about
missing linked target, like:
Qt6QSQLiteDriverPluginTargets.cmake:61 (set_target_properties):
The link interface of target "Qt6::QSQLiteDriverPlugin" contains:
SQLite::SQLite3
but the target was not found. Possible reasons include:
* There is a typo in the target name.
* A find_package call is missing for an IMPORTED target.
* An ALIAS target is missing.
This indeed should be handled properly and we should omit creating
targets especially if users don't really use the plugin directly.
Also if dependencies are not satisfied it looks logically to set
the <plugin>_FOUND to false as this will be yet another indicator
for user that the plugin is not found.
Task-number: QTBUG-132244
Pick-to: 6.8 6.9 6.5
Change-Id: I8685163df0dee3a728c724901f69780569ffcad5
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
When calculating the maximum width of a text layout, we
would add the text width of each substring *after* we had
added the negative right bearing of the last character to it.
But the bearing of the last character in a wrapped substring
does not actually add to the maximum width unless it is the
last character of the *whole* string.
Prior to 250117086f this was
not noticed, because the last glyph in the substring would
typically be a space and the space does not have any
bearings (when doing wrapping on individual characters it
could still happen). After the change, the previous glyph
for which we get the right bearing will be the last
non-whitespace glyph. If this happened to have a negative
right bearing, we would add this to the max width and
end up with a larger max width than we should.
This caused a test failure in tst_qquicktext.
This test prefers the text width without the bearing (i.e.
the *advance* of the substring) unless the line is manually
wrapped or it is the last line of the layout.
Pick-to: 6.8 6.9
Change-Id: Iba1a5ad48d575683672400f0572dfa683a0f2d9c
Reviewed-by: Lars Knoll <lars@knoll.priv.no>