Merge remote-tracking branch 'origin/5.13' into dev

Change-Id: I056b658ffe9390dfcbe2787e2bddc7f4e9b389dd
This commit is contained in:
Qt Forward Merge Bot 2019-03-09 01:00:54 +01:00
commit 261c0dedac
53 changed files with 887 additions and 697 deletions

View File

@ -291,6 +291,8 @@ defineTest(qtConfTest_architecture) {
content = $$cat($$test_out_dir/libarch.so, blob)
else: wasm:exists($$test_out_dir/arch.wasm): \
content = $$cat($$test_out_dir/arch.wasm, blob)
else: wasm:exists($$test_out_dir/arch.o): \
content = $$cat($$test_out_dir/arch.o, blob)
else: \
error("$$eval($${1}.label) detection binary not found.")

View File

@ -5,6 +5,17 @@ QMAKE_PLATFORM = wasm unix
include(../common/gcc-base.conf)
include(../common/clang.conf)
load(device_config)
# Support setting WASM_OBJECT_FILES with -device-option WASM_OBJECT_FILES=1
!isEmpty(WASM_OBJECT_FILES): {
!equals(WASM_OBJECT_FILES, 1):!equals(WASM_OBJECT_FILES, 0): \
message(Error: The value for WASM_OBJECT_FILES must be 0 or 1)
QMAKE_CFLAGS += -s WASM_OBJECT_FILES=$$WASM_OBJECT_FILES
QMAKE_CXXFLAGS += -s WASM_OBJECT_FILES=$$WASM_OBJECT_FILES
QMAKE_LFLAGS += -s WASM_OBJECT_FILES=$$WASM_OBJECT_FILES
}
EMTERP_FLAGS = \
-s EMTERPRETIFY=1 \
-s EMTERPRETIFY_ASYNC=1 \
@ -22,10 +33,14 @@ EMCC_COMMON_LFLAGS = \
-s \"BINARYEN_TRAP_MODE=\'clamp\'\"
EMCC_USE_PORTS_FLAGS = \
-s USE_LIBPNG=1 \
-s USE_FREETYPE=1 \
-s USE_ZLIB=1
# libpng does not build for WASM_OBJECT_FILES=1, see
# https://github.com/emscripten-core/emscripten/issues/8143
equals(WASM_OBJECT_FILES, 0):\
EMCC_USE_PORTS_FLAGS += -s USE_LIBPNG=1
# The -s arguments can also be used with release builds,
# but are here in debug for clarity.
EMCC_COMMON_LFLAGS_DEBUG = \
@ -38,6 +53,35 @@ EMCC_COMMON_LFLAGS_DEBUG = \
# -s SOCKET_DEBUG \ #print out socket,network data transfer
-s GL_DEBUG=1
# Set up debug/optimization flags
QMAKE_CXXFLAGS_RELEASE -= -O2
QMAKE_CFLAGS_RELEASE -= -O2
equals(WASM_OBJECT_FILES, 1) {
QMAKE_LFLAGS_DEBUG += -g
QMAKE_CXXFLAGS_RELEASE += -O3
QMAKE_CFLAGS_RELEASE += -O3
QMAKE_LFLAGS_RELEASE += -O3
QMAKE_CFLAGS_OPTIMIZE += -O3
QMAKE_CFLAGS_OPTIMIZE_FULL += -Oz
} else {
# Practical debugging setup:
# "-g4" preserves function names for stack traces
# "-Os" produces reasonably sized binaries
QMAKE_CFLAGS_DEBUG -= -g
QMAKE_CXXFLAGS_DEBUG -= -g
QMAKE_CFLAGS_DEBUG += -Os -g4
QMAKE_CXXFLAGS_DEBUG += -Os -g4
QMAKE_LFLAGS_DEBUG += -Os -g4
QMAKE_CXXFLAGS_RELEASE += -O3
QMAKE_CFLAGS_RELEASE += -O3
QMAKE_LFLAGS_RELEASE += -O3
QMAKE_CFLAGS_OPTIMIZE += -O3
QMAKE_CFLAGS_OPTIMIZE_FULL += -Oz
}
QMAKE_COMPILER += emscripten
QMAKE_CC = emcc
@ -46,23 +90,6 @@ QMAKE_CXX = em++
QMAKE_CFLAGS += $$EMCC_USE_PORTS_FLAGS
QMAKE_CXXFLAGS += $$EMCC_USE_PORTS_FLAGS
# Practical debugging setup:
# "-g4" preserves function names for stack traces
# "-Os" produces reasonably sized binaries
QMAKE_CFLAGS_DEBUG -= -g
QMAKE_CXXFLAGS_DEBUG -= -g
QMAKE_CFLAGS_DEBUG += -Os -g4
QMAKE_CXXFLAGS_DEBUG += -Os -g4
QMAKE_LFLAGS_DEBUG += -Os -g4
QMAKE_CXXFLAGS_RELEASE -= -O2
QMAKE_CXXFLAGS_RELEASE += -O3
QMAKE_CFLAGS_RELEASE -= -O2
QMAKE_CFLAGS_RELEASE += -O3
QMAKE_LFLAGS_RELEASE += -O3
QMAKE_CFLAGS_OPTIMIZE += -O3
QMAKE_CFLAGS_OPTIMIZE_FULL += -Oz
QMAKE_LINK = $$QMAKE_CXX
QMAKE_LINK_SHLIB = $$QMAKE_CXX
QMAKE_LINK_C = $$QMAKE_CC

View File

@ -84,6 +84,7 @@ Win32MakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags)
if (impexts.isEmpty())
impexts = project->values("QMAKE_EXTENSION_STATICLIB");
QList<QMakeLocalFileName> dirs;
int libidx = 0;
for (const ProString &dlib : project->values("QMAKE_DEFAULT_LIBDIRS"))
dirs.append(QMakeLocalFileName(dlib.toQString()));
static const char * const lflags[] = { "LIBS", "LIBS_PRIVATE",
@ -96,11 +97,12 @@ Win32MakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags)
LibFlagType type = parseLibFlag(opt, &arg);
if (type == LibFlagPath) {
QMakeLocalFileName lp(arg.toQString());
if (dirs.contains(lp)) {
int idx = dirs.indexOf(lp);
if (idx >= 0 && idx < libidx) {
it = l.erase(it);
continue;
}
dirs.append(lp);
dirs.insert(libidx++, lp);
(*it) = "-L" + lp.real();
} else if (type == LibFlagLib) {
QString lib = arg.toQString();

View File

@ -82,6 +82,7 @@ import android.view.inputmethod.InputMethodManager;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.hardware.display.DisplayManager;
import java.io.BufferedReader;
import java.io.DataOutputStream;
@ -667,6 +668,28 @@ public class QtActivityDelegate
} catch (Exception e) {
e.printStackTrace();
}
DisplayManager.DisplayListener displayListener = new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) { }
@Override
public void onDisplayChanged(int displayId) {
m_currentRotation = m_activity.getWindowManager().getDefaultDisplay().getRotation();
QtNative.handleOrientationChanged(m_currentRotation, m_nativeOrientation);
}
@Override
public void onDisplayRemoved(int displayId) { }
};
try {
DisplayManager displayManager = (DisplayManager) m_activity.getSystemService(Context.DISPLAY_SERVICE);
displayManager.registerDisplayListener(displayListener, null);
} catch (Exception e) {
e.printStackTrace();
}
m_mainLib = QtNative.loadMainLibrary(m_mainLib, nativeLibsDir);
return m_mainLib != null;
}
@ -856,13 +879,6 @@ public class QtActivityDelegate
} catch (Exception e) {
e.printStackTrace();
}
int rotation = m_activity.getWindowManager().getDefaultDisplay().getRotation();
if (rotation != m_currentRotation) {
QtNative.handleOrientationChanged(rotation, m_nativeOrientation);
}
m_currentRotation = rotation;
}
public void onDestroy()

View File

@ -286,7 +286,7 @@ static inline QStringList *resourceSearchPaths()
decompress, use the \c{ZSTD_decompress} function from the zstd
library.
\sa compressionAlgorithm(), isCopressed()
\sa compressionAlgorithm(), isCompressed()
*/
class QResourcePrivate {
@ -558,7 +558,7 @@ bool QResource::isValid() const
check compressionAlgorithm() to verify what algorithm to use to decompress
the data.
\sa data(), compressionType(), isFile()
\sa data(), compressionAlgorithm(), isFile()
*/
bool QResource::isCompressed() const

View File

@ -912,16 +912,20 @@ QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
file.open(QIODevice::ReadOnly);
//dump data
QTemporaryFile *ret = new QTemporaryFile;
ret->open();
file.seek(0);
char buffer[1024];
while(true) {
qint64 len = file.read(buffer, 1024);
if(len < 1)
break;
ret->write(buffer, len);
if (ret->open()) {
file.seek(0);
char buffer[1024];
while (true) {
qint64 len = file.read(buffer, 1024);
if (len < 1)
break;
ret->write(buffer, len);
}
ret->seek(0);
} else {
delete ret;
ret = nullptr;
}
ret->seek(0);
//restore
if(wasOpen)
file.seek(old_off);

View File

@ -103,7 +103,7 @@ QConcatenateTablesProxyModelPrivate::QConcatenateTablesProxyModelPrivate()
\since 5.13
\class QConcatenateTablesProxyModel
\inmodule QtCore
\brief The QConcatenateTablesProxyModel class proxies multiple source models, concatenating their rows
\brief The QConcatenateTablesProxyModel class proxies multiple source models, concatenating their rows.
\ingroup model-view
@ -163,7 +163,7 @@ QModelIndex QConcatenateTablesProxyModel::mapFromSource(const QModelIndex &sourc
}
/*!
Returns the source index for a given proxy index.
Returns the source index for a given \a proxyIndex.
*/
QModelIndex QConcatenateTablesProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
@ -232,8 +232,8 @@ bool QConcatenateTablesProxyModel::setItemData(const QModelIndex &proxyIndex, co
/*!
Returns the flags for the given index.
If the index is valid, the flags come from the source model for this index.
If the index is invalid (as used to determine if dropping onto an empty area
If the \a index is valid, the flags come from the source model for this \a index.
If the \a index is invalid (as used to determine if dropping onto an empty area
in the view is allowed, for instance), the flags from the first model are returned.
*/
Qt::ItemFlags QConcatenateTablesProxyModel::flags(const QModelIndex &index) const

View File

@ -162,8 +162,9 @@ void QTransposeProxyModelPrivate::onRowsAboutToBeMoved(const QModelIndex &source
/*!
\since 5.13
\class QTransposeProxyModel
\brief This proxy transposes the source model
\details This model will make the rows of the source model become columns of the proxy model and vice-versa.
\brief This proxy transposes the source model.
This model will make the rows of the source model become columns of the proxy model and vice-versa.
If the model is a tree, the parents will be transposed as well. For example, if an index in the source model had parent `index(2,0)`, it will have parent `index(0,2)` in the proxy.
*/

View File

@ -65,6 +65,19 @@ QCFString::operator CFStringRef() const
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
bool AppleUnifiedLogger::willMirrorToStderr()
{
// When running under Xcode or LLDB, one or more of these variables will
// be set, which triggers libsystem_trace.dyld to log messages to stderr
// as well, via_os_log_impl_mirror_to_stderr. Un-setting these variables
// is not an option, as that would silence normal NSLog or os_log calls,
// so instead we skip our own stderr output. See rdar://36919139.
static bool willMirror = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE")
|| qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR")
|| qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR");
return willMirror;
}
QT_MAC_WEAK_IMPORT(_os_log_default);
bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogContext &context,
const QString &message, const QString &optionalSubsystem)
@ -103,15 +116,7 @@ bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogCont
// system from redacting our log message.
os_log_with_type(log, logType, "%{public}s", qPrintable(message));
// When running under Xcode or LLDB, one or more of these variables will
// be set, which triggers libsystem_trace.dyld to log messages to stderr
// as well, via_os_log_impl_mirror_to_stderr. Un-setting these variables
// is not an option, as that would silence normal NSLog or os_log calls,
// so instead we skip our own stderr output. See rdar://36919139.
static bool mirroredToStderr = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE")
|| qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR")
|| qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR");
return mirroredToStderr;
return willMirrorToStderr();
}
os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)

View File

@ -202,6 +202,7 @@ class Q_CORE_EXPORT AppleUnifiedLogger
public:
static bool messageHandler(QtMsgType msgType, const QMessageLogContext &context, const QString &message,
const QString &subsystem = QString());
static bool willMirrorToStderr();
private:
static os_log_type_t logTypeForMessageType(QtMsgType msgType);
static os_log_t cachedLog(const QString &subsystem, const QString &category);

View File

@ -178,7 +178,7 @@ public:
#ifndef QT_NO_REGEXP
#if QT_DEPRECATED_SINCE(5, 13)
template<typename T>
QT_DEPRECATED_X("Use findChildren(const RegularExpression &, ...) instead.")
QT_DEPRECATED_X("Use findChildren(const QRegularExpression &, ...) instead.")
inline QList<T> findChildren(const QRegExp &re, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;

View File

@ -818,6 +818,16 @@ void QThread::quit()
}
void QThread::exit(int returnCode)
{
Q_D(QThread);
d->data->quitNow = true;
for (int i = 0; i < d->data->eventLoops.size(); ++i) {
QEventLoop *eventLoop = d->data->eventLoops.at(i);
eventLoop->exit(returnCode);
}
}
bool QThread::wait(unsigned long time)
{
Q_UNUSED(time);

View File

@ -91,16 +91,29 @@ macro(_qt5gui_find_extra_libs Name Libs LibDir IncDirs)
endforeach()
!!ENDIF
foreach(_lib ${Libs})
string(REGEX REPLACE "[^_A-Za-z0-9]" "_" _cmake_lib_name ${_lib})
if (IS_ABSOLUTE ${_lib})
get_filename_component(_libFile ${_lib} NAME_WE)
if (_libFile MATCHES \"^${CMAKE_SHARED_LIBRARY_PREFIX}(.*)\")
set(_libFile ${CMAKE_MATCH_1})
endif()
else()
set(_libFile ${_lib})
endif()
string(REGEX REPLACE "[^_A-Za-z0-9]" "_" _cmake_lib_name ${_libFile})
if (NOT TARGET Qt5::Gui_${_cmake_lib_name} AND NOT _Qt5Gui_${_cmake_lib_name}_LIBRARY_DONE)
find_library(Qt5Gui_${_cmake_lib_name}_LIBRARY ${_lib}
if (IS_ABSOLUTE ${_lib})
set(Qt5Gui_${_cmake_lib_name}_LIBRARY ${_lib})
else()
find_library(Qt5Gui_${_cmake_lib_name}_LIBRARY ${_lib}
!!IF !isEmpty(CROSS_COMPILE)
PATHS \"${LibDir}\"
PATHS \"${LibDir}\"
!!IF !mac
NO_DEFAULT_PATH
NO_DEFAULT_PATH
!!ENDIF
!!ENDIF
)
)
endif()
!!IF mac
set(Qt5Gui_${_cmake_lib_name}_LIBRARY "${Qt5Gui_${_cmake_lib_name}_LIBRARY}/${_lib}")
if (NOT EXISTS "${Qt5Gui_${_cmake_lib_name}_LIBRARY}")

View File

@ -2259,16 +2259,16 @@ bool QImage::reinterpretAsFormat(Format format)
\sa convertToFormat()
*/
void QImage::convertTo(Format f, Qt::ImageConversionFlags flags)
void QImage::convertTo(Format format, Qt::ImageConversionFlags flags)
{
if (!d || f == QImage::Format_Invalid)
if (!d || format == QImage::Format_Invalid)
return;
detach();
if (convertToFormat_inplace(f, flags))
if (convertToFormat_inplace(format, flags))
return;
*this = convertToFormat_helper(f, flags);
*this = convertToFormat_helper(format, flags);
}
/*!

View File

@ -95,9 +95,9 @@ public:
void fill(const QColor &fillColor = Qt::white);
#if QT_DEPRECATED_SINCE(5, 13)
QT_DEPRECATED_X(" Use QPainter or the fill(QColor)")
QT_DEPRECATED_X("Use QPainter or fill(QColor)")
void fill(const QPaintDevice *device, const QPoint &ofs);
QT_DEPRECATED_X(" Use QPainter or the fill(QColor)")
QT_DEPRECATED_X("Use QPainter or fill(QColor)")
void fill(const QPaintDevice *device, int xofs, int yofs);
#endif

View File

@ -114,6 +114,18 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag
externalFormat = GL_BGRA;
internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_INT_8_8_8_8_REV;
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
// Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian:
} else if (funcs->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat) && !sRgbBinding) {
// The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external.
externalFormat = internalFormat = GL_BGRA;
pixelType = GL_UNSIGNED_BYTE;
} else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) {
// Is only allowed as an external format like OpenGL.
externalFormat = GL_BGRA;
internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
#endif
} else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) {
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
GLint swizzle[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA };
@ -125,25 +137,8 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag
externalFormat = internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
} else {
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
// Without GL_UNSIGNED_INT_8_8_8_8_REV, BGRA only matches ARGB on little endian.
if (funcs->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat) && !sRgbBinding) {
// The GL_EXT_texture_format_BGRA8888 extension requires the internal format to match the external.
externalFormat = internalFormat = GL_BGRA;
pixelType = GL_UNSIGNED_BYTE;
} else if (context->isOpenGLES() && context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) {
// Is only allowed as an external format like OpenGL.
externalFormat = GL_BGRA;
internalFormat = GL_RGBA;
pixelType = GL_UNSIGNED_BYTE;
} else {
// No support for direct ARGB32 upload.
break;
}
#else
// Big endian requires GL_UNSIGNED_INT_8_8_8_8_REV for ARGB to match BGRA
// No support for direct ARGB32 upload.
break;
#endif
}
targetFormat = image.format();
break;

View File

@ -333,7 +333,9 @@ static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qr
*shifted = QBezier::fromPoints(points_shifted[map[0]], points_shifted[map[1]],
points_shifted[map[2]], points_shifted[map[3]]);
return good_offset(orig, shifted, offset, threshold);
if (np > 2)
return good_offset(orig, shifted, offset, threshold);
return Ok;
}
// This value is used to determine the length of control point vectors
@ -432,7 +434,6 @@ redo:
} else if (res == Ok) {
++o;
--b;
continue;
} else if (res == Circle && maxSegments - (o - curveSegments) >= 2) {
// add semi circle
if (addCircle(b, offset, o))

View File

@ -1421,7 +1421,7 @@ QTextCharFormat::QTextCharFormat(const QTextFormat &fmt)
\fn void QTextCharFormat::setFontStyleName(const QString &styleName)
\since 5.13
Sets the text format's font \a style name.
Sets the text format's font \a styleName.
\sa setFont(), QFont::setStyleName()
*/

View File

@ -42,6 +42,7 @@
#include <QtCore/qdebug.h>
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <limits>
@ -61,7 +62,7 @@ HeaderSize entry_size(const QByteArray &name, const QByteArray &value)
// for counting the number of references to the name and value would have
// 32 octets of overhead."
const unsigned sum = unsigned(name.size()) + value.size();
const unsigned sum = unsigned(name.size() + value.size());
if (std::numeric_limits<unsigned>::max() - 32 < sum)
return HeaderSize();
return HeaderSize(true, quint32(sum + 32));
@ -75,7 +76,7 @@ int compare(const QByteArray &lhs, const QByteArray &rhs)
if (const int minLen = std::min(lhs.size(), rhs.size())) {
// We use memcmp, since strings in headers are allowed
// to contain '\0'.
const int cmp = std::memcmp(lhs.constData(), rhs.constData(), minLen);
const int cmp = std::memcmp(lhs.constData(), rhs.constData(), std::size_t(minLen));
if (cmp)
return cmp;
}
@ -138,82 +139,6 @@ bool FieldLookupTable::SearchEntry::operator < (const SearchEntry &rhs)const
return offset > rhs.offset;
}
// This data is from HPACK's specs and it's quite
// conveniently sorted == works with binary search as it is.
// Later this can probably change and instead of simple
// vector we'll just reuse FieldLookupTable.
// TODO: it makes sense to generate this table while ...
// configuring/building Qt (some script downloading/parsing/generating
// would be quite handy).
const std::vector<HeaderField> &staticTable()
{
static std::vector<HeaderField> table = {
{":authority", ""},
{":method", "GET"},
{":method", "POST"},
{":path", "/"},
{":path", "/index.html"},
{":scheme", "http"},
{":scheme", "https"},
{":status", "200"},
{":status", "204"},
{":status", "206"},
{":status", "304"},
{":status", "400"},
{":status", "404"},
{":status", "500"},
{"accept-charset", ""},
{"accept-encoding", "gzip, deflate"},
{"accept-language", ""},
{"accept-ranges", ""},
{"accept", ""},
{"access-control-allow-origin", ""},
{"age", ""},
{"allow", ""},
{"authorization", ""},
{"cache-control", ""},
{"content-disposition", ""},
{"content-encoding", ""},
{"content-language", ""},
{"content-length", ""},
{"content-location", ""},
{"content-range", ""},
{"content-type", ""},
{"cookie", ""},
{"date", ""},
{"etag", ""},
{"expect", ""},
{"expires", ""},
{"from", ""},
{"host", ""},
{"if-match", ""},
{"if-modified-since", ""},
{"if-none-match", ""},
{"if-range", ""},
{"if-unmodified-since", ""},
{"last-modified", ""},
{"link", ""},
{"location", ""},
{"max-forwards", ""},
{"proxy-authenticate", ""},
{"proxy-authorization", ""},
{"range", ""},
{"referer", ""},
{"refresh", ""},
{"retry-after", ""},
{"server", ""},
{"set-cookie", ""},
{"strict-transport-security", ""},
{"transfer-encoding", ""},
{"user-agent", ""},
{"vary", ""},
{"via", ""},
{"www-authenticate", ""}
};
return table;
}
FieldLookupTable::FieldLookupTable(quint32 maxSize, bool use)
: maxTableSize(maxSize),
tableCapacity(maxSize),
@ -296,12 +221,12 @@ void FieldLookupTable::evictEntry()
quint32 FieldLookupTable::numberOfEntries() const
{
return quint32(staticTable().size()) + nDynamic;
return quint32(staticPart().size()) + nDynamic;
}
quint32 FieldLookupTable::numberOfStaticEntries() const
{
return quint32(staticTable().size());
return quint32(staticPart().size());
}
quint32 FieldLookupTable::numberOfDynamicEntries() const
@ -326,24 +251,18 @@ void FieldLookupTable::clearDynamicTable()
bool FieldLookupTable::indexIsValid(quint32 index) const
{
return index && index <= staticTable().size() + nDynamic;
return index && index <= staticPart().size() + nDynamic;
}
quint32 FieldLookupTable::indexOf(const QByteArray &name, const QByteArray &value)const
{
// Start from the static part first:
const auto &table = staticTable();
const auto &table = staticPart();
const HeaderField field(name, value);
const auto staticPos = std::lower_bound(table.begin(), table.end(), field,
[](const HeaderField &lhs, const HeaderField &rhs) {
int cmp = compare(lhs.name, rhs.name);
if (cmp)
return cmp < 0;
return compare(lhs.value, rhs.value) < 0;
});
const auto staticPos = findInStaticPart(field, CompareMode::nameAndValue);
if (staticPos != table.end()) {
if (staticPos->name == name && staticPos->value == value)
return staticPos - table.begin() + 1;
return quint32(staticPos - table.begin() + 1);
}
// Now we have to lookup in our dynamic part ...
@ -366,15 +285,12 @@ quint32 FieldLookupTable::indexOf(const QByteArray &name, const QByteArray &valu
quint32 FieldLookupTable::indexOf(const QByteArray &name) const
{
// Start from the static part first:
const auto &table = staticTable();
const auto &table = staticPart();
const HeaderField field(name, QByteArray());
const auto staticPos = std::lower_bound(table.begin(), table.end(), field,
[](const HeaderField &lhs, const HeaderField &rhs) {
return compare(lhs.name, rhs.name) < 0;
});
const auto staticPos = findInStaticPart(field, CompareMode::nameOnly);
if (staticPos != table.end()) {
if (staticPos->name == name)
return staticPos - table.begin() + 1;
return quint32(staticPos - table.begin() + 1);
}
// Now we have to lookup in our dynamic part ...
@ -402,7 +318,7 @@ bool FieldLookupTable::field(quint32 index, QByteArray *name, QByteArray *value)
if (!indexIsValid(index))
return false;
const auto &table = staticTable();
const auto &table = staticPart();
if (index - 1 < table.size()) {
*name = table[index - 1].name;
*value = table[index - 1].value;
@ -477,7 +393,7 @@ quint32 FieldLookupTable::keyToIndex(const SearchEntry &key) const
Q_ASSERT(offset < ChunkSize);
Q_ASSERT(chunkIndex || offset >= begin);
return quint32(offset + chunkIndex * ChunkSize - begin + 1 + staticTable().size());
return quint32(offset + chunkIndex * ChunkSize - begin + 1 + staticPart().size());
}
FieldLookupTable::SearchEntry FieldLookupTable::frontKey() const
@ -526,6 +442,103 @@ void FieldLookupTable::setMaxDynamicTableSize(quint32 size)
updateDynamicTableSize(size);
}
// This data is from the HPACK's specs and it's quite conveniently sorted,
// except ... 'accept' is in the wrong position, see how we handle it below.
const std::vector<HeaderField> &FieldLookupTable::staticPart()
{
static std::vector<HeaderField> table = {
{":authority", ""},
{":method", "GET"},
{":method", "POST"},
{":path", "/"},
{":path", "/index.html"},
{":scheme", "http"},
{":scheme", "https"},
{":status", "200"},
{":status", "204"},
{":status", "206"},
{":status", "304"},
{":status", "400"},
{":status", "404"},
{":status", "500"},
{"accept-charset", ""},
{"accept-encoding", "gzip, deflate"},
{"accept-language", ""},
{"accept-ranges", ""},
{"accept", ""},
{"access-control-allow-origin", ""},
{"age", ""},
{"allow", ""},
{"authorization", ""},
{"cache-control", ""},
{"content-disposition", ""},
{"content-encoding", ""},
{"content-language", ""},
{"content-length", ""},
{"content-location", ""},
{"content-range", ""},
{"content-type", ""},
{"cookie", ""},
{"date", ""},
{"etag", ""},
{"expect", ""},
{"expires", ""},
{"from", ""},
{"host", ""},
{"if-match", ""},
{"if-modified-since", ""},
{"if-none-match", ""},
{"if-range", ""},
{"if-unmodified-since", ""},
{"last-modified", ""},
{"link", ""},
{"location", ""},
{"max-forwards", ""},
{"proxy-authenticate", ""},
{"proxy-authorization", ""},
{"range", ""},
{"referer", ""},
{"refresh", ""},
{"retry-after", ""},
{"server", ""},
{"set-cookie", ""},
{"strict-transport-security", ""},
{"transfer-encoding", ""},
{"user-agent", ""},
{"vary", ""},
{"via", ""},
{"www-authenticate", ""}
};
return table;
}
std::vector<HeaderField>::const_iterator FieldLookupTable::findInStaticPart(const HeaderField &field, CompareMode mode)
{
const auto &table = staticPart();
const auto acceptPos = table.begin() + 18;
if (field.name == "accept") {
if (mode == CompareMode::nameAndValue && field.value != "")
return table.end();
return acceptPos;
}
auto predicate = [mode](const HeaderField &lhs, const HeaderField &rhs) {
const int cmp = compare(lhs.name, rhs.name);
if (cmp)
return cmp < 0;
else if (mode == CompareMode::nameAndValue)
return compare(lhs.value, rhs.value) < 0;
return false;
};
const auto staticPos = std::lower_bound(table.begin(), acceptPos, field, predicate);
if (staticPos != acceptPos)
return staticPos;
return std::lower_bound(acceptPos + 1, table.end(), field, predicate);
}
}
QT_END_NAMESPACE

View File

@ -173,6 +173,8 @@ public:
bool updateDynamicTableSize(quint32 size);
void setMaxDynamicTableSize(quint32 size);
static const std::vector<HeaderField> &staticPart();
private:
// Table's maximum size is controlled
// by SETTINGS_HEADER_TABLE_SIZE (HTTP/2, 6.5.2).
@ -225,9 +227,16 @@ private:
quint32 indexOfChunk(const Chunk *chunk) const;
quint32 keyToIndex(const SearchEntry &key) const;
enum class CompareMode {
nameOnly,
nameAndValue
};
static std::vector<HeaderField>::const_iterator findInStaticPart(const HeaderField &field, CompareMode mode);
mutable QByteArray dummyDst;
Q_DISABLE_COPY_MOVE(FieldLookupTable);
Q_DISABLE_COPY_MOVE(FieldLookupTable)
};
}

View File

@ -265,7 +265,7 @@ qint64 QNetworkReplyWasmImpl::readData(char *data, qint64 maxlen)
Q_D(QNetworkReplyWasmImpl);
qint64 howMuch = qMin(maxlen, (d->downloadBuffer.size() - d->downloadBufferReadPosition));
memcpy(data, d->downloadBuffer.constData(), howMuch);
memcpy(data, d->downloadBuffer.constData() + d->downloadBufferReadPosition, howMuch);
d->downloadBufferReadPosition += howMuch;
return howMuch;

View File

@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QOcspResponse
\brief This class represents Online Certificate Status Protocol response
\brief This class represents Online Certificate Status Protocol response.
\since 5.13
\ingroup network
@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE
received by the client-side socket during the TLS handshake. QSslSocket must be
configured with OCSP stapling enabled.
\sa QSslSocket, QSslSocket::ocspResponse(), certificateStatus(),
\sa QSslSocket, QSslSocket::ocspResponses(), certificateStatus(),
revocationReason(), responder(), subject(), QOcspCertificateStatus, QOcspRevocationReason,
QSslConfiguration::setOcspStaplingEnabled(), QSslConfiguration::ocspStaplingEnabled(),
QSslConfiguration::peerCertificate()
@ -126,14 +126,14 @@ QOcspResponse::QOcspResponse()
Creates a new response, the copy of \a other.
*/
QOcspResponse::QOcspResponse(const QOcspResponse &other) = default;
QOcspResponse::QOcspResponse(const QOcspResponse &) = default;
/*!
\since 5.13
Move-constructs a QOcspResponse instance from \a other.
*/
QOcspResponse::QOcspResponse(QOcspResponse &&other) Q_DECL_NOTHROW = default;
QOcspResponse::QOcspResponse(QOcspResponse &&) Q_DECL_NOTHROW = default;
/*!
\since 5.13
@ -147,14 +147,14 @@ QOcspResponse::~QOcspResponse() = default;
Assigns \a other to the response and returns a reference to this response.
*/
QOcspResponse &QOcspResponse::operator=(const QOcspResponse &other) = default;
QOcspResponse &QOcspResponse::operator=(const QOcspResponse &) = default;
/*!
\since 5.13
Move-assigns \a other to this QOcspResponse instance.
*/
QOcspResponse &QOcspResponse::operator=(QOcspResponse &&other) Q_DECL_NOTHROW = default;
QOcspResponse &QOcspResponse::operator=(QOcspResponse &&) Q_DECL_NOTHROW = default;
/*!
\fn void QOcspResponse::swap(QOcspResponse &other)

View File

@ -179,7 +179,15 @@ static inline bool checkNeedPortalSupport()
return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty() || qEnvironmentVariableIsSet("SNAP");
}
static inline bool xdgDesktopPortalOpenFile(const QUrl &url)
static inline bool isPortalReturnPermanent(const QDBusError &error)
{
// A service unknown error isn't permanent, it just indicates that we
// should fall back to the regular way. This check includes
// QDBusError::NoError.
return error.type() != QDBusError::ServiceUnknown;
}
static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
{
// DBus signature:
// OpenFile (IN s parent_window,
@ -198,23 +206,22 @@ static inline bool xdgDesktopPortalOpenFile(const QUrl &url)
QLatin1String("org.freedesktop.portal.OpenURI"),
QLatin1String("OpenFile"));
QDBusUnixFileDescriptor descriptor(fd);
qt_safe_close(fd);
QDBusUnixFileDescriptor descriptor;
descriptor.giveFileDescriptor(fd);
// FIXME parent_window_id and handle writable option
message << QString() << QVariant::fromValue(descriptor) << QVariantMap();
QDBusPendingReply<QDBusObjectPath> reply = QDBusConnection::sessionBus().call(message);
return !reply.isError();
return QDBusConnection::sessionBus().call(message);
}
#else
Q_UNUSED(url)
#endif
return false;
return QDBusMessage::createError(QDBusError::InternalError, qt_error_string());
}
static inline bool xdgDesktopPortalOpenUrl(const QUrl &url)
static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url)
{
// DBus signature:
// OpenURI (IN s parent_window,
@ -234,11 +241,10 @@ static inline bool xdgDesktopPortalOpenUrl(const QUrl &url)
// FIXME parent_window_id and handle writable option
message << QString() << url.toString() << QVariantMap();
QDBusPendingReply<QDBusObjectPath> reply = QDBusConnection::sessionBus().call(message);
return !reply.isError();
return QDBusConnection::sessionBus().call(message);
}
static inline bool xdgDesktopPortalSendEmail(const QUrl &url)
static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url)
{
// DBus signature:
// ComposeEmail (IN s parent_window,
@ -281,8 +287,7 @@ static inline bool xdgDesktopPortalSendEmail(const QUrl &url)
// FIXME parent_window_id
message << QString() << options;
QDBusPendingReply<QDBusObjectPath> reply = QDBusConnection::sessionBus().call(message);
return !reply.isError();
return QDBusConnection::sessionBus().call(message);
}
#endif // QT_CONFIG(dbus)
@ -296,15 +301,23 @@ bool QGenericUnixServices::openUrl(const QUrl &url)
{
if (url.scheme() == QLatin1String("mailto")) {
#if QT_CONFIG(dbus)
if (checkNeedPortalSupport())
return xdgDesktopPortalSendEmail(url);
if (checkNeedPortalSupport()) {
QDBusError error = xdgDesktopPortalSendEmail(url);
if (isPortalReturnPermanent(error))
return !error.isValid();
// service not running, fall back
}
#endif
return openDocument(url);
}
#if QT_CONFIG(dbus)
if (checkNeedPortalSupport())
return xdgDesktopPortalOpenUrl(url);
if (checkNeedPortalSupport()) {
QDBusError error = xdgDesktopPortalOpenUrl(url);
if (isPortalReturnPermanent(error))
return !error.isValid();
}
#endif
if (m_webBrowser.isEmpty() && !detectWebBrowser(desktopEnvironment(), true, &m_webBrowser)) {
@ -317,8 +330,11 @@ bool QGenericUnixServices::openUrl(const QUrl &url)
bool QGenericUnixServices::openDocument(const QUrl &url)
{
#if QT_CONFIG(dbus)
if (checkNeedPortalSupport())
return xdgDesktopPortalOpenFile(url);
if (checkNeedPortalSupport()) {
QDBusError error = xdgDesktopPortalOpenFile(url);
if (isPortalReturnPermanent(error))
return !error.isValid();
}
#endif
if (m_documentLauncher.isEmpty() && !detectWebBrowser(desktopEnvironment(), false, &m_documentLauncher)) {

View File

@ -70,7 +70,9 @@ void QComposeInputContext::ensureInitialized()
}
m_initialized = true;
const char *const locale = setlocale(LC_CTYPE, "");
const char *locale = setlocale(LC_CTYPE, "");
if (!locale)
locale = setlocale(LC_CTYPE, nullptr);
qCDebug(lcXkbCompose) << "detected locale (LC_CTYPE):" << locale;
m_composeTable = xkb_compose_table_new_from_locale(m_XkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);

View File

@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing");
Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse");
Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg);
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen");
//

View File

@ -137,22 +137,13 @@
{
if ((self = [super initWithFrame:NSZeroRect])) {
m_platformWindow = platformWindow;
m_buttons = Qt::NoButton;
m_acceptedMouseDowns = Qt::NoButton;
m_frameStrutButtons = Qt::NoButton;
m_sendKeyEvent = false;
m_sendUpAsRightButton = false;
m_inputSource = nil;
m_mouseMoveHelper = [[QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) alloc] initWithView:self];
m_resendKeyEvent = false;
m_scrolling = false;
m_updatingDrag = false;
m_currentlyInterpretedKeyEvent = nil;
m_dontOverrideCtrlLMB = qt_mac_resolveOption(false, platformWindow->window(),
"_q_platform_MacDontOverrideCtrlLMB", "QT_MAC_DONT_OVERRIDE_CTRL_LMB");
self.focusRingType = NSFocusRingTypeNone;
self.cursor = nil;
self.previousSuperview = nil;
self.previousWindow = nil;

View File

@ -57,9 +57,9 @@
NSFilesPromisePboardType, NSInkTextPboardType,
NSMultipleTextSelectionPboardType, mimeTypeGeneric]];
// Add custom types supported by the application.
// Add custom types supported by the application
for (const QString &customType : qt_mac_enabledDraggedTypes())
[supportedTypes addObject:customType.toNSString()];
[supportedTypes addObject:customType.toNSString()];
[self registerForDraggedTypes:supportedTypes];
}
@ -79,11 +79,11 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
return target->mapFromGlobal(source->mapToGlobal(point));
}
- (NSDragOperation)draggingSession:(NSDraggingSession *)session
sourceOperationMaskForDraggingContext:(NSDraggingContext)context
- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
{
Q_UNUSED(session);
Q_UNUSED(context);
QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions());
}
@ -134,30 +134,29 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
if (pixmapCursor.isNull()) {
switch (response.acceptedAction()) {
case Qt::CopyAction:
nativeCursor = [NSCursor dragCopyCursor];
break;
case Qt::LinkAction:
nativeCursor = [NSCursor dragLinkCursor];
break;
case Qt::IgnoreAction:
// Uncomment the next lines if forbiden cursor wanted on non droppable targets.
/*nativeCursor = [NSCursor operationNotAllowedCursor];
break;*/
case Qt::MoveAction:
default:
nativeCursor = [NSCursor arrowCursor];
break;
case Qt::CopyAction:
nativeCursor = [NSCursor dragCopyCursor];
break;
case Qt::LinkAction:
nativeCursor = [NSCursor dragLinkCursor];
break;
case Qt::IgnoreAction:
// Uncomment the next lines if forbidden cursor is wanted on undroppable targets.
/*nativeCursor = [NSCursor operationNotAllowedCursor];
break;*/
case Qt::MoveAction:
default:
nativeCursor = [NSCursor arrowCursor];
break;
}
}
else {
} else {
NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor);
nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize());
nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint];
[nsimage release];
}
// change the cursor
// Change the cursor
[nativeCursor set];
// Make sure the cursor is updated correctly if the mouse does not move and window is under cursor
@ -169,39 +168,33 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
if (m_updatingDrag)
return;
const QPoint mousePos(QCursor::pos());
CGEventRef moveEvent(CGEventCreateMouseEvent(
NULL, kCGEventMouseMoved,
CGPointMake(mousePos.x(), mousePos.y()),
QCFType<CGEventRef> moveEvent = CGEventCreateMouseEvent(
nullptr, kCGEventMouseMoved, QCursor::pos().toCGPoint(),
kCGMouseButtonLeft // ignored
));
);
CGEventPost(kCGHIDEventTap, moveEvent);
CFRelease(moveEvent);
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
return [self handleDrag : sender];
return [self handleDrag:(QEvent::DragEnter) sender:sender];
}
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
{
m_updatingDrag = true;
const NSDragOperation ret([self handleDrag : sender]);
m_updatingDrag = false;
return ret;
QScopedValueRollback<bool> rollback(m_updatingDrag, true);
return [self handleDrag:(QEvent::DragMove) sender:sender];
}
// Sends drag update to Qt, return the action
- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender
- (NSDragOperation)handleDrag:(QEvent::Type)dragType sender:(id<NSDraggingInfo>)sender
{
if (!m_platformWindow)
return NSDragOperationNone;
NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint();
Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(sender.draggingSourceOperationMask);
QWindow *target = findEventTargetWindow(m_platformWindow->window());
if (!target)
@ -209,7 +202,12 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
const auto modifiers = [QNSView convertKeyModifiers:NSApp.currentEvent.modifierFlags];
const auto buttons = currentlyPressedMouseButtons();
const auto point = mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint);
const auto point = mapWindowCoordinates(m_platformWindow->window(), target, windowPoint);
if (dragType == QEvent::DragEnter)
qCDebug(lcQpaMouse) << dragType << self << "at" << windowPoint;
else
qCDebug(lcQpaMouse) << dragType << "at" << windowPoint << "with" << buttons;
QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect());
QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
@ -219,7 +217,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
point, qtAllowed, buttons, modifiers);
[self updateCursorFromDragResponse:response drag:nativeDrag];
} else {
QCocoaDropData mimeData([sender draggingPasteboard]);
QCocoaDropData mimeData(sender.draggingPasteboard);
response = QWindowSystemInterface::handleDrag(target, &mimeData,
point, qtAllowed, buttons, modifiers);
}
@ -227,7 +225,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
return qt_mac_mapDropAction(response.acceptedAction());
}
- (void)draggingExited:(id <NSDraggingInfo>)sender
- (void)draggingExited:(id<NSDraggingInfo>)sender
{
if (!m_platformWindow)
return;
@ -236,17 +234,18 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
if (!target)
return;
NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint();
qCDebug(lcQpaMouse) << QEvent::DragLeave << self << "at" << windowPoint;
// Send 0 mime data to indicate drag exit
QWindowSystemInterface::handleDrag(target, nullptr,
mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint),
mapWindowCoordinates(m_platformWindow->window(), target, windowPoint),
Qt::IgnoreAction, Qt::NoButton, Qt::NoModifier);
}
// called on drop, send the drop to Qt and return if it was accepted.
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
// Called on drop, send the drop to Qt and return if it was accepted
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
{
if (!m_platformWindow)
return false;
@ -255,31 +254,31 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
if (!target)
return false;
NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint();
Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(sender.draggingSourceOperationMask);
QPlatformDropQtResponse response(false, Qt::IgnoreAction);
QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
const auto modifiers = [QNSView convertKeyModifiers:NSApp.currentEvent.modifierFlags];
const auto buttons = currentlyPressedMouseButtons();
const auto point = mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint);
const auto point = mapWindowCoordinates(m_platformWindow->window(), target, windowPoint);
qCDebug(lcQpaMouse) << QEvent::Drop << "at" << windowPoint << "with" << buttons;
if (nativeDrag->currentDrag()) {
// The drag was started from within the application
response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(),
point, qtAllowed, buttons, modifiers);
} else {
QCocoaDropData mimeData([sender draggingPasteboard]);
QCocoaDropData mimeData(sender.draggingPasteboard);
response = QWindowSystemInterface::handleDrop(target, &mimeData,
point, qtAllowed, buttons, modifiers);
}
return response.isAccepted();
}
- (void)draggingSession:(NSDraggingSession *)session
endedAtPoint:(NSPoint)screenPoint
operation:(NSDragOperation)operation
- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
{
Q_UNUSED(session);
Q_UNUSED(screenPoint);
@ -295,6 +294,8 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation));
m_buttons = currentlyPressedMouseButtons();
qCDebug(lcQpaMouse) << "Drag session" << session << "ended, with" << m_buttons;
}
@end

View File

@ -93,6 +93,7 @@
- (void)resetMouseButtons
{
qCDebug(lcQpaMouse) << "Reseting mouse buttons";
m_buttons = Qt::NoButton;
m_frameStrutButtons = Qt::NoButton;
}
@ -145,6 +146,9 @@
QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint();
ulong timestamp = [theEvent timestamp] * 1000;
auto eventType = cocoaEvent2QtMouseEvent(theEvent);
qCInfo(lcQpaMouse) << "Frame-strut" << eventType << "at" << qtWindowPoint << "with" << m_frameStrutButtons << "in" << self.window;
QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons);
}
@end
@ -153,6 +157,19 @@
- (void)initMouse
{
m_buttons = Qt::NoButton;
m_acceptedMouseDowns = Qt::NoButton;
m_frameStrutButtons = Qt::NoButton;
m_scrolling = false;
self.cursor = nil;
m_sendUpAsRightButton = false;
m_dontOverrideCtrlLMB = qt_mac_resolveOption(false, m_platformWindow->window(),
"_q_platform_MacDontOverrideCtrlLMB", "QT_MAC_DONT_OVERRIDE_CTRL_LMB");
m_mouseMoveHelper = [[QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) alloc] initWithView:self];
NSUInteger trackingOptions = NSTrackingActiveInActiveApp
| NSTrackingMouseEnteredAndExited | NSTrackingCursorUpdate;
@ -241,6 +258,11 @@
button = Qt::RightButton;
const auto eventType = cocoaEvent2QtMouseEvent(theEvent);
if (eventType == QEvent::MouseMove)
qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << buttons;
else
qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << buttons;
QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(),
timestamp, qtWindowPoint, qtScreenPoint,
buttons, button, eventType, modifiers);
@ -446,16 +468,16 @@
- (void)cursorUpdate:(NSEvent *)theEvent
{
qCDebug(lcQpaMouse) << "Updating cursor for" << self << "to" << self.cursor;
// Note: We do not get this callback when moving from a subview that
// uses the legacy cursorRect API, so the cursor is reset to the arrow
// cursor. See rdar://34183708
if (self.cursor)
if (self.cursor && self.cursor != NSCursor.currentCursor) {
qCInfo(lcQpaMouse) << "Updating cursor for" << self << "to" << self.cursor;
[self.cursor set];
else
} else {
[super cursorUpdate:theEvent];
}
}
- (void)mouseMovedImpl:(NSEvent *)theEvent
@ -510,6 +532,8 @@
QPointF screenPoint;
[self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
m_platformWindow->m_enterLeaveTargetWindow = m_platformWindow->childWindowAt(windowPoint.toPoint());
qCInfo(lcQpaMouse) << QEvent::Enter << self << "at" << windowPoint << "with" << currentlyPressedMouseButtons();
QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint);
}
@ -528,6 +552,7 @@
if (!m_platformWindow->isContentView())
return;
qCInfo(lcQpaMouse) << QEvent::Leave << self;
QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow);
m_platformWindow->m_enterLeaveTargetWindow = 0;
}
@ -626,8 +651,10 @@
// "isInverted": natural OS X scrolling, inverted from the Qt/other platform/Jens perspective.
bool isInverted = [theEvent isDirectionInvertedFromDevice];
qCDebug(lcQpaMouse) << "scroll wheel @ window pos" << qt_windowPoint << "delta px" << pixelDelta
<< "angle" << angleDelta << "phase" << phase << (isInverted ? "inverted" : "");
qCInfo(lcQpaMouse).nospace() << phase << " at " << qt_windowPoint
<< " pixelDelta=" << pixelDelta << " angleDelta=" << angleDelta
<< (isInverted ? " inverted=true" : "");
QWindowSystemInterface::handleWheelEvent(m_platformWindow->window(), qt_timestamp, qt_windowPoint,
qt_screenPoint, pixelDelta, angleDelta, m_currentWheelModifiers, phase, source, isInverted);
}

View File

@ -50,6 +50,7 @@
// External mode.usage:
//
// var config = {
// canvasElements : [$("canvas-id")],
// showLoader: function() {
// loader.style.display = 'block'
// canvas.style.display = 'hidden'
@ -69,6 +70,8 @@
// One or more HTML elements. QtLoader will display loader elements
// on these while loading the applicaton, and replace the loader with a
// canvas on load complete.
// canvasElements : [canvas-element, ...]
// One or more canvas elements.
// showLoader : function(status, containerElement)
// Optional loading element constructor function. Implement to create
// a custom loading screen. This function may be called multiple times,
@ -146,8 +149,25 @@ function QtLoader(config)
while (element.firstChild) element.removeChild(element.firstChild);
}
// Set default state handler functions if needed
function createCanvas() {
var canvas = document.createElement("canvas");
canvas.className = "QtCanvas";
canvas.style.height = "100%";
canvas.style.width = "100%";
// Set contentEditable in order to enable clipboard events; hide the resulting focus frame.
canvas.contentEditable = true;
canvas.style.outline = "0px solid transparent";
canvas.style.cursor = "default";
return canvas;
}
// Set default state handler functions and create canvases if needed
if (config.containerElements !== undefined) {
config.canvasElements = config.containerElements.map(createCanvas);
config.showError = config.showError || function(errorText, container) {
removeChildren(container);
var errorTextElement = document.createElement("text");
@ -164,12 +184,8 @@ function QtLoader(config)
return loadingText;
};
config.showCanvas = config.showCanvas || function(container) {
config.showCanvas = config.showCanvas || function(canvas, container) {
removeChildren(container);
var canvas = document.createElement("canvas");
canvas.className = "QtCanvas"
canvas.style = "height: 100%; width: 100%;"
return canvas;
}
config.showExit = config.showExit || function(crashed, exitCode, container) {
@ -384,6 +400,8 @@ function QtLoader(config)
Module.mainScriptUrlOrBlob = new Blob([emscriptenModuleSource], {type: 'text/javascript'});
Module.qtCanvasElements = config.canvasElements;
config.restart = function() {
// Restart by reloading the page. This will wipe all state which means
@ -438,19 +456,17 @@ function QtLoader(config)
}
function setCanvasContent() {
var firstCanvas;
if (config.containerElements === undefined) {
firstCanvas = config.showCanvas();
} else {
for (container of config.containerElements) {
var canvasElement = config.showCanvas(container);
container.appendChild(canvasElement);
}
firstCanvas = config.containerElements[0].firstChild;
if (config.showCanvas !== undefined)
config.showCanvas();
return;
}
if (Module.canvas === undefined) {
Module.canvas = firstCanvas;
for (var i = 0; i < config.containerElements.length; ++i) {
var container = config.containerElements[i];
var canvas = config.canvasElements[i];
config.showCanvas(canvas, container);
container.appendChild(canvas);
}
}

View File

@ -67,23 +67,42 @@ static void qClipboardPromiseResolve(emscripten::val something)
pasteClipboardData(emscripten::val("text/plain"), something);
}
static void qClipboardCopyTo(val event)
static void qClipboardCutTo(val event)
{
val target = event["target"];
val clipboard = event["clipboardData"];
if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
// Send synthetic Ctrl+X to make the app cut data to Qt's clipboard
QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
0, QEvent::KeyPress, Qt::Key_X, Qt::ControlModifier, "X");
}
val module = val::global("Module");
val clipdata = module.call<val>("getClipboardData");
val clipFormat = module.call<val>("getClipboardFormat");
clipboard.call<void>("setData", clipFormat, clipdata);
target.call<void>("preventDefault");
event["clipboardData"].call<void>("setData", clipFormat, clipdata);
event.call<void>("preventDefault");
}
static void qClipboardCopyTo(val event)
{
if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
// Send synthetic Ctrl+C to make the app copy data to Qt's clipboard
QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C");
}
val module = val::global("Module");
val clipdata = module.call<val>("getClipboardData");
val clipFormat = module.call<val>("getClipboardFormat");
event["clipboardData"].call<void>("setData", clipFormat, clipdata);
event.call<void>("preventDefault");
}
static void qClipboardPasteTo(val event)
{
val target = event["clipboardData"];
val module = val::global("Module");
val clipdata = module.call<val>("getClipboardData");
bool hasClipboardApi = QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi;
val clipdata = hasClipboardApi ?
val::global("Module").call<val>("getClipboardData") :
event["clipboardData"].call<val>("getData", std::string("text"));
const std::string data = clipdata.as<std::string>();
if (data.length() > 0) {
@ -99,13 +118,16 @@ EMSCRIPTEN_BINDINGS(clipboard_module) {
function("getClipboardFormat", &getClipboardFormat);
function("pasteClipboardData", &pasteClipboardData);
function("qClipboardPromiseResolve", &qClipboardPromiseResolve);
function("qClipboardCutTo", &qClipboardCutTo);
function("qClipboardCopyTo", &qClipboardCopyTo);
function("qClipboardPasteTo", &qClipboardPasteTo);
}
QWasmClipboard::QWasmClipboard() :
hasClipboardApi(false)
QWasmClipboard::QWasmClipboard()
{
val clipboard = val::global("navigator")["clipboard"];
hasClipboardApi = (!clipboard.isUndefined() && !clipboard["readText"].isUndefined());
initClipboardEvents();
}
@ -157,29 +179,32 @@ void QWasmClipboard::qWasmClipboardPaste(QMimeData *mData)
void QWasmClipboard::initClipboardEvents()
{
val navigator = val::global("navigator");
val permissions = navigator["permissions"];
val clipboard = navigator["clipboard"];
if (!hasClipboardApi)
return;
hasClipboardApi = (!clipboard.isUndefined());
if (hasClipboardApi) {
val readPermissionsMap = val::object();
readPermissionsMap.set("name", val("clipboard-read"));
permissions.call<val>("query", readPermissionsMap);
val permissions = val::global("navigator")["permissions"];
val readPermissionsMap = val::object();
readPermissionsMap.set("name", val("clipboard-read"));
permissions.call<val>("query", readPermissionsMap);
val writePermissionsMap = val::object();
writePermissionsMap.set("name", val("clipboard-write"));
permissions.call<val>("query", writePermissionsMap);
val writePermissionsMap = val::object();
writePermissionsMap.set("name", val("clipboard-write"));
permissions.call<val>("query", writePermissionsMap);
}
} else {
void QWasmClipboard::installEventHandlers(const QString &canvasId)
{
if (hasClipboardApi)
return;
val window = val::global("window");
window.call<void>("addEventListener", std::string("paste"),
val::module_property("qClipboardPasteTo"));
window.call<void>("addEventListener", std::string("copy"),
val::module_property("qClipboardCopyTo"));
}
// Fallback path for browsers which do not support direct clipboard access
val canvas = val::global(canvasId.toUtf8().constData());
canvas.call<void>("addEventListener", std::string("cut"),
val::module_property("qClipboardCutTo"));
canvas.call<void>("addEventListener", std::string("copy"),
val::module_property("qClipboardCopyTo"));
canvas.call<void>("addEventListener", std::string("paste"),
val::module_property("qClipboardPasteTo"));
}
void QWasmClipboard::readTextFromClipboard()

View File

@ -51,6 +51,7 @@ public:
static void qWasmClipboardPaste(QMimeData *mData);
void initClipboardEvents();
void installEventHandlers(const QString &canvasId);
bool hasClipboardApi;
void readTextFromClipboard();
void writeTextToClipboard();

View File

@ -56,8 +56,9 @@ QWasmCompositedWindow::QWasmCompositedWindow()
{
}
QWasmCompositor::QWasmCompositor()
: m_frameBuffer(nullptr)
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
:QObject(screen)
, m_frameBuffer(nullptr)
, m_blitter(new QOpenGLTextureBlitter)
, m_needComposit(false)
, m_inFlush(false)
@ -107,11 +108,6 @@ void QWasmCompositor::removeWindow(QWasmWindow *window)
notifyTopWindowChanged(window);
}
void QWasmCompositor::setScreen(QWasmScreen *screen)
{
m_screen = screen;
}
void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
{
QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
@ -654,7 +650,7 @@ void QWasmCompositor::frame()
m_needComposit = false;
if (m_windowStack.empty() || !m_screen)
if (m_windowStack.empty() || !screen())
return;
QWasmWindow *someWindow = nullptr;
@ -673,7 +669,7 @@ void QWasmCompositor::frame()
if (m_context.isNull()) {
m_context.reset(new QOpenGLContext());
//mContext->setFormat(mScreen->format());
m_context->setScreen(m_screen->screen());
m_context->setScreen(screen()->screen());
m_context->create();
}
@ -682,8 +678,8 @@ void QWasmCompositor::frame()
if (!m_blitter->isCreated())
m_blitter->create();
qreal dpr = m_screen->devicePixelRatio();
glViewport(0, 0, m_screen->geometry().width() * dpr, m_screen->geometry().height() * dpr);
qreal dpr = screen()->devicePixelRatio();
glViewport(0, 0, screen()->geometry().width() * dpr, screen()->geometry().height() * dpr);
m_context->functions()->glClearColor(0.2, 0.2, 0.2, 1.0);
m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
@ -697,7 +693,7 @@ void QWasmCompositor::frame()
if (!compositedWindow.visible)
continue;
drawWindow(m_blitter.data(), m_screen, window);
drawWindow(m_blitter.data(), screen(), window);
}
m_blitter->release();
@ -719,3 +715,8 @@ void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
requestRedraw();
QWindowSystemInterface::handleWindowActivated(window->window());
}
QWasmScreen *QWasmCompositor::screen()
{
return static_cast<QWasmScreen *>(parent());
}

View File

@ -62,7 +62,7 @@ class QWasmCompositor : public QObject
{
Q_OBJECT
public:
QWasmCompositor();
QWasmCompositor(QWasmScreen *screen);
~QWasmCompositor();
enum QWasmSubControl {
@ -103,7 +103,6 @@ public:
void addWindow(QWasmWindow *window, QWasmWindow *parentWindow = nullptr);
void removeWindow(QWasmWindow *window);
void setScreen(QWasmScreen *screen);
void setVisible(QWasmWindow *window, bool visible);
void raise(QWasmWindow *window);
@ -129,8 +128,7 @@ private slots:
void frame();
private:
void createFrameBuffer();
void flushCompletedCallback(int32_t);
QWasmScreen *screen();
void notifyTopWindowChanged(QWasmWindow *window);
void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
@ -142,7 +140,6 @@ private:
QImage *m_frameBuffer;
QScopedPointer<QOpenGLContext> m_context;
QScopedPointer<QOpenGLTextureBlitter> m_blitter;
QWasmScreen *m_screen;
QHash<QWasmWindow *, QWasmCompositedWindow> m_compositedWindows;
QList<QWasmWindow *> m_windowStack;

View File

@ -28,20 +28,21 @@
****************************************************************************/
#include "qwasmcursor.h"
#include "qwasmscreen.h"
#include <QtCore/qdebug.h>
#include <QtGui/qwindow.h>
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
{
if (windowCursor == nullptr)
if (!windowCursor || !window)
return;
QScreen *screen = window->screen();
if (!screen)
return;
// FIXME: The HTML5 plugin sets the cursor on the native canvas; when using multiple windows
// multiple cursors need to be managed taking mouse postion and stacking into account.
Q_UNUSED(window);
// Bitmap and custom cursors are not implemented (will fall back to "auto")
if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor)
@ -52,8 +53,9 @@ void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
if (htmlCursorName.isEmpty())
htmlCursorName = "auto";
// Set cursor on the main canvas
emscripten::val canvasStyle = emscripten::val::module_property("canvas")["style"];
// Set cursor on the canvas
QString canvasId = QWasmScreen::get(screen)->canvasId();
emscripten::val canvasStyle = emscripten::val::global(canvasId.toUtf8().constData())["style"];
canvasStyle.set("cursor", emscripten::val(htmlCursorName.constData()));
}

View File

@ -320,41 +320,35 @@ EMSCRIPTEN_BINDINGS(mouse_module) {
function("mouseWheelEvent", &mouseWheelEvent);
}
QWasmEventTranslator::QWasmEventTranslator(QObject *parent)
: QObject(parent)
QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen)
: QObject(screen)
, draggedWindow(nullptr)
, lastWindow(nullptr)
, pressedButtons(Qt::NoButton)
, resizeMode(QWasmWindow::ResizeNone)
{
emscripten_set_keydown_callback(0, (void *)this, 1, &keyboard_cb);
emscripten_set_keyup_callback(0, (void *)this, 1, &keyboard_cb);
emscripten_set_mousedown_callback(0, (void *)this, 1, &mouse_cb);
emscripten_set_mouseup_callback(0, (void *)this, 1, &mouse_cb);
emscripten_set_mousemove_callback(0, (void *)this, 1, &mouse_cb);
emscripten_set_focus_callback(0, (void *)this, 1, &focus_cb);
emscripten_set_wheel_callback(0, (void *)this, 1, &wheel_cb);
touchDevice = new QTouchDevice;
touchDevice->setType(QTouchDevice::TouchScreen);
touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition);
QWindowSystemInterface::registerTouchDevice(touchDevice);
emscripten_set_touchstart_callback("#canvas", (void *)this, 1, &touchCallback);
emscripten_set_touchend_callback("#canvas", (void *)this, 1, &touchCallback);
emscripten_set_touchmove_callback("#canvas", (void *)this, 1, &touchCallback);
emscripten_set_touchcancel_callback("#canvas", (void *)this, 1, &touchCallback);
initEventHandlers();
}
void QWasmEventTranslator::initEventHandlers()
{
qDebug() << "QWasmEventTranslator::initEventHandlers";
QByteArray _canvasId = screen()->canvasId().toUtf8();
const char *canvasId = _canvasId.constData();
// The Platform Detect: expand coverage and move as needed
enum Platform {
GenericPlatform,
MacOSPlatform
};
Platform platform = Platform(emscripten::val::global("navigator")["platform"]
.call<bool>("includes", emscripten::val("Mac")));
Platform platform = Platform(emscripten::val::global("navigator")["platform"]
.call<bool>("includes", emscripten::val("Mac")));
g_usePlatformMacCtrlMetaSwitching = (platform == MacOSPlatform);
if (platform == MacOSPlatform) {
@ -362,11 +356,30 @@ QWasmEventTranslator::QWasmEventTranslator(QObject *parent)
if (emscripten::val::global("window")["safari"].isUndefined()) {
emscripten::val::global("canvas").call<void>("addEventListener",
emscripten::val::global(canvasId).call<void>("addEventListener",
std::string("wheel"),
val::module_property("mouseWheelEvent"));
}
}
emscripten_set_keydown_callback(canvasId, (void *)this, 1, &keyboard_cb);
emscripten_set_keyup_callback(canvasId, (void *)this, 1, &keyboard_cb);
emscripten_set_mousedown_callback(canvasId, (void *)this, 1, &mouse_cb);
emscripten_set_mouseup_callback(canvasId, (void *)this, 1, &mouse_cb);
emscripten_set_mousemove_callback(canvasId, (void *)this, 1, &mouse_cb);
emscripten_set_focus_callback(canvasId, (void *)this, 1, &focus_cb);
emscripten_set_wheel_callback(canvasId, (void *)this, 1, &wheel_cb);
emscripten_set_touchstart_callback(canvasId, (void *)this, 1, &touchCallback);
emscripten_set_touchend_callback(canvasId, (void *)this, 1, &touchCallback);
emscripten_set_touchmove_callback(canvasId, (void *)this, 1, &touchCallback);
emscripten_set_touchcancel_callback(canvasId, (void *)this, 1, &touchCallback);
emscripten_set_resize_callback(nullptr, (void *)this, 1, uiEvent_cb); // Note: handles browser window resize
}
template <typename Event>
@ -415,6 +428,11 @@ int QWasmEventTranslator::keyboard_cb(int eventType, const EmscriptenKeyboardEve
return accepted ? 1 : 0;
}
QWasmScreen *QWasmEventTranslator::screen()
{
return static_cast<QWasmScreen *>(parent());
}
Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey)
{
Qt::Key qtKey = Qt::Key_unknown;
@ -531,14 +549,14 @@ void resizeWindow(QWindow *window, QWasmWindow::ResizeMode mode,
void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent)
{
auto timestamp = mouseEvent->timestamp;
QPoint point(mouseEvent->canvasX, mouseEvent->canvasY);
QPoint point(mouseEvent->targetX, mouseEvent->targetY);
QEvent::Type buttonEventType = QEvent::None;
Qt::MouseButton button = translateMouseButton(mouseEvent->button);
Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent);
QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5);
QWindow *window2 = screen()->compositor()->windowAt(point, 5);
if (window2 != nullptr)
lastWindow = window2;
@ -635,6 +653,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
{
Q_UNUSED(eventType)
QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
EmscriptenMouseEvent mouseEvent = wheelEvent->mouse;
int scrollFactor = 0;
@ -658,7 +677,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
auto timestamp = mouseEvent.timestamp;
QPoint globalPoint(mouseEvent.canvasX, mouseEvent.canvasY);
QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(globalPoint, 5);
QWindow *window2 = eventTranslator->screen()->compositor()->windowAt(globalPoint, 5);
QPoint localPoint(globalPoint.x() - window2->geometry().x(), globalPoint.y() - window2->geometry().y());
@ -676,6 +695,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
{
QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
QList<QWindowSystemInterface::TouchPoint> touchPointList;
touchPointList.reserve(touchEvent->numTouches);
QWindow *window2;
@ -685,7 +705,7 @@ int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEven
const EmscriptenTouchPoint *touches = &touchEvent->touches[i];
QPoint point(touches->canvasX, touches->canvasY);
window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5);
window2 = eventTranslator->screen()->compositor()->windowAt(point, 5);
QWindowSystemInterface::TouchPoint touchPoint;
@ -832,6 +852,14 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa
return 0;
QFlags<Qt::KeyboardModifier> mods = translateKeyboardEventModifier(keyEvent);
// Clipboard fallback path: cut/copy/paste are handled by clipboard event
// handlers if direct clipboard access is not available.
if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi && modifiers & Qt::ControlModifier &&
(qtKey == Qt::Key_X || qtKey == Qt::Key_C || qtKey == Qt::Key_V)) {
return 0;
}
bool accepted = false;
if (keyType == QEvent::KeyPress &&
@ -857,4 +885,19 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa
return accepted;
}
int QWasmEventTranslator::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData)
{
Q_UNUSED(e)
QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
if (eventType == EMSCRIPTEN_EVENT_RESIZE) {
// This resize event is called when the HTML window is resized. Depending
// on the page layout the the canvas might also have been resized, so we
// update the Qt screen size (and canvas render size).
eventTranslator->screen()->updateQScreenAndCanvasRenderSize();
}
return 0;
}
QT_END_NAMESPACE

View File

@ -48,7 +48,7 @@ class QWasmEventTranslator : public QObject
public:
explicit QWasmEventTranslator(QObject *parent = 0);
explicit QWasmEventTranslator(QWasmScreen *screen);
static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData);
static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
@ -57,12 +57,15 @@ public:
static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);
static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData);
void processEvents();
void initEventHandlers();
Q_SIGNALS:
void getWindowAt(const QPoint &point, QWindow **window);
private:
QWasmScreen *screen();
Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey);
template <typename Event>
QFlags<Qt::KeyboardModifier> translatKeyModifier(const Event *event);

View File

@ -70,29 +70,35 @@ QWasmIntegration *QWasmIntegration::s_instance;
QWasmIntegration::QWasmIntegration()
: m_fontDb(nullptr),
m_compositor(new QWasmCompositor),
m_screen(new QWasmScreen(m_compositor)),
m_eventDispatcher(nullptr),
m_clipboard(new QWasmClipboard)
{
s_instance = this;
updateQScreenAndCanvasRenderSize();
screenAdded(m_screen);
emscripten_set_resize_callback(0, (void *)this, 1, uiEvent_cb);
// We expect that qtloader.js has populated Module.qtCanvasElements with one or more canvases.
// Also check Module.canvas, which may be set if the emscripen or a custom loader is used.
emscripten::val qtCanvaseElements = val::module_property("qtCanvasElements");
emscripten::val canvas = val::module_property("canvas");
m_eventTranslator = new QWasmEventTranslator;
if (!qtCanvaseElements.isUndefined()) {
int screenCount = qtCanvaseElements["length"].as<int>();
for (int i = 0; i < screenCount; ++i) {
emscripten::val canvas = qtCanvaseElements[i].as<emscripten::val>();
QString canvasId = QString::fromStdString(canvas["id"].as<std::string>());
addScreen(canvasId);
}
} else if (!canvas.isUndefined()){
QString canvasId = QString::fromStdString(canvas["id"].as<std::string>());
addScreen(canvasId);
}
emscripten::val::global("window").set("onbeforeunload", val::module_property("browserBeforeUnload"));
}
QWasmIntegration::~QWasmIntegration()
{
delete m_compositor;
destroyScreen(m_screen);
delete m_fontDb;
delete m_eventTranslator;
qDeleteAll(m_screens);
s_instance = nullptr;
}
@ -117,13 +123,15 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const
QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const
{
return new QWasmWindow(window, m_compositor, m_backingStores.value(window));
QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor();
return new QWasmWindow(window, compositor, m_backingStores.value(window));
}
QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const
{
#ifndef QT_NO_OPENGL
QWasmBackingStore *backingStore = new QWasmBackingStore(m_compositor, window);
QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor();
QWasmBackingStore *backingStore = new QWasmBackingStore(compositor, window);
m_backingStores.insert(window, backingStore);
return backingStore;
#else
@ -168,55 +176,22 @@ QPlatformTheme *QWasmIntegration::createPlatformTheme(const QString &name) const
return QPlatformIntegration::createPlatformTheme(name);
}
int QWasmIntegration::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData)
{
Q_UNUSED(e)
Q_UNUSED(userData)
if (eventType == EMSCRIPTEN_EVENT_RESIZE) {
// This resize event is called when the HTML window is resized. Depending
// on the page layout the the canvas might also have been resized, so we
// update the Qt screen size (and canvas render size).
updateQScreenAndCanvasRenderSize();
}
return 0;
}
static void set_canvas_size(double width, double height)
{
emscripten::val canvas = emscripten::val::global("canvas");
canvas.set("width", width);
canvas.set("height", height);
}
void QWasmIntegration::updateQScreenAndCanvasRenderSize()
{
// The HTML canvas has two sizes: the CSS size and the canvas render size.
// The CSS size is determined according to standard CSS rules, while the
// render size is set using the "width" and "height" attributes. The render
// size must be set manually and is not auto-updated on CSS size change.
// Setting the render size to a value larger than the CSS size enables high-dpi
// rendering.
double css_width;
double css_height;
emscripten_get_element_css_size(0, &css_width, &css_height);
QSizeF cssSize(css_width, css_height);
QWasmScreen *screen = QWasmIntegration::get()->m_screen;
QSizeF canvasSize = cssSize * screen->devicePixelRatio();
set_canvas_size(canvasSize.width(), canvasSize.height());
screen->setGeometry(QRect(QPoint(0, 0), cssSize.toSize()));
QWasmIntegration::get()->m_compositor->redrawWindowContent();
}
QPlatformClipboard* QWasmIntegration::clipboard() const
{
if (!m_clipboard)
m_clipboard = new QWasmClipboard;
return m_clipboard;
}
QVector<QWasmScreen *> QWasmIntegration::screens()
{
return m_screens;
}
void QWasmIntegration::addScreen(const QString &canvasId)
{
QWasmScreen *screen = new QWasmScreen(canvasId);
m_clipboard->installEventHandlers(canvasId);
m_screens.append(screen);
screenAdded(screen);
}
QT_END_NAMESPACE

View File

@ -71,24 +71,20 @@ public:
QPlatformTheme *createPlatformTheme(const QString &name) const override;
QPlatformClipboard *clipboard() const override;
QWasmScreen *screen() { return m_screen; }
QWasmCompositor *compositor() { return m_compositor; }
QWasmEventTranslator *eventTranslator() { return m_eventTranslator; }
QVector<QWasmScreen *>screens();
QWasmClipboard *getWasmClipboard() { return m_clipboard; }
static QWasmIntegration *get() { return s_instance; }
static void QWasmBrowserExit();
static void updateQScreenAndCanvasRenderSize();
private:
void addScreen(const QString &canvasId);
mutable QWasmFontDatabase *m_fontDb;
QWasmCompositor *m_compositor;
mutable QWasmScreen *m_screen;
mutable QWasmEventTranslator *m_eventTranslator;
mutable QWasmEventDispatcher *m_eventDispatcher;
static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData);
mutable QHash<QWindow *, QWasmBackingStore *> m_backingStores;
QVector<QWasmScreen *> m_screens;
mutable QWasmClipboard *m_clipboard;
static QWasmIntegration *s_instance;
};

View File

@ -28,7 +28,7 @@
****************************************************************************/
#include "qwasmopenglcontext.h"
#include "qwasmintegration.h"
#include <EGL/egl.h>
QT_BEGIN_NAMESPACE
@ -57,7 +57,7 @@ void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surfac
emscripten_webgl_destroy_context(m_context);
// Create new context
const char *canvasId = 0; // (use default canvas) FIXME: get the actual canvas from the surface.
const QString canvasId = QWasmScreen::get(surface->screen())->canvasId();
m_context = createEmscriptenContext(canvasId, m_requestedFormat);
// Register context-lost callback.
@ -73,11 +73,11 @@ void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surfac
return true;
};
bool capture = true;
emscripten_set_webglcontextlost_callback(canvasId, this, capture, callback);
emscripten_set_webglcontextlost_callback(canvasId.toLocal8Bit().constData(), this, capture, callback);
}
}
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const char *canvasId, QSurfaceFormat format)
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const QString &canvasId, QSurfaceFormat format)
{
EmscriptenWebGLContextAttributes attributes;
emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes
@ -96,7 +96,7 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(cons
attributes.depth = format.depthBufferSize() > 0;
attributes.stencil = format.stencilBufferSize() > 0;
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId, &attributes);
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId.toLocal8Bit().constData(), &attributes);
return context;
}

View File

@ -51,7 +51,7 @@ public:
private:
void maybeRecreateEmscriptenContext(QPlatformSurface *surface);
static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const char *canvasId, QSurfaceFormat format);
static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const QString &canvasId, QSurfaceFormat format);
bool m_contextLost = false;
QSurfaceFormat m_requestedFormat;

View File

@ -29,8 +29,10 @@
#include "qwasmscreen.h"
#include "qwasmwindow.h"
#include "qwasmeventtranslator.h"
#include "qwasmcompositor.h"
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <QtEglSupport/private/qeglconvenience_p.h>
#ifndef QT_NO_OPENGL
@ -44,12 +46,13 @@
QT_BEGIN_NAMESPACE
QWasmScreen::QWasmScreen(QWasmCompositor *compositor)
: m_compositor(compositor)
, m_depth(32)
, m_format(QImage::Format_RGB32)
QWasmScreen::QWasmScreen(const QString &canvasId)
: m_canvasId(canvasId)
{
m_compositor->setScreen(this);
m_compositor = new QWasmCompositor(this);
m_eventTranslator = new QWasmEventTranslator(this);
updateQScreenAndCanvasRenderSize();
}
QWasmScreen::~QWasmScreen()
@ -57,6 +60,31 @@ QWasmScreen::~QWasmScreen()
}
QWasmScreen *QWasmScreen::get(QPlatformScreen *screen)
{
return static_cast<QWasmScreen *>(screen);
}
QWasmScreen *QWasmScreen::get(QScreen *screen)
{
return get(screen->handle());
}
QWasmCompositor *QWasmScreen::compositor()
{
return m_compositor;
}
QWasmEventTranslator *QWasmScreen::eventTranslator()
{
return m_eventTranslator;
}
QString QWasmScreen::canvasId() const
{
return m_canvasId;
}
QRect QWasmScreen::geometry() const
{
return m_geometry;
@ -82,6 +110,11 @@ qreal QWasmScreen::devicePixelRatio() const
return qreal(htmlWindowDpr);
}
QString QWasmScreen::name() const
{
return m_canvasId;
}
QPlatformCursor *QWasmScreen::cursor() const
{
return const_cast<QWasmCursor *>(&m_cursor);
@ -114,4 +147,31 @@ void QWasmScreen::setGeometry(const QRect &rect)
resizeMaximizedWindows();
}
void QWasmScreen::updateQScreenAndCanvasRenderSize()
{
// The HTML canvas has two sizes: the CSS size and the canvas render size.
// The CSS size is determined according to standard CSS rules, while the
// render size is set using the "width" and "height" attributes. The render
// size must be set manually and is not auto-updated on CSS size change.
// Setting the render size to a value larger than the CSS size enables high-dpi
// rendering.
QByteArray canvasId = m_canvasId.toUtf8();
double css_width;
double css_height;
emscripten_get_element_css_size(canvasId.constData(), &css_width, &css_height);
QSizeF cssSize(css_width, css_height);
QSizeF canvasSize = cssSize * devicePixelRatio();
emscripten::val canvas = emscripten::val::global(canvasId.constData());
canvas.set("width", canvasSize.width());
canvas.set("height", canvasSize.height());
emscripten::val rect = canvas.call<emscripten::val>("getBoundingClientRect");
QPoint position(rect["left"].as<int>(), rect["top"].as<int>());
setGeometry(QRect(position, cssSize.toSize()));
m_compositor->redrawWindowContent();
}
QT_END_NAMESPACE

View File

@ -43,20 +43,28 @@ class QPlatformOpenGLContext;
class QWasmWindow;
class QWasmBackingStore;
class QWasmCompositor;
class QWasmEventTranslator;
class QOpenGLContext;
class QWasmScreen : public QObject, public QPlatformScreen
{
Q_OBJECT
public:
QWasmScreen(QWasmCompositor *compositor);
QWasmScreen(const QString &canvasId);
~QWasmScreen();
static QWasmScreen *get(QPlatformScreen *screen);
static QWasmScreen *get(QScreen *screen);
QString canvasId() const;
QWasmCompositor *compositor();
QWasmEventTranslator *eventTranslator();
QRect geometry() const override;
int depth() const override;
QImage::Format format() const override;
qreal devicePixelRatio() const override;
QString name() const override;
QPlatformCursor *cursor() const override;
void resizeMaximizedWindows();
@ -64,17 +72,18 @@ public:
QWindow *topLevelAt(const QPoint &p) const override;
void invalidateSize();
void updateQScreenAndCanvasRenderSize();
public slots:
void setGeometry(const QRect &rect);
protected:
private:
QWasmCompositor *m_compositor;
QString m_canvasId;
QWasmCompositor *m_compositor = nullptr;
QWasmEventTranslator *m_eventTranslator = nullptr;
QRect m_geometry = QRect(0, 0, 100, 100);
int m_depth;
QImage::Format m_format;
int m_depth = 32;
QImage::Format m_format = QImage::Format_RGB32;
QWasmCursor m_cursor;
};

View File

@ -7,27 +7,32 @@
<style>
html, body { padding: 0; margin : 0; overflow:hidden; height: 100% }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas { border: 0px none; background-color: white; height:100%; width:100%; }
canvas { border: 0px none; background-color: white; height:100%; width:100%; }
/* The contenteditable property is set to true for the canvas in order to support
clipboard events. Hide the resulting focus frame and set the cursor back to
the default cursor. */
canvas { outline: 0px solid transparent; cursor:default }
</style>
</head>
<body onload="init()">
<figure style="overflow:visible;" id="spinner">
<figure style="overflow:visible;" id="qtspinner">
<center style="margin-top:1.5em; line-height:150%">
<img src="qtlogo.svg"; width=320; height=200; style="display:block"> </img>
<strong>Qt for WebAssembly: APPNAME</strong>
<div id="status"></div>
<div id="qtstatus"></div>
<noscript>JavaScript is disabled. Please enable JavaScript to use this application.</noscript>
</center>
</figure>
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<canvas id="qtcanvas" oncontextmenu="event.preventDefault()" contenteditable="true"></canvas>
<script type='text/javascript'>
function init() {
var spinner = document.getElementById('spinner');
var canvas = document.getElementById('canvas');
var status = document.getElementById('status')
var spinner = document.getElementById('qtspinner');
var canvas = document.getElementById('qtcanvas');
var status = document.getElementById('qtstatus')
var qtLoader = QtLoader({
canvasElements : [canvas],
showLoader: function(loaderStatus) {
spinner.style.display = 'block';
canvas.style.display = 'none';
@ -50,7 +55,6 @@
showCanvas: function() {
spinner.style.display = 'none';
canvas.style.display = 'block';
return canvas;
},
});
qtLoader.loadEmscriptenModule("APPNAME");

View File

@ -55,9 +55,8 @@ bool QAppleTestLogger::debugLoggingEnabled()
return os_log_type_enabled(OS_LOG_DEFAULT, OS_LOG_TYPE_DEBUG);
}
QAppleTestLogger::QAppleTestLogger(QAbstractTestLogger *logger)
QAppleTestLogger::QAppleTestLogger()
: QAbstractTestLogger(nullptr)
, m_logger(logger)
{
}
@ -65,6 +64,8 @@ static QAppleLogActivity testFunctionActivity;
void QAppleTestLogger::enterTestFunction(const char *function)
{
Q_UNUSED(function);
// Re-create activity each time
testFunctionActivity = QT_APPLE_LOG_ACTIVITY("Running test function").enter();
@ -73,15 +74,12 @@ void QAppleTestLogger::enterTestFunction(const char *function)
QString identifier = QString::fromLatin1(testIdentifier.data());
QMessageLogContext context(nullptr, 0, nullptr, "qt.test.enter");
QString message = identifier;
if (AppleUnifiedLogger::messageHandler(QtDebugMsg, context, message, identifier))
return; // AUL already printed to stderr
m_logger->enterTestFunction(function);
AppleUnifiedLogger::messageHandler(QtDebugMsg, context, message, identifier);
}
void QAppleTestLogger::leaveTestFunction()
{
m_logger->leaveTestFunction();
testFunctionActivity.leave();
}
@ -134,18 +132,12 @@ void QAppleTestLogger::addIncident(IncidentTypes type, const char *description,
if (qstrlen(description))
message += QLatin1Char('\n') % QString::fromLatin1(description);
if (AppleUnifiedLogger::messageHandler(incidentClassification.first, context, message, subsystem))
return; // AUL already printed to stderr
m_logger->addIncident(type, description, file, line);
AppleUnifiedLogger::messageHandler(incidentClassification.first, context, message, subsystem);
}
void QAppleTestLogger::addMessage(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
if (AppleUnifiedLogger::messageHandler(type, context, message))
return; // AUL already printed to stderr
m_logger->addMessage(type, context, message);
AppleUnifiedLogger::messageHandler(type, context, message);
}
#endif // QT_USE_APPLE_UNIFIED_LOGGING

View File

@ -63,12 +63,7 @@ class QAppleTestLogger : public QAbstractTestLogger
public:
static bool debugLoggingEnabled();
QAppleTestLogger(QAbstractTestLogger *logger);
void startLogging() override
{ m_logger->startLogging(); }
void stopLogging() override
{ m_logger->stopLogging(); }
QAppleTestLogger();
void enterTestFunction(const char *function) override;
void leaveTestFunction() override;
@ -77,16 +72,12 @@ public:
const char *file = 0, int line = 0) override;
void addMessage(QtMsgType, const QMessageLogContext &,
const QString &) override;
void addBenchmarkResult(const QBenchmarkResult &result) override
{ m_logger->addBenchmarkResult(result); }
void addMessage(MessageTypes type, const QString &message,
const char *file = 0, int line = 0) override
{ m_logger->addMessage(type, message, file, line); }
{ Q_UNUSED(type); Q_UNUSED(message); Q_UNUSED(file); Q_UNUSED(line); Q_UNREACHABLE(); }
private:
QScopedPointer<QAbstractTestLogger> m_logger;
void addBenchmarkResult(const QBenchmarkResult &result) override
{ Q_UNUSED(result); }
};
#endif

View File

@ -78,6 +78,10 @@
#include <QtTest/private/qtestutil_macos_p.h>
#endif
#if defined(Q_OS_DARWIN)
#include <QtTest/private/qappletestlogger_p.h>
#endif
#include <cmath>
#include <numeric>
#include <algorithm>
@ -511,7 +515,7 @@ static int qToInt(const char *str)
Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool qml)
{
QTestLog::LogMode logFormat = QTestLog::Plain;
int logFormat = -1; // Not set
const char *logFilename = 0;
QTest::testFunctions.clear();
@ -679,7 +683,7 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool
fprintf(stderr, "only one logger can log to stdout\n");
exit(1);
}
QTestLog::addLogger(logFormat, filename);
QTestLog::addLogger(QTestLog::LogMode(logFormat), filename);
}
delete [] filename;
delete [] format;
@ -841,10 +845,25 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool
QTestLog::setInstalledTestCoverage(installedTestCoverage);
// If no loggers were created by the long version of the -o command-line
// option, create a logger using whatever filename and format were
// set using the old-style command-line options.
if (QTestLog::loggerCount() == 0)
QTestLog::addLogger(logFormat, logFilename);
// option, but a logger was requested via the old-style option, add it.
const bool explicitLoggerRequested = logFormat != -1;
if (QTestLog::loggerCount() == 0 && explicitLoggerRequested)
QTestLog::addLogger(QTestLog::LogMode(logFormat), logFilename);
bool addFallbackLogger = !explicitLoggerRequested;
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
// Any explicitly requested loggers will be added by now, so we can check if they use stdout
const bool safeToAddAppleLogger = !AppleUnifiedLogger::willMirrorToStderr() || !QTestLog::loggerUsingStdout();
if (safeToAddAppleLogger && QAppleTestLogger::debugLoggingEnabled()) {
QTestLog::addLogger(QTestLog::Apple, nullptr);
if (AppleUnifiedLogger::willMirrorToStderr() && !logFilename)
addFallbackLogger = false; // Prevent plain test logger fallback below
}
#endif
if (addFallbackLogger)
QTestLog::addLogger(QTestLog::Plain, logFilename);
}
// Temporary, backwards compatibility, until qtdeclarative's use of it is converted
@ -2171,13 +2190,12 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
if (found.isEmpty()) {
const char *testObjectName = QTestResult::currentTestObjectName();
if (testObjectName) {
QString testsPath = QLibraryInfo::location(QLibraryInfo::TestsPath);
QString candidate = QString::fromLatin1("%1/%2/%3")
const QString testsPath = QLibraryInfo::location(QLibraryInfo::TestsPath);
const QString candidate = QString::fromLatin1("%1/%2/%3")
.arg(testsPath, QFile::decodeName(testObjectName).toLower(), base);
if (QFileInfo::exists(candidate)) {
found = candidate;
}
else if (QTestLog::verboseLevel() >= 2) {
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
QString::fromLatin1("testdata %1 not found in tests install path [%2]; "
"checking next location")
@ -2199,11 +2217,10 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
}
const QString canonicalPath = srcdir.canonicalFilePath();
QString candidate = QString::fromLatin1("%1/%2").arg(canonicalPath, base);
const QString candidate = QString::fromLatin1("%1/%2").arg(canonicalPath, base);
if (!canonicalPath.isEmpty() && QFileInfo::exists(candidate)) {
found = candidate;
}
else if (QTestLog::verboseLevel() >= 2) {
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
QString::fromLatin1("testdata %1 not found relative to source path [%2]")
.arg(base, QDir::toNativeSeparators(candidate))),
@ -2213,31 +2230,48 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
// 4. Try resources
if (found.isEmpty()) {
QString candidate = QString::fromLatin1(":/%1").arg(base);
if (QFileInfo::exists(candidate))
const QString candidate = QString::fromLatin1(":/%1").arg(base);
if (QFileInfo::exists(candidate)) {
found = candidate;
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
QString::fromLatin1("testdata %1 not found in resources [%2]")
.arg(base, QDir::toNativeSeparators(candidate))),
file, line);
}
}
// 5. Try current directory
if (found.isEmpty()) {
const QString candidate = QDir::currentPath() + QLatin1Char('/') + base;
if (QFileInfo::exists(candidate))
if (QFileInfo::exists(candidate)) {
found = candidate;
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
QString::fromLatin1("testdata %1 not found in current directory [%2]")
.arg(base, QDir::toNativeSeparators(candidate))),
file, line);
}
}
// 6. Try main source directory
if (found.isEmpty()) {
QString candidate = QTest::mainSourcePath % QLatin1Char('/') % base;
if (QFileInfo::exists(candidate))
const QString candidate = QTest::mainSourcePath % QLatin1Char('/') % base;
if (QFileInfo::exists(candidate)) {
found = candidate;
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
QString::fromLatin1("testdata %1 not found in main source directory [%2]")
.arg(base, QDir::toNativeSeparators(candidate))),
file, line);
}
}
if (found.isEmpty()) {
QTest::qWarn(qPrintable(
QString::fromLatin1("testdata %1 could not be located!").arg(base)),
file, line);
}
else if (QTestLog::verboseLevel() >= 1) {
} else if (QTestLog::verboseLevel() >= 1) {
QTestLog::info(qPrintable(
QString::fromLatin1("testdata %1 was located at %2").arg(base, QDir::toNativeSeparators(found))),
file, line);

View File

@ -60,6 +60,7 @@
#include <QtCore/qbytearray.h>
#include <QtCore/QElapsedTimer>
#include <QtCore/QVariant>
#include <QtCore/qvector.h>
#if QT_CONFIG(regularexpression)
#include <QtCore/QRegularExpression>
#endif
@ -98,6 +99,8 @@ static void saveCoverageTool(const char * appname, bool testfailed, bool install
static QElapsedTimer elapsedFunctionTime;
static QElapsedTimer elapsedTotalTime;
#define FOREACH_TEST_LOGGER for (QAbstractTestLogger *logger : QTest::loggers)
namespace QTest {
int fails = 0;
@ -165,109 +168,7 @@ namespace QTest {
static IgnoreResultList *ignoreResultList = 0;
struct LoggerList
{
QAbstractTestLogger *logger;
LoggerList *next;
};
class TestLoggers
{
public:
static void addLogger(QAbstractTestLogger *logger)
{
LoggerList *l = new LoggerList;
l->logger = logger;
l->next = loggers;
loggers = l;
}
static void destroyLoggers()
{
while (loggers) {
LoggerList *l = loggers;
loggers = loggers->next;
delete l->logger;
delete l;
}
}
#define FOREACH_LOGGER(operation) \
LoggerList *l = loggers; \
while (l) { \
QAbstractTestLogger *logger = l->logger; \
Q_UNUSED(logger); \
operation; \
l = l->next; \
}
static void startLogging()
{
FOREACH_LOGGER(logger->startLogging());
}
static void stopLogging()
{
FOREACH_LOGGER(logger->stopLogging());
}
static void enterTestFunction(const char *function)
{
FOREACH_LOGGER(logger->enterTestFunction(function));
}
static void leaveTestFunction()
{
FOREACH_LOGGER(logger->leaveTestFunction());
}
static void enterTestData(QTestData *data)
{
FOREACH_LOGGER(logger->enterTestData(data));
}
static void addIncident(QAbstractTestLogger::IncidentTypes type, const char *description,
const char *file = 0, int line = 0)
{
FOREACH_LOGGER(logger->addIncident(type, description, file, line));
}
static void addBenchmarkResult(const QBenchmarkResult &result)
{
FOREACH_LOGGER(logger->addBenchmarkResult(result));
}
static void addMessage(QtMsgType type, const QMessageLogContext &context,
const QString &message)
{
FOREACH_LOGGER(logger->addMessage(type, context, message));
}
static void addMessage(QAbstractTestLogger::MessageTypes type, const QString &message,
const char *file = 0, int line = 0)
{
FOREACH_LOGGER(logger->addMessage(type, message, file, line));
}
static void outputString(const char *msg)
{
FOREACH_LOGGER(logger->outputString(msg));
}
static int loggerCount()
{
int count = 0;
FOREACH_LOGGER(++count);
return count;
}
private:
static LoggerList *loggers;
};
#undef FOREACH_LOGGER
LoggerList *TestLoggers::loggers = 0;
static QVector<QAbstractTestLogger*> loggers;
static bool loggerUsingStdout = false;
static int verbosity = 0;
@ -306,10 +207,10 @@ namespace QTest {
{
static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(QTest::maxWarnings);
if (QTest::TestLoggers::loggerCount() == 0) {
if (QTestLog::loggerCount() == 0) {
// if this goes wrong, something is seriously broken.
qInstallMessageHandler(oldMessageHandler);
QTEST_ASSERT(QTest::TestLoggers::loggerCount() != 0);
QTEST_ASSERT(QTestLog::loggerCount() != 0);
}
if (handleIgnoredMessage(type, message)) {
@ -322,13 +223,16 @@ namespace QTest {
return;
if (!counter.deref()) {
QTest::TestLoggers::addMessage(QAbstractTestLogger::QSystem,
FOREACH_TEST_LOGGER {
logger->addMessage(QAbstractTestLogger::QSystem,
QStringLiteral("Maximum amount of warnings exceeded. Use -maxwarnings to override."));
}
return;
}
}
QTest::TestLoggers::addMessage(type, context, message);
FOREACH_TEST_LOGGER
logger->addMessage(type, context, message);
if (type == QtFatalMsg) {
/* Right now, we're inside the custom message handler and we're
@ -351,13 +255,16 @@ void QTestLog::enterTestFunction(const char* function)
QTEST_ASSERT(function);
QTest::TestLoggers::enterTestFunction(function);
FOREACH_TEST_LOGGER
logger->enterTestFunction(function);
}
void QTestLog::enterTestData(QTestData *data)
{
QTEST_ASSERT(data);
QTest::TestLoggers::enterTestData(data);
FOREACH_TEST_LOGGER
logger->enterTestData(data);
}
int QTestLog::unhandledIgnoreMessages()
@ -376,7 +283,8 @@ void QTestLog::leaveTestFunction()
if (printAvailableTags)
return;
QTest::TestLoggers::leaveTestFunction();
FOREACH_TEST_LOGGER
logger->leaveTestFunction();
}
void QTestLog::printUnhandledIgnoreMessages()
@ -391,7 +299,8 @@ void QTestLog::printUnhandledIgnoreMessages()
message = QStringLiteral("Did not receive any message matching: \"") + list->pattern.toRegularExpression().pattern() + QLatin1Char('"');
#endif
}
QTest::TestLoggers::addMessage(QAbstractTestLogger::Info, message);
FOREACH_TEST_LOGGER
logger->addMessage(QAbstractTestLogger::Info, message);
list = list->next;
}
@ -411,7 +320,8 @@ void QTestLog::addPass(const char *msg)
++QTest::passes;
QTest::TestLoggers::addIncident(QAbstractTestLogger::Pass, msg);
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::Pass, msg);
}
void QTestLog::addFail(const char *msg, const char *file, int line)
@ -420,7 +330,8 @@ void QTestLog::addFail(const char *msg, const char *file, int line)
++QTest::fails;
QTest::TestLoggers::addIncident(QAbstractTestLogger::Fail, msg, file, line);
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::Fail, msg, file, line);
}
void QTestLog::addXFail(const char *msg, const char *file, int line)
@ -428,7 +339,8 @@ void QTestLog::addXFail(const char *msg, const char *file, int line)
QTEST_ASSERT(msg);
QTEST_ASSERT(file);
QTest::TestLoggers::addIncident(QAbstractTestLogger::XFail, msg, file, line);
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::XFail, msg, file, line);
}
void QTestLog::addXPass(const char *msg, const char *file, int line)
@ -438,7 +350,8 @@ void QTestLog::addXPass(const char *msg, const char *file, int line)
++QTest::fails;
QTest::TestLoggers::addIncident(QAbstractTestLogger::XPass, msg, file, line);
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::XPass, msg, file, line);
}
void QTestLog::addBPass(const char *msg)
@ -447,7 +360,8 @@ void QTestLog::addBPass(const char *msg)
++QTest::blacklists;
QTest::TestLoggers::addIncident(QAbstractTestLogger::BlacklistedPass, msg);
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::BlacklistedPass, msg);
}
void QTestLog::addBFail(const char *msg, const char *file, int line)
@ -457,7 +371,8 @@ void QTestLog::addBFail(const char *msg, const char *file, int line)
++QTest::blacklists;
QTest::TestLoggers::addIncident(QAbstractTestLogger::BlacklistedFail, msg, file, line);
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::BlacklistedFail, msg, file, line);
}
void QTestLog::addBXPass(const char *msg, const char *file, int line)
@ -467,7 +382,8 @@ void QTestLog::addBXPass(const char *msg, const char *file, int line)
++QTest::blacklists;
QTest::TestLoggers::addIncident(QAbstractTestLogger::BlacklistedXPass, msg, file, line);
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::BlacklistedXPass, msg, file, line);
}
void QTestLog::addBXFail(const char *msg, const char *file, int line)
@ -477,7 +393,8 @@ void QTestLog::addBXFail(const char *msg, const char *file, int line)
++QTest::blacklists;
QTest::TestLoggers::addIncident(QAbstractTestLogger::BlacklistedXFail, msg, file, line);
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::BlacklistedXFail, msg, file, line);
}
void QTestLog::addSkip(const char *msg, const char *file, int line)
@ -487,27 +404,33 @@ void QTestLog::addSkip(const char *msg, const char *file, int line)
++QTest::skips;
QTest::TestLoggers::addMessage(QAbstractTestLogger::Skip, QString::fromUtf8(msg), file, line);
FOREACH_TEST_LOGGER
logger->addMessage(QAbstractTestLogger::Skip, QString::fromUtf8(msg), file, line);
}
void QTestLog::addBenchmarkResult(const QBenchmarkResult &result)
{
QTest::TestLoggers::addBenchmarkResult(result);
FOREACH_TEST_LOGGER
logger->addBenchmarkResult(result);
}
void QTestLog::startLogging()
{
elapsedTotalTime.start();
elapsedFunctionTime.start();
QTest::TestLoggers::startLogging();
FOREACH_TEST_LOGGER
logger->startLogging();
QTest::oldMessageHandler = qInstallMessageHandler(QTest::messageHandler);
}
void QTestLog::stopLogging()
{
qInstallMessageHandler(QTest::oldMessageHandler);
QTest::TestLoggers::stopLogging();
QTest::TestLoggers::destroyLoggers();
FOREACH_TEST_LOGGER {
logger->stopLogging();
delete logger;
}
QTest::loggers.clear();
QTest::loggerUsingStdout = false;
saveCoverageTool(QTestResult::currentAppName(), failCount() != 0, QTestLog::installedTestCoverage());
}
@ -542,6 +465,11 @@ void QTestLog::addLogger(LogMode mode, const char *filename)
case QTestLog::TAP:
logger = new QTapTestLogger(filename);
break;
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
case QTestLog::Apple:
logger = new QAppleTestLogger;
break;
#endif
#if defined(HAVE_XCTEST)
case QTestLog::XCTest:
logger = new QXcodeTestLogger;
@ -549,21 +477,13 @@ void QTestLog::addLogger(LogMode mode, const char *filename)
#endif
}
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
// Logger that also feeds messages to AUL. It needs to wrap the existing
// logger, as it needs to be able to short circuit the existing logger
// in case AUL prints to stderr.
if (QAppleTestLogger::debugLoggingEnabled())
logger = new QAppleTestLogger(logger);
#endif
QTEST_ASSERT(logger);
QTest::TestLoggers::addLogger(logger);
QTest::loggers.append(logger);
}
int QTestLog::loggerCount()
{
return QTest::TestLoggers::loggerCount();
return QTest::loggers.size();
}
bool QTestLog::loggerUsingStdout()
@ -575,15 +495,16 @@ void QTestLog::warn(const char *msg, const char *file, int line)
{
QTEST_ASSERT(msg);
if (QTest::TestLoggers::loggerCount() > 0)
QTest::TestLoggers::addMessage(QAbstractTestLogger::Warn, QString::fromUtf8(msg), file, line);
FOREACH_TEST_LOGGER
logger->addMessage(QAbstractTestLogger::Warn, QString::fromUtf8(msg), file, line);
}
void QTestLog::info(const char *msg, const char *file, int line)
{
QTEST_ASSERT(msg);
QTest::TestLoggers::addMessage(QAbstractTestLogger::Info, QString::fromUtf8(msg), file, line);
FOREACH_TEST_LOGGER
logger->addMessage(QAbstractTestLogger::Info, QString::fromUtf8(msg), file, line);
}
void QTestLog::setVerboseLevel(int level)

View File

@ -53,6 +53,10 @@
#include <QtTest/qttestglobal.h>
#if defined(Q_OS_DARWIN)
#include <QtCore/private/qcore_mac_p.h>
#endif
QT_BEGIN_NAMESPACE
class QBenchmarkResult;
@ -63,9 +67,12 @@ class Q_TESTLIB_EXPORT QTestLog
{
public:
enum LogMode {
Plain = 0, XML, LightXML, XunitXML, CSV, TeamCity, TAP,
Plain = 0, XML, LightXML, XunitXML, CSV, TeamCity, TAP
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
, Apple
#endif
#if defined(HAVE_XCTEST)
XCTest
, XCTest
#endif
};

View File

@ -148,7 +148,7 @@ bool QDialogPrivate::canBeNativeDialog() const
/*!
\internal
Properly hides dialog and sets the \p resultCode
Properly hides dialog and sets the \a resultCode.
*/
void QDialogPrivate::hide(int resultCode)
{
@ -164,8 +164,8 @@ void QDialogPrivate::hide(int resultCode)
/*!
\internal
Emits finished() signal with \p resultCode. If the \p dialogCode
is equal to 0 emits rejected(), if the \p dialogCode is equal to
Emits finished() signal with \a resultCode. If the \a dialogCode
is equal to 0 emits rejected(), if the \a dialogCode is equal to
1 emits accepted().
*/
void QDialogPrivate::finalize(int resultCode, int dialogCode)

View File

@ -2045,7 +2045,7 @@ void tst_QDir::detachingOperations()
QCOMPARE(dir2.nameFilters(), nameFilters);
QCOMPARE(dir2.sorting(), sorting);
dir2 = path1;
dir2.setPath(path1);
QCOMPARE(dir2.path(), path1);
QCOMPARE(dir2.filter(), filter);
QCOMPARE(dir2.nameFilters(), nameFilters);

View File

@ -10,7 +10,7 @@ SUBDIRS = lib \
tst.depends += almostplugin
SUBDIRS += almostplugin
}
macos:qtConfig(private_tests) {
macos:qtConfig(private_tests):qtHaveModule(gui) {
tst.depends += machtest
SUBDIRS += machtest
}

View File

@ -57,7 +57,6 @@ private Q_SLOTS:
void lookupTableConstructor();
void lookupTableStatic_data();
void lookupTableStatic();
void lookupTableDynamic();
@ -126,7 +125,7 @@ void tst_Hpack::bitstreamConstruction()
// 'Read' some data back:
for (int i = 0; i < size; ++i) {
uchar bitPattern = 0;
const auto bitsRead = in.peekBits(i * 8, 8, &bitPattern);
const auto bitsRead = in.peekBits(quint64(i * 8), 8, &bitPattern);
QVERIFY(bitsRead == 8);
QVERIFY(bitPattern == bytes[i]);
}
@ -282,7 +281,7 @@ void tst_Hpack::bitstreamCompression()
const auto start = QRandomGenerator::global()->bounded(uint(bytes.length()) / 2);
auto end = start * 2;
if (!end)
end = bytes.length() / 2;
end = unsigned(bytes.length() / 2);
strings.push_back(bytes.substr(start, end - start));
const auto &s = strings.back();
totalStringBytes += s.size();
@ -384,43 +383,21 @@ void tst_Hpack::lookupTableConstructor()
}
}
void tst_Hpack::lookupTableStatic_data()
{
QTest::addColumn<QByteArray>("expectedName");
QTest::addColumn<QByteArray>("expectedValue");
// Some predefined fields to find
// (they are always defined/required by HPACK).
QTest::newRow(":authority|") << QByteArray(":authority") << QByteArray("");
QTest::newRow(":method|GET") << QByteArray(":method") << QByteArray("GET");
QTest::newRow(":method|POST") << QByteArray(":method") << QByteArray("POST");
QTest::newRow(":path|/") << QByteArray(":path") << QByteArray("/");
QTest::newRow(":path|/index.html") << QByteArray(":path") << QByteArray("/index.html");
QTest::newRow(":scheme|http") << QByteArray(":scheme") << QByteArray("http");
QTest::newRow(":scheme|https") << QByteArray(":scheme") << QByteArray("https");
QTest::newRow(":status|200") << QByteArray(":status") << QByteArray("200");
QTest::newRow(":status|204") << QByteArray(":status") << QByteArray("204");
QTest::newRow(":status|206") << QByteArray(":status") << QByteArray("206");
QTest::newRow(":status|304") << QByteArray(":status") << QByteArray("304");
QTest::newRow(":status|400") << QByteArray(":status") << QByteArray("400");
QTest::newRow(":status|404") << QByteArray(":status") << QByteArray("404");
QTest::newRow(":status|500") << QByteArray(":status") << QByteArray("500");
}
void tst_Hpack::lookupTableStatic()
{
const FieldLookupTable table(0, false /*all static, no need in 'search index'*/);
QFETCH(QByteArray, expectedName);
QFETCH(QByteArray, expectedValue);
const quint32 index = table.indexOf(expectedName, expectedValue);
QVERIFY(index != 0);
const auto &staticTable = FieldLookupTable::staticPart();
QByteArray name, value;
QVERIFY(table.field(index, &name, &value));
QCOMPARE(name, expectedName);
QCOMPARE(value, expectedValue);
quint32 currentIndex = 1; // HPACK is indexing starting from 1.
for (const HeaderField &field : staticTable) {
const quint32 index = table.indexOf(field.name, field.value);
QVERIFY(index != 0);
QCOMPARE(index, currentIndex);
QVERIFY(table.field(index, &name, &value));
QCOMPARE(name, field.name);
QCOMPARE(value, field.value);
++currentIndex;
}
}
void tst_Hpack::lookupTableDynamic()

View File

@ -56,7 +56,7 @@ winrt|!qtHaveModule(gui)|!qtConfig(accessibility): SUBDIRS -= qaccessibility
!qtConfig(process): SUBDIRS -= qprocess_and_guieventloop
!mac: SUBDIRS -= \
!macos|!qtHaveModule(gui): SUBDIRS -= \
macgui \
macnativeevents \
macplist \