fix(mac): properly handle system initiated quit events
This commit is contained in:
parent
352f494bee
commit
0bc325addc
4 changed files with 74 additions and 8 deletions
|
|
@ -163,6 +163,11 @@ MainWindow::MainWindow()
|
||||||
applyConfig();
|
applyConfig();
|
||||||
m_statusBar->setSecurityIcon(TlsUtility::isEnabled());
|
m_statusBar->setSecurityIcon(TlsUtility::isEnabled());
|
||||||
restoreWindow();
|
restoreWindow();
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
// Route native quits (Cmd+Q / Apple menu Quit / Dock "Quit") to the usual close-to-tray decision instead.
|
||||||
|
installQuitHandler([this] { return !maybeHideToTray(); });
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
|
|
@ -898,16 +903,25 @@ void MainWindow::handlePeerFingerprint(const QString &fingerprint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MainWindow::maybeHideToTray()
|
||||||
|
{
|
||||||
|
if (!Settings::value(Settings::Gui::CloseToTray).toBool()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::value(Settings::Gui::CloseReminder).toBool()) {
|
||||||
|
messages::showCloseReminder(this);
|
||||||
|
Settings::setValue(Settings::Gui::CloseReminder, false);
|
||||||
|
}
|
||||||
|
Settings::setValue(Settings::Gui::WindowGeometry, geometry());
|
||||||
|
qDebug() << "hiding to tray";
|
||||||
|
hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent *event)
|
void MainWindow::closeEvent(QCloseEvent *event)
|
||||||
{
|
{
|
||||||
if (Settings::value(Settings::Gui::CloseToTray).toBool() && event->spontaneous()) {
|
if (event->spontaneous() && maybeHideToTray()) {
|
||||||
if (Settings::value(Settings::Gui::CloseReminder).toBool()) {
|
|
||||||
messages::showCloseReminder(this);
|
|
||||||
Settings::setValue(Settings::Gui::CloseReminder, false);
|
|
||||||
}
|
|
||||||
Settings::setValue(Settings::Gui::WindowGeometry, geometry());
|
|
||||||
qDebug() << "hiding to tray";
|
|
||||||
hide();
|
|
||||||
event->ignore();
|
event->ignore();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,7 @@ private:
|
||||||
void handlePeerFingerprint(const QString &fingerprint);
|
void handlePeerFingerprint(const QString &fingerprint);
|
||||||
void handleMissingKeyboardLayouts(const QString &layouts);
|
void handleMissingKeyboardLayouts(const QString &layouts);
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
bool maybeHideToTray();
|
||||||
void secureSocket(bool secureSocket);
|
void secureSocket(bool secureSocket);
|
||||||
void connectSlots();
|
void connectSlots();
|
||||||
void handleLogLine(const QString &line);
|
void handleLogLine(const QString &line);
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,12 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
void requestOSXNotificationPermission();
|
void requestOSXNotificationPermission();
|
||||||
bool isOSXDevelopmentBuild();
|
bool isOSXDevelopmentBuild();
|
||||||
bool showOSXNotification(const QString &title, const QString &body);
|
bool showOSXNotification(const QString &title, const QString &body);
|
||||||
bool isOSXInterfaceStyleDark();
|
bool isOSXInterfaceStyleDark();
|
||||||
void forceAppActive();
|
void forceAppActive();
|
||||||
void macOSNativeHide();
|
void macOSNativeHide();
|
||||||
|
void installQuitHandler(std::function<bool()> shouldQuit);
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,19 @@
|
||||||
#import <UserNotifications/UNNotificationContent.h>
|
#import <UserNotifications/UNNotificationContent.h>
|
||||||
#import <UserNotifications/UNNotificationTrigger.h>
|
#import <UserNotifications/UNNotificationTrigger.h>
|
||||||
#import <UserNotifications/UNUserNotificationCenter.h>
|
#import <UserNotifications/UNUserNotificationCenter.h>
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
#import <QtGlobal>
|
#import <QtGlobal>
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::function<bool()> s_shouldQuit;
|
||||||
|
IMP s_originalShouldTerminate = nullptr;
|
||||||
|
BOOL s_isSystemShuttingDown = NO;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void requestOSXNotificationPermission()
|
void requestOSXNotificationPermission()
|
||||||
{
|
{
|
||||||
#if OSX_DEPLOYMENT_TARGET >= 1014
|
#if OSX_DEPLOYMENT_TARGET >= 1014
|
||||||
|
|
@ -107,3 +114,44 @@ void macOSNativeHide()
|
||||||
[NSApp hide:nil];
|
[NSApp hide:nil];
|
||||||
[[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyAccessory];
|
[[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyAccessory];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NSApplicationTerminateReply deskflow_applicationShouldTerminate(id self, SEL _cmd, NSApplication *sender)
|
||||||
|
{
|
||||||
|
// Don't intercept a system shutdown (or logoff/restart)
|
||||||
|
if (!s_isSystemShuttingDown && s_shouldQuit && !s_shouldQuit()) {
|
||||||
|
return NSTerminateCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute Qt's applicationShouldTerminate
|
||||||
|
if (s_originalShouldTerminate) {
|
||||||
|
using ShouldTerminateFn = NSApplicationTerminateReply (*)(id, SEL, NSApplication *);
|
||||||
|
return reinterpret_cast<ShouldTerminateFn>(s_originalShouldTerminate)(self, _cmd, sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NSTerminateNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void installQuitHandler(std::function<bool()> shouldQuit)
|
||||||
|
{
|
||||||
|
s_shouldQuit = std::move(shouldQuit);
|
||||||
|
|
||||||
|
Class cls = [[NSApp delegate] class];
|
||||||
|
SEL selector = @selector(applicationShouldTerminate:);
|
||||||
|
|
||||||
|
Method method = class_getInstanceMethod(cls, selector);
|
||||||
|
if (method) {
|
||||||
|
s_originalShouldTerminate = method_getImplementation(method);
|
||||||
|
method_setImplementation(method, (IMP)deskflow_applicationShouldTerminate);
|
||||||
|
} else {
|
||||||
|
class_addMethod(cls, selector, (IMP)deskflow_applicationShouldTerminate, "l@:@");
|
||||||
|
}
|
||||||
|
|
||||||
|
// shutdown is also triggered for logout/restart
|
||||||
|
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserverForName:NSWorkspaceWillPowerOffNotification
|
||||||
|
object:nil
|
||||||
|
queue:[NSOperationQueue mainQueue]
|
||||||
|
usingBlock:^(NSNotification *note) {
|
||||||
|
Q_UNUSED(note)
|
||||||
|
s_isSystemShuttingDown = YES;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue