/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/mac/specific_mac_p.h" #include "mainwindow.h" #include "mainwidget.h" #include "calls/calls_instance.h" #include "core/sandbox.h" #include "core/application.h" #include "core/core_settings.h" #include "core/crash_reports.h" #include "menu/menu_dock.h" #include "storage/localstorage.h" #include "media/audio/media_audio.h" #include "window/window_controller.h" #include "base/platform/mac/base_utilities_mac.h" #include "base/platform/base_platform_info.h" #include "base/timer.h" #include "styles/style_window.h" #include "platform/platform_specific.h" #include #include #include #if __has_include() #include #endif // __has_include() #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) #include #endif // Qt < 6.6.0 #include #include #include #include // AyuGram includes #include "ayu/ayu_settings.h" using Platform::Q2NSString; using Platform::NS2QString; namespace { constexpr auto kIgnoreActivationTimeoutMs = 500; void SetupDockMenu() { static const auto DockMenu = std::make_unique(); QObject::connect(DockMenu.get(), &QMenu::aboutToShow, [] { Menu::RefreshDockMenu(DockMenu.get()); }); DockMenu->setAsDockMenu(); } } // namespace @interface qVisualize : NSObject { } + (id)str:(const QString &)str; - (id)initWithString:(const QString &)str; + (id)bytearr:(const QByteArray &)arr; - (id)initWithByteArray:(const QByteArray &)arr; - (id)debugQuickLookObject; @end // @interface qVisualize @implementation qVisualize { NSString *value; } + (id)bytearr:(const QByteArray &)arr { return [[qVisualize alloc] initWithByteArray:arr]; } - (id)initWithByteArray:(const QByteArray &)arr { if (self = [super init]) { value = [NSString stringWithUTF8String:arr.constData()]; } return self; } + (id)str:(const QString &)str { return [[qVisualize alloc] initWithString:str]; } - (id)initWithString:(const QString &)str { if (self = [super init]) { value = [NSString stringWithUTF8String:str.toUtf8().constData()]; } return self; } - (id)debugQuickLookObject { return value; } @end // @implementation qVisualize @interface ApplicationDelegate : NSObject { } - (BOOL) applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag; - (void) applicationDidBecomeActive:(NSNotification *)aNotification; - (void) applicationDidResignActive:(NSNotification *)aNotification; - (void) receiveWakeNote:(NSNotification*)note; - (void) ignoreApplicationActivationRightNow; @end // @interface ApplicationDelegate ApplicationDelegate *_sharedDelegate = nil; @implementation ApplicationDelegate { bool _ignoreActivation; base::Timer _ignoreActivationStop; } - (instancetype) init { _ignoreActivation = false; _ignoreActivationStop.setCallback([self] { _ignoreActivation = false; }); return [super init]; } - (BOOL) applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag { Core::Sandbox::Instance().customEnterFromEventLoop([&] { if (Core::IsAppLaunched()) { if (const auto window = Core::App().activeWindow()) { if (window->widget()->isHidden()) { window->widget()->showFromTray(); } } } }); return YES; } - (void) applicationDidBecomeActive:(NSNotification *)aNotification { Core::Sandbox::Instance().customEnterFromEventLoop([&] { if (Core::IsAppLaunched() && !_ignoreActivation) { Core::App().handleAppActivated(); if (const auto window = Core::App().activeWindow()) { if (window->widget()->isHidden()) { if (Core::App().calls().hasVisiblePanel()) { Core::App().calls().activateCurrentCall(); } else { window->widget()->showFromTray(); } } } } }); } - (void) applicationDidResignActive:(NSNotification *)aNotification { } - (void) receiveWakeNote:(NSNotification*)aNotification { if (!Core::IsAppLaunched()) { return; } Core::Sandbox::Instance().customEnterFromEventLoop([&] { Core::App().checkLocalTime(); LOG(("Audio Info: " "-receiveWakeNote: received, scheduling detach from audio device")); Media::Audio::ScheduleDetachFromDeviceSafe(); #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) Core::App().settings().setSystemDarkMode(Platform::IsDarkMode()); #elif QT_VERSION < QT_VERSION_CHECK(6, 6, 0) // Qt < 6.5.0 QWindowSystemInterface::handleThemeChange(); #endif // Qt < 6.6.0 }); } - (void) ignoreApplicationActivationRightNow { _ignoreActivation = true; _ignoreActivationStop.callOnce(kIgnoreActivationTimeoutMs); } @end // @implementation ApplicationDelegate namespace Platform { void SetApplicationIcon(const QIcon &icon) { const auto &settings = AyuSettings::getInstance(); if (settings.appIcon().isEmpty()) { return; } const auto name = QString("AppIcon-") + settings.appIcon()[0].toUpper() + settings.appIcon().mid(1); @autoreleasepool { if (name == QString("AppIcon-Default")) { NSApplication.sharedApplication.applicationIconImage = nil; return; } // NSString *iconPath = [NSBundle.mainBundle pathForResource:Q2NSString(name) ofType:@"icns"]; // NSImage *icon = [[NSImage alloc] initWithContentsOfFile:iconPath]; NSImage *icon = [NSBundle.mainBundle imageForResource:Q2NSString(name)]; if (icon) { NSApplication.sharedApplication.applicationIconImage = icon; } } } } // namespace Platform void objc_debugShowAlert(const QString &str) { @autoreleasepool { NSAlert *alert = [[NSAlert alloc] init]; alert.messageText = @"Debug Message"; alert.informativeText = Q2NSString(str); [alert runModal]; } } void objc_outputDebugString(const QString &str) { @autoreleasepool { NSLog(@"%@", Q2NSString(str)); } } void objc_start() { // Patch: Fix macOS regression. On 10.14.4, it crashes on GPU switches. // See https://bugreports.qt.io/browse/QTCREATORBUG-22215 const auto version = QOperatingSystemVersion::current(); if (version.majorVersion() == 10 && version.minorVersion() == 14 && version.microVersion() == 4) { qputenv("QT_MAC_PRO_WEBENGINE_WORKAROUND", "1"); } _sharedDelegate = [[ApplicationDelegate alloc] init]; [[NSApplication sharedApplication] setDelegate:_sharedDelegate]; [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: _sharedDelegate selector: @selector(receiveWakeNote:) name: NSWorkspaceDidWakeNotification object: NULL]; crl::on_main([=] { SetupDockMenu(); }); } void objc_ignoreApplicationActivationRightNow() { if (_sharedDelegate) { [_sharedDelegate ignoreApplicationActivationRightNow]; } } namespace { NSURL *_downloadPathUrl = nil; } void objc_finish() { [_sharedDelegate release]; _sharedDelegate = nil; if (_downloadPathUrl) { [_downloadPathUrl stopAccessingSecurityScopedResource]; _downloadPathUrl = nil; } } void objc_activateProgram(WId winId) { [NSApp activateIgnoringOtherApps:YES]; if (winId) { NSWindow *w = [reinterpret_cast(winId) window]; [w makeKeyAndOrderFront:NSApp]; } } bool objc_moveFile(const QString &from, const QString &to) { @autoreleasepool { NSString *f = Q2NSString(from), *t = Q2NSString(to); if ([[NSFileManager defaultManager] fileExistsAtPath:t]) { NSData *data = [NSData dataWithContentsOfFile:f]; if (data) { if ([data writeToFile:t atomically:YES]) { if ([[NSFileManager defaultManager] removeItemAtPath:f error:nil]) { return true; } } } } else { if ([[NSFileManager defaultManager] moveItemAtPath:f toPath:t error:nil]) { return true; } } } return false; } double objc_appkitVersion() { return NSAppKitVersionNumber; } QString objc_documentsPath() { NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; if (url) { return QString::fromUtf8([[url path] fileSystemRepresentation]) + '/'; } return QString(); } QString objc_appDataPath() { NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; if (url) { return QString::fromUtf8([[url path] fileSystemRepresentation]) + '/' + AppName.utf16() + '/'; } return QString(); } QByteArray objc_downloadPathBookmark(const QString &path) { #ifndef OS_MAC_STORE return QByteArray(); #else // OS_MAC_STORE NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:path.toUtf8().constData()] isDirectory:YES]; if (!url) return QByteArray(); NSError *error = nil; NSData *data = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; return data ? QByteArray::fromNSData(data) : QByteArray(); #endif // OS_MAC_STORE } void objc_downloadPathEnableAccess(const QByteArray &bookmark) { #ifdef OS_MAC_STORE if (bookmark.isEmpty()) return; BOOL isStale = NO; NSError *error = nil; NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark.toNSData() options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; if (!url) return; if ([url startAccessingSecurityScopedResource]) { if (_downloadPathUrl) { [_downloadPathUrl stopAccessingSecurityScopedResource]; } _downloadPathUrl = [url retain]; Core::App().settings().setDownloadPath(NS2QString([_downloadPathUrl path]) + '/'); if (isStale) { NSData *data = [_downloadPathUrl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; if (data) { Core::App().settings().setDownloadPathBookmark(QByteArray::fromNSData(data)); Local::writeSettings(); } } } #endif // OS_MAC_STORE }