Skip to content

Commit

Permalink
Create our own message loop to receive WM_ENDSESSION
Browse files Browse the repository at this point in the history
This ensures we receive the message even if no window is shown
  • Loading branch information
TheOneRing committed Nov 17, 2021
1 parent 604babc commit f2b959a
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 23 deletions.
5 changes: 3 additions & 2 deletions changelog/unreleased/8979
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Bugfix: Properly handle Windows log off

We worked around a Qt bug which prevented the client from properly shutdown
on Windows logout or during the client update.
We now ensure that we receive the window messages dispatched by the system.

https://github.com/owncloud/client/issues/8979
https://github.com/owncloud/client/pull/9142
https://github.com/owncloud/client/pull/9220
53 changes: 53 additions & 0 deletions src/gui/guiutility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
#include <QUrlQuery>
#include <QIcon>

#ifdef Q_OS_WIN
#include <QtConcurrent>
#endif

#include "theme.h"

#include "common/asserts.h"
Expand All @@ -30,6 +34,55 @@ using namespace OCC;

Q_LOGGING_CATEGORY(lcUtility, "gui.utility", QtInfoMsg)

namespace {

#ifdef Q_OS_WIN
void watchWM()
{
// Qt only receives window message if a window was displayed at least once
// create an invisible window to handle WM_ENDSESSION
if (qobject_cast<QGuiApplication *>(qApp)) {
QtConcurrent::run([] {
WNDCLASS wc = {};
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = L"ocWindowMessageWatcher";
wc.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -> LRESULT {
// qDebug() << MSG { hwnd, msg, wParam, lParam, 0, {} };
if (msg == WM_QUERYENDSESSION) {
return 1;
} else if (msg == WM_ENDSESSION) {
qCInfo(OCC::lcUtility) << "Received WM_ENDSESSION quitting";
QTimer::singleShot(0, qApp, &QApplication::quit);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
};

OC_ASSERT(RegisterClass(&wc));

auto window = CreateWindowW(wc.lpszClassName, L"watcher", WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, wc.hInstance, nullptr);
OC_ASSERT_X(window, Utility::formatWinError(GetLastError()).toUtf8().constData());

bool run = true;
QObject::connect(qApp, &QApplication::aboutToQuit, qApp, [&] {
run = false;
});
MSG msg;
while (run) {
if (!PeekMessageW(&msg, window, 0, 0, PM_REMOVE)) {
QThread::msleep(100);
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
});
}
}
Q_COREAPP_STARTUP_FUNCTION(watchWM);
#endif
}

bool Utility::openBrowser(const QUrl &url, QWidget *errorWidgetParent)
{
if (!QDesktopServices::openUrl(url)) {
Expand Down
17 changes: 0 additions & 17 deletions src/gui/settingsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,23 +326,6 @@ void SettingsDialog::setVisible(bool visible)
QMainWindow::setVisible(visible);
}

#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)

bool SettingsDialog::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
auto msg = reinterpret_cast<MSG *>(message);
// https://github.com/owncloud/client/issues/8979
// Qt5 has a bug that Windows already get closed on WM_QUERYENDSESSION
// so they never receive WM_ENDSESSION
// Capture the event and go down in style
if (msg->message == WM_QUERYENDSESSION || msg->message == WM_ENDSESSION) {
qCInfo(lcApplication) << "Shutting down" << *msg;
QTimer::singleShot(0, ocApp(), Application::quit);
}
return false;
}
#endif

void SettingsDialog::slotSwitchPage(QAction *action)
{
_ui->stack->setCurrentWidget(_actionGroupWidgets.value(action));
Expand Down
4 changes: 0 additions & 4 deletions src/gui/settingsdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ public slots:
void changeEvent(QEvent *) override;
void setVisible(bool visible) override;

#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
#endif

private slots:
void accountAdded(AccountState *);
void accountRemoved(AccountState *);
Expand Down

0 comments on commit f2b959a

Please sign in to comment.