init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
This commit is contained in:
302
Telegram/SourceFiles/platform/mac/specific_mac.mm
Normal file
302
Telegram/SourceFiles/platform/mac/specific_mac.mm
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
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.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "history/history_widget.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "mainwindow.h"
|
||||
#include "history/history_location_manager.h"
|
||||
#include "base/platform/mac/base_confirm_quit.h"
|
||||
#include "base/platform/mac/base_utilities_mac.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "main/main_session.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "base/options.h"
|
||||
|
||||
#include <QtGui/QDesktopServices>
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <execinfo.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <CoreFoundation/CFURL.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/hidsystem/ev_keymap.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <AVFoundation/AVFoundation.h>
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QImage ImageFromNS(NSImage *icon) {
|
||||
CGImageRef image = [icon CGImageForProposedRect:NULL context:nil hints:nil];
|
||||
|
||||
const int width = CGImageGetWidth(image);
|
||||
const int height = CGImageGetHeight(image);
|
||||
auto result = QImage(width, height, QImage::Format_ARGB32_Premultiplied);
|
||||
result.fill(Qt::transparent);
|
||||
|
||||
CGColorSpaceRef space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
||||
CGBitmapInfo info = CGBitmapInfo(kCGImageAlphaPremultipliedFirst) | kCGBitmapByteOrder32Host;
|
||||
CGContextRef context = CGBitmapContextCreate(
|
||||
result.bits(),
|
||||
width,
|
||||
height,
|
||||
8,
|
||||
result.bytesPerLine(),
|
||||
space,
|
||||
info);
|
||||
|
||||
CGRect rect = CGRectMake(0, 0, width, height);
|
||||
CGContextDrawImage(context, rect, image);
|
||||
|
||||
CFRelease(space);
|
||||
CFRelease(context);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage ResolveBundleIconDefault() {
|
||||
NSString *path = [[NSBundle mainBundle] bundlePath];
|
||||
NSString *icon = [path stringByAppendingString:@"/Contents/Resources/Icon.icns"];
|
||||
NSImage *image = [[NSImage alloc] initWithContentsOfFile:icon];
|
||||
if (!image) {
|
||||
return Window::Logo();
|
||||
}
|
||||
|
||||
auto result = ImageFromNS(image);
|
||||
[image release];
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QString psAppDataPath() {
|
||||
return objc_appDataPath();
|
||||
}
|
||||
|
||||
void psDoCleanup() {
|
||||
try {
|
||||
Platform::AutostartToggle(false);
|
||||
psSendToMenu(false, true);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
int psCleanup() {
|
||||
psDoCleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psDoFixPrevious() {
|
||||
}
|
||||
|
||||
int psFixPrevious() {
|
||||
psDoFixPrevious();
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace Platform {
|
||||
|
||||
void start() {
|
||||
objc_start();
|
||||
}
|
||||
|
||||
void finish() {
|
||||
objc_finish();
|
||||
}
|
||||
|
||||
QString SingleInstanceLocalServerName(const QString &hash) {
|
||||
#ifndef OS_MAC_STORE
|
||||
return u"/tmp/"_q + hash + '-' + cGUIDStr();
|
||||
#else // OS_MAC_STORE
|
||||
return objc_documentsPath() + hash.left(4);
|
||||
#endif // OS_MAC_STORE
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
|
||||
namespace {
|
||||
|
||||
QString strStyleOfInterface() {
|
||||
const uint32 letters[] = { 0x3BBB7F05, 0xED4C5EC3, 0xC62C15A3, 0x5D10B283, 0x1BB35729, 0x63FB674D, 0xDBE5C174, 0x401EA195, 0x87B0C82A, 0x311BD596, 0x7063ECFA, 0x4AB90C27, 0xDA587DC4, 0x0B6296F8, 0xAA5603FA, 0xE1140A9F, 0x3D12D094, 0x339B5708, 0x712BA5B1 };
|
||||
return Platform::MakeFromLetters(letters);
|
||||
}
|
||||
|
||||
bool IsDarkMenuBar() {
|
||||
bool result = false;
|
||||
@autoreleasepool {
|
||||
|
||||
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain];
|
||||
id style = [dict objectForKey:Q2NSString(strStyleOfInterface())];
|
||||
BOOL darkModeOn = (style && [style isKindOfClass:[NSString class]] && NSOrderedSame == [style caseInsensitiveCompare:@"dark"]);
|
||||
result = darkModeOn ? true : false;
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<bool> IsDarkMode() {
|
||||
return IsMac10_14OrGreater()
|
||||
? std::make_optional(IsDarkMenuBar())
|
||||
: std::nullopt;
|
||||
}
|
||||
#endif // Qt < 6.5.0
|
||||
|
||||
void WriteCrashDumpDetails() {
|
||||
#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
|
||||
double v = objc_appkitVersion();
|
||||
CrashReports::dump() << "OS-Version: " << v;
|
||||
#endif // TDESKTOP_DISABLE_CRASH_REPORTS
|
||||
}
|
||||
|
||||
// I do check for availability, just not in the exact way clang is content with
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
PermissionStatus GetPermissionStatus(PermissionType type) {
|
||||
switch (type) {
|
||||
case PermissionType::Microphone:
|
||||
case PermissionType::Camera:
|
||||
const auto nativeType = (type == PermissionType::Microphone)
|
||||
? AVMediaTypeAudio
|
||||
: AVMediaTypeVideo;
|
||||
if ([AVCaptureDevice respondsToSelector: @selector(authorizationStatusForMediaType:)]) { // Available starting with 10.14
|
||||
switch ([AVCaptureDevice authorizationStatusForMediaType:nativeType]) {
|
||||
case AVAuthorizationStatusNotDetermined:
|
||||
return PermissionStatus::CanRequest;
|
||||
case AVAuthorizationStatusAuthorized:
|
||||
return PermissionStatus::Granted;
|
||||
case AVAuthorizationStatusDenied:
|
||||
case AVAuthorizationStatusRestricted:
|
||||
return PermissionStatus::Denied;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return PermissionStatus::Granted;
|
||||
}
|
||||
|
||||
void RequestPermission(PermissionType type, Fn<void(PermissionStatus)> resultCallback) {
|
||||
switch (type) {
|
||||
case PermissionType::Microphone:
|
||||
case PermissionType::Camera:
|
||||
const auto nativeType = (type == PermissionType::Microphone)
|
||||
? AVMediaTypeAudio
|
||||
: AVMediaTypeVideo;
|
||||
if ([AVCaptureDevice respondsToSelector: @selector(requestAccessForMediaType:completionHandler:)]) { // Available starting with 10.14
|
||||
[AVCaptureDevice requestAccessForMediaType:nativeType completionHandler:^(BOOL granted) {
|
||||
crl::on_main([=] {
|
||||
resultCallback(granted ? PermissionStatus::Granted : PermissionStatus::Denied);
|
||||
});
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
resultCallback(PermissionStatus::Granted);
|
||||
}
|
||||
#pragma clang diagnostic pop // -Wunguarded-availability
|
||||
|
||||
void OpenSystemSettingsForPermission(PermissionType type) {
|
||||
switch (type) {
|
||||
case PermissionType::Microphone:
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"]];
|
||||
break;
|
||||
case PermissionType::Camera:
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_Camera"]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenSystemSettings(SystemSettingsType type) {
|
||||
switch (type) {
|
||||
case SystemSettingsType::Audio:
|
||||
[[NSWorkspace sharedWorkspace] openFile:@"/System/Library/PreferencePanes/Sound.prefPane"];
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IgnoreApplicationActivationRightNow() {
|
||||
objc_ignoreApplicationActivationRightNow();
|
||||
}
|
||||
|
||||
void AutostartToggle(bool enabled, Fn<void(bool)> done) {
|
||||
if (done) {
|
||||
done(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool AutostartSkip() {
|
||||
return !cAutoStart();
|
||||
}
|
||||
|
||||
void NewVersionLaunched(int oldVersion) {
|
||||
if (const auto window = Core::App().activeWindow()) {
|
||||
if (const auto controller = window->sessionController()) {
|
||||
const auto userId = controller->session().userId().bare;
|
||||
const auto hash = std::hash<uint64>{}(userId);
|
||||
if ((hash % 100) < 15 || (userId % 100) == 91) {
|
||||
base::options::lookup<bool>("text-recognition-mac").set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QImage DefaultApplicationIcon() {
|
||||
static auto result = ResolveBundleIconDefault();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PreventsQuit(Core::QuitReason reason) {
|
||||
// Thanks Chromium, see
|
||||
// chromium.org/developers/design-documents/confirm-to-quit-experiment
|
||||
return (reason == Core::QuitReason::QtQuitEvent)
|
||||
&& Core::App().settings().macWarnBeforeQuit()
|
||||
&& ([[NSApp currentEvent] type] == NSEventTypeKeyDown)
|
||||
&& !ConfirmQuit::RunModal(
|
||||
tr::lng_mac_hold_to_quit(
|
||||
tr::now,
|
||||
lt_text,
|
||||
ConfirmQuit::QuitKeysString()));
|
||||
}
|
||||
|
||||
void ActivateThisProcess() {
|
||||
const auto window = Core::App().activeWindow();
|
||||
objc_activateProgram(window ? window->widget()->winId() : 0);
|
||||
}
|
||||
|
||||
void LaunchMaps(const Data::LocationPoint &point, Fn<void()> fail) {
|
||||
if (!QDesktopServices::openUrl(
|
||||
u"https://maps.apple.com/?q=Point&z=16&ll=%1,%2"_q.arg(
|
||||
point.latAsString(),
|
||||
point.lonAsString()))) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Platform
|
||||
|
||||
void psSendToMenu(bool send, bool silent) {
|
||||
}
|
||||
|
||||
void psDownloadPathEnableAccess() {
|
||||
objc_downloadPathEnableAccess(Core::App().settings().downloadPathBookmark());
|
||||
}
|
||||
|
||||
QByteArray psDownloadPathBookmark(const QString &path) {
|
||||
return objc_downloadPathBookmark(path);
|
||||
}
|
||||
Reference in New Issue
Block a user