mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 18:21:16 +02:00
Merge branch 'playEntityCollisionSound-crash' of github.com:sethalves/hifi into fix/asset-request-crash
This commit is contained in:
commit
1416e397e4
13 changed files with 68 additions and 58 deletions
|
@ -201,7 +201,7 @@ var toolBar = (function() {
|
||||||
}, true, false);
|
}, true, false);
|
||||||
|
|
||||||
newModelButton = toolBar.addTool({
|
newModelButton = toolBar.addTool({
|
||||||
imageURL: toolIconUrl + "upload-01.svg",
|
imageURL: toolIconUrl + "model-01.svg",
|
||||||
subImage: {
|
subImage: {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: Tool.IMAGE_WIDTH,
|
y: Tool.IMAGE_WIDTH,
|
||||||
|
|
|
@ -64,9 +64,9 @@ void AnimationReader::run() {
|
||||||
|
|
||||||
if (urlValid) {
|
if (urlValid) {
|
||||||
// Parse the FBX directly from the QNetworkReply
|
// Parse the FBX directly from the QNetworkReply
|
||||||
FBXGeometry* fbxgeo = nullptr;
|
FBXGeometry::Pointer fbxgeo;
|
||||||
if (_url.path().toLower().endsWith(".fbx")) {
|
if (_url.path().toLower().endsWith(".fbx")) {
|
||||||
fbxgeo = readFBX(_data, QVariantHash(), _url.path());
|
fbxgeo.reset(readFBX(_data, QVariantHash(), _url.path()));
|
||||||
} else {
|
} else {
|
||||||
QString errorStr("usupported format");
|
QString errorStr("usupported format");
|
||||||
emit onError(299, errorStr);
|
emit onError(299, errorStr);
|
||||||
|
@ -117,16 +117,16 @@ const QVector<FBXAnimationFrame>& Animation::getFramesReference() const {
|
||||||
void Animation::downloadFinished(const QByteArray& data) {
|
void Animation::downloadFinished(const QByteArray& data) {
|
||||||
// parse the animation/fbx file on a background thread.
|
// parse the animation/fbx file on a background thread.
|
||||||
AnimationReader* animationReader = new AnimationReader(_url, data);
|
AnimationReader* animationReader = new AnimationReader(_url, data);
|
||||||
connect(animationReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(animationParseSuccess(FBXGeometry*)));
|
connect(animationReader, SIGNAL(onSuccess(FBXGeometry::Pointer)), SLOT(animationParseSuccess(FBXGeometry::Pointer)));
|
||||||
connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString)));
|
connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString)));
|
||||||
QThreadPool::globalInstance()->start(animationReader);
|
QThreadPool::globalInstance()->start(animationReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::animationParseSuccess(FBXGeometry* geometry) {
|
void Animation::animationParseSuccess(FBXGeometry::Pointer geometry) {
|
||||||
|
|
||||||
qCDebug(animation) << "Animation parse success" << _url.toDisplayString();
|
qCDebug(animation) << "Animation parse success" << _url.toDisplayString();
|
||||||
|
|
||||||
_geometry.reset(geometry);
|
_geometry = geometry;
|
||||||
finishedLoading(true);
|
finishedLoading(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,12 +68,12 @@ protected:
|
||||||
virtual void downloadFinished(const QByteArray& data) override;
|
virtual void downloadFinished(const QByteArray& data) override;
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void animationParseSuccess(FBXGeometry* geometry);
|
void animationParseSuccess(FBXGeometry::Pointer geometry);
|
||||||
void animationParseError(int error, QString str);
|
void animationParseError(int error, QString str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::unique_ptr<FBXGeometry> _geometry;
|
FBXGeometry::Pointer _geometry;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Reads geometry in a worker thread.
|
/// Reads geometry in a worker thread.
|
||||||
|
@ -85,7 +85,7 @@ public:
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void onSuccess(FBXGeometry* geometry);
|
void onSuccess(FBXGeometry::Pointer geometry);
|
||||||
void onError(int error, QString str);
|
void onError(int error, QString str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -820,16 +820,24 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityItemPointer entity = entityTree->findEntityByEntityItemID(id);
|
QString collisionSoundURL;
|
||||||
if (!entity) {
|
float mass = 1.0; // value doesn't get used, but set it so compiler is quiet
|
||||||
|
AACube minAACube;
|
||||||
|
bool success = false;
|
||||||
|
_tree->withReadLock([&] {
|
||||||
|
EntityItemPointer entity = entityTree->findEntityByEntityItemID(id);
|
||||||
|
if (entity) {
|
||||||
|
collisionSoundURL = entity->getCollisionSoundURL();
|
||||||
|
mass = entity->computeMass();
|
||||||
|
minAACube = entity->getMinimumAACube(success);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& collisionSoundURL = entity->getCollisionSoundURL();
|
|
||||||
if (collisionSoundURL.isEmpty()) {
|
if (collisionSoundURL.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const float mass = entity->computeMass();
|
|
||||||
const float COLLISION_PENETRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity()
|
const float COLLISION_PENETRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity()
|
||||||
// The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact,
|
// The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact,
|
||||||
// but that first contact depends on exactly where we hit in the physics step.
|
// but that first contact depends on exactly where we hit in the physics step.
|
||||||
|
@ -854,11 +862,6 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
|
||||||
|
|
||||||
// Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2)
|
// Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2)
|
||||||
const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f;
|
const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f;
|
||||||
bool success;
|
|
||||||
auto minAACube = entity->getMinimumAACube(success);
|
|
||||||
if (!success) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const float stretchFactor = log(1.0f + (minAACube.getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
|
const float stretchFactor = log(1.0f + (minAACube.getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
|
||||||
AudioInjector::playSound(collisionSoundURL, volume, stretchFactor, position);
|
AudioInjector::playSound(collisionSoundURL, volume, stretchFactor, position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
static int FBXGeometryPointerMetaTypeId = qRegisterMetaType<FBXGeometry::Pointer>();
|
||||||
|
|
||||||
QStringList FBXGeometry::getJointNames() const {
|
QStringList FBXGeometry::getJointNames() const {
|
||||||
QStringList names;
|
QStringList names;
|
||||||
foreach (const FBXJoint& joint, joints) {
|
foreach (const FBXJoint& joint, joints) {
|
||||||
|
|
|
@ -270,6 +270,7 @@ inline bool operator!=(const SittingPoint& lhs, const SittingPoint& rhs)
|
||||||
/// A set of meshes extracted from an FBX document.
|
/// A set of meshes extracted from an FBX document.
|
||||||
class FBXGeometry {
|
class FBXGeometry {
|
||||||
public:
|
public:
|
||||||
|
using Pointer = std::shared_ptr<FBXGeometry>;
|
||||||
|
|
||||||
QString author;
|
QString author;
|
||||||
QString applicationName; ///< the name of the application that generated the model
|
QString applicationName; ///< the name of the application that generated the model
|
||||||
|
@ -330,6 +331,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(FBXGeometry)
|
Q_DECLARE_METATYPE(FBXGeometry)
|
||||||
|
Q_DECLARE_METATYPE(FBXGeometry::Pointer)
|
||||||
|
|
||||||
/// Reads FBX geometry from the supplied model and mapping data.
|
/// Reads FBX geometry from the supplied model and mapping data.
|
||||||
/// \exception QString if an error occurs in parsing
|
/// \exception QString if an error occurs in parsing
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
|
static int TexturePointerMetaTypeId = qRegisterMetaType<TexturePointer>();
|
||||||
|
|
||||||
std::atomic<uint32_t> Texture::_textureCPUCount{ 0 };
|
std::atomic<uint32_t> Texture::_textureCPUCount{ 0 };
|
||||||
std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 };
|
std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 };
|
||||||
|
@ -857,8 +858,8 @@ void TextureSource::reset(const QUrl& url) {
|
||||||
_imageUrl = url;
|
_imageUrl = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureSource::resetTexture(gpu::Texture* texture) {
|
void TextureSource::resetTexture(gpu::TexturePointer texture) {
|
||||||
_gpuTexture.reset(texture);
|
_gpuTexture = texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureSource::isDefined() const {
|
bool TextureSource::isDefined() const {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <algorithm> //min max and more
|
#include <algorithm> //min max and more
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
|
||||||
|
#include <QMetaType>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
@ -469,7 +470,6 @@ protected:
|
||||||
typedef std::shared_ptr<Texture> TexturePointer;
|
typedef std::shared_ptr<Texture> TexturePointer;
|
||||||
typedef std::vector< TexturePointer > Textures;
|
typedef std::vector< TexturePointer > Textures;
|
||||||
|
|
||||||
|
|
||||||
// TODO: For now TextureView works with Texture as a place holder for the Texture.
|
// TODO: For now TextureView works with Texture as a place holder for the Texture.
|
||||||
// The overall logic should be about the same except that the Texture will be a real GL Texture under the hood
|
// The overall logic should be about the same except that the Texture will be a real GL Texture under the hood
|
||||||
class TextureView {
|
class TextureView {
|
||||||
|
@ -526,7 +526,7 @@ public:
|
||||||
|
|
||||||
void reset(const QUrl& url);
|
void reset(const QUrl& url);
|
||||||
|
|
||||||
void resetTexture(gpu::Texture* texture);
|
void resetTexture(gpu::TexturePointer texture);
|
||||||
|
|
||||||
bool isDefined() const;
|
bool isDefined() const;
|
||||||
|
|
||||||
|
@ -538,5 +538,6 @@ typedef std::shared_ptr< TextureSource > TextureSourcePointer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(gpu::TexturePointer)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -143,40 +143,38 @@ void GeometryReader::run() {
|
||||||
QString urlname = _url.path().toLower();
|
QString urlname = _url.path().toLower();
|
||||||
if (!urlname.isEmpty() && !_url.path().isEmpty() &&
|
if (!urlname.isEmpty() && !_url.path().isEmpty() &&
|
||||||
(_url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"))) {
|
(_url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"))) {
|
||||||
FBXGeometry* fbxGeometry = nullptr;
|
FBXGeometry::Pointer fbxGeometry;
|
||||||
|
|
||||||
if (_url.path().toLower().endsWith(".fbx")) {
|
if (_url.path().toLower().endsWith(".fbx")) {
|
||||||
fbxGeometry = readFBX(_data, _mapping, _url.path());
|
fbxGeometry.reset(readFBX(_data, _mapping, _url.path()));
|
||||||
if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) {
|
if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) {
|
||||||
throw QString("empty geometry, possibly due to an unsupported FBX version");
|
throw QString("empty geometry, possibly due to an unsupported FBX version");
|
||||||
}
|
}
|
||||||
} else if (_url.path().toLower().endsWith(".obj")) {
|
} else if (_url.path().toLower().endsWith(".obj")) {
|
||||||
fbxGeometry = OBJReader().readOBJ(_data, _mapping, _url);
|
fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _url));
|
||||||
} else {
|
} else {
|
||||||
throw QString("unsupported format");
|
throw QString("unsupported format");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the resource has not been deleted, and won't be while invokeMethod is in flight.
|
// Ensure the resource has not been deleted
|
||||||
auto resource = _resource.toStrongRef();
|
auto resource = _resource.toStrongRef();
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
delete fbxGeometry;
|
|
||||||
} else {
|
} else {
|
||||||
QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", Qt::BlockingQueuedConnection, Q_ARG(void*, fbxGeometry));
|
QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition",
|
||||||
|
Q_ARG(FBXGeometry::Pointer, fbxGeometry));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw QString("url is invalid");
|
throw QString("url is invalid");
|
||||||
}
|
}
|
||||||
} catch (const QString& error) {
|
} catch (const QString& error) {
|
||||||
|
|
||||||
qCDebug(modelnetworking) << "Error reading " << _url << ": " << error;
|
qCDebug(modelnetworking) << "Error parsing model for" << _url << ":" << error;
|
||||||
|
|
||||||
auto resource = _resource.toStrongRef();
|
auto resource = _resource.toStrongRef();
|
||||||
// Ensure the resoruce has not been deleted, and won't be while invokeMethod is in flight.
|
if (resource) {
|
||||||
if (!resource) {
|
QMetaObject::invokeMethod(resource.data(), "finishedLoading",
|
||||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
Q_ARG(bool, false));
|
||||||
} else {
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "finishedLoading", Qt::BlockingQueuedConnection, Q_ARG(bool, false));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +188,7 @@ public:
|
||||||
virtual void downloadFinished(const QByteArray& data) override;
|
virtual void downloadFinished(const QByteArray& data) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Q_INVOKABLE void setGeometryDefinition(void* fbxGeometry);
|
Q_INVOKABLE void setGeometryDefinition(FBXGeometry::Pointer fbxGeometry);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariantHash _mapping;
|
QVariantHash _mapping;
|
||||||
|
@ -200,9 +198,9 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
|
||||||
QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data));
|
QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeometryDefinitionResource::setGeometryDefinition(void* fbxGeometry) {
|
void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxGeometry) {
|
||||||
// Assume ownership of the geometry pointer
|
// Assume ownership of the geometry pointer
|
||||||
_geometry.reset(static_cast<FBXGeometry*>(fbxGeometry));
|
_geometry = fbxGeometry;
|
||||||
|
|
||||||
// Copy materials
|
// Copy materials
|
||||||
QHash<QString, size_t> materialIDAtlas;
|
QHash<QString, size_t> materialIDAtlas;
|
||||||
|
|
|
@ -330,7 +330,7 @@ void ImageReader::run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::Texture* texture = nullptr;
|
gpu::TexturePointer texture = nullptr;
|
||||||
{
|
{
|
||||||
// Double-check the resource still exists between long operations.
|
// Double-check the resource still exists between long operations.
|
||||||
auto resource = _resource.toStrongRef();
|
auto resource = _resource.toStrongRef();
|
||||||
|
@ -342,36 +342,32 @@ void ImageReader::run() {
|
||||||
auto url = _url.toString().toStdString();
|
auto url = _url.toString().toStdString();
|
||||||
|
|
||||||
PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr);
|
PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr);
|
||||||
texture = resource.dynamicCast<NetworkTexture>()->getTextureLoader()(image, url);
|
texture.reset(resource.dynamicCast<NetworkTexture>()->getTextureLoader()(image, url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the resource has not been deleted, and won't be while invokeMethod is in flight.
|
// Ensure the resource has not been deleted
|
||||||
auto resource = _resource.toStrongRef();
|
auto resource = _resource.toStrongRef();
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
delete texture;
|
|
||||||
} else {
|
} else {
|
||||||
QMetaObject::invokeMethod(resource.data(), "setImage", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(resource.data(), "setImage",
|
||||||
Q_ARG(void*, texture),
|
Q_ARG(gpu::TexturePointer, texture),
|
||||||
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth,
|
||||||
int originalHeight) {
|
int originalHeight) {
|
||||||
_originalWidth = originalWidth;
|
_originalWidth = originalWidth;
|
||||||
_originalHeight = originalHeight;
|
_originalHeight = originalHeight;
|
||||||
|
|
||||||
gpu::Texture* texture = static_cast<gpu::Texture*>(voidTexture);
|
|
||||||
|
|
||||||
// Passing ownership
|
// Passing ownership
|
||||||
_textureSource->resetTexture(texture);
|
_textureSource->resetTexture(texture);
|
||||||
auto gpuTexture = _textureSource->getGPUTexture();
|
|
||||||
|
|
||||||
if (gpuTexture) {
|
if (texture) {
|
||||||
_width = gpuTexture->getWidth();
|
_width = texture->getWidth();
|
||||||
_height = gpuTexture->getHeight();
|
_height = texture->getHeight();
|
||||||
setSize(gpuTexture->getStoredSize());
|
setSize(texture->getStoredSize());
|
||||||
} else {
|
} else {
|
||||||
// FIXME: If !gpuTexture, we failed to load!
|
// FIXME: If !gpuTexture, we failed to load!
|
||||||
_width = _height = 0;
|
_width = _height = 0;
|
||||||
|
|
|
@ -128,7 +128,6 @@ public:
|
||||||
signals:
|
signals:
|
||||||
void networkTextureCreated(const QWeakPointer<NetworkTexture>& self);
|
void networkTextureCreated(const QWeakPointer<NetworkTexture>& self);
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual bool isCacheable() const override { return _loaded; }
|
virtual bool isCacheable() const override { return _loaded; }
|
||||||
|
@ -136,9 +135,7 @@ protected:
|
||||||
virtual void downloadFinished(const QByteArray& data) override;
|
virtual void downloadFinished(const QByteArray& data) override;
|
||||||
|
|
||||||
Q_INVOKABLE void loadContent(const QByteArray& content);
|
Q_INVOKABLE void loadContent(const QByteArray& content);
|
||||||
// FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on...
|
Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight);
|
||||||
Q_INVOKABLE void setImage(void* texture, int originalWidth, int originalHeight);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TextureType _type;
|
TextureType _type;
|
||||||
|
|
|
@ -606,8 +606,9 @@ void Resource::handleReplyFinished() {
|
||||||
const int BASE_DELAY_MS = 1000;
|
const int BASE_DELAY_MS = 1000;
|
||||||
if (_attempts++ < MAX_ATTEMPTS) {
|
if (_attempts++ < MAX_ATTEMPTS) {
|
||||||
auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts);
|
auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts);
|
||||||
qCDebug(networking).nospace() << "Retrying to load the asset in " << waitTime
|
qCDebug(networking) << "Server unavailable for" << _url <<
|
||||||
<< "ms, attempt " << _attempts << " of " << MAX_ATTEMPTS;
|
"retrying in " << waitTime << "ms," <<
|
||||||
|
"attempt " << _attempts + 1 << "of" << MAX_ATTEMPTS;
|
||||||
QTimer::singleShot(waitTime, this, &Resource::attemptRequest);
|
QTimer::singleShot(waitTime, this, &Resource::attemptRequest);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,15 @@ void MainWindow::saveGeometry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent* event) {
|
void MainWindow::closeEvent(QCloseEvent* event) {
|
||||||
|
// It is the job of Application::quit() to shut things down properly when it is finished with its event loop.
|
||||||
|
// But if we don't explicitly ignore this event now, the window and application event loop will close
|
||||||
|
// before we've had a chance to act on the aboutToClose signal. This will leaves a zombie process on all platforms.
|
||||||
|
// To repro:
|
||||||
|
// Open a QML modal dialog (e.g., select an avatar to wear), but don't dismiss it.
|
||||||
|
// Close the application with the operating system window close button (not the menu Quit)
|
||||||
|
// With ignore: App will wait until you accept or dismiss the dialog and log "Normal exit".
|
||||||
|
// Without ignore: App will close immediately, and nothing will log about quitting or exit.
|
||||||
|
event->ignore();
|
||||||
qApp->quit();
|
qApp->quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue