1/*
2This file is part of Telegram Desktop,
3the official desktop application for the Telegram messaging service.
4
5For license and copyright information please follow this link:
6https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7*/
8#include "platform/mac/specific_mac.h"
9
10#include "lang/lang_keys.h"
11#include "mainwidget.h"
12#include "history/history_widget.h"
13#include "core/crash_reports.h"
14#include "core/sandbox.h"
15#include "core/application.h"
16#include "core/core_settings.h"
17#include "storage/localstorage.h"
18#include "window/window_controller.h"
19#include "mainwindow.h"
20#include "history/history_location_manager.h"
21#include "base/platform/mac/base_utilities_mac.h"
22#include "base/platform/base_platform_info.h"
23
24#include <QtGui/QDesktopServices>
25#include <QtWidgets/QApplication>
26
27#include <cstdlib>
28#include <execinfo.h>
29#include <sys/xattr.h>
30
31#include <Cocoa/Cocoa.h>
32#include <CoreFoundation/CFURL.h>
33#include <IOKit/IOKitLib.h>
34#include <IOKit/hidsystem/ev_keymap.h>
35#include <mach-o/dyld.h>
36#include <AVFoundation/AVFoundation.h>
37
38void psActivateProcess(uint64 pid) {
39	if (!pid) {
40		const auto window = Core::App().activeWindow();
41		objc_activateProgram(window ? window->widget()->winId() : 0);
42	}
43}
44
45QString psAppDataPath() {
46	return objc_appDataPath();
47}
48
49void psDoCleanup() {
50	try {
51		Platform::AutostartToggle(false);
52		psSendToMenu(false, true);
53	} catch (...) {
54	}
55}
56
57int psCleanup() {
58	psDoCleanup();
59	return 0;
60}
61
62void psDoFixPrevious() {
63}
64
65int psFixPrevious() {
66	psDoFixPrevious();
67	return 0;
68}
69
70namespace Platform {
71
72void start() {
73	objc_start();
74}
75
76void finish() {
77	objc_finish();
78}
79
80QString SingleInstanceLocalServerName(const QString &hash) {
81#ifndef OS_MAC_STORE
82	return qsl("/tmp/") + hash + '-' + cGUIDStr();
83#else // OS_MAC_STORE
84	return objc_documentsPath() + hash.left(4);
85#endif // OS_MAC_STORE
86}
87
88bool IsDarkMenuBar() {
89	bool result = false;
90	@autoreleasepool {
91
92	NSDictionary *dict = [[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain];
93	id style = [dict objectForKey:Q2NSString(strStyleOfInterface())];
94	BOOL darkModeOn = (style && [style isKindOfClass:[NSString class]] && NSOrderedSame == [style caseInsensitiveCompare:@"dark"]);
95	result = darkModeOn ? true : false;
96
97	}
98	return result;
99}
100
101std::optional<bool> IsDarkMode() {
102	return IsMac10_14OrGreater()
103		? std::make_optional(IsDarkMenuBar())
104		: std::nullopt;
105}
106
107void WriteCrashDumpDetails() {
108#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
109	double v = objc_appkitVersion();
110	CrashReports::dump() << "OS-Version: " << v;
111#endif // DESKTOP_APP_DISABLE_CRASH_REPORTS
112}
113
114// I do check for availability, just not in the exact way clang is content with
115#pragma clang diagnostic push
116#pragma clang diagnostic ignored "-Wunguarded-availability"
117PermissionStatus GetPermissionStatus(PermissionType type) {
118	switch (type) {
119	case PermissionType::Microphone:
120	case PermissionType::Camera:
121		const auto nativeType = (type == PermissionType::Microphone)
122			? AVMediaTypeAudio
123			: AVMediaTypeVideo;
124		if ([AVCaptureDevice respondsToSelector: @selector(authorizationStatusForMediaType:)]) { // Available starting with 10.14
125			switch ([AVCaptureDevice authorizationStatusForMediaType:nativeType]) {
126				case AVAuthorizationStatusNotDetermined:
127					return PermissionStatus::CanRequest;
128				case AVAuthorizationStatusAuthorized:
129					return PermissionStatus::Granted;
130				case AVAuthorizationStatusDenied:
131				case AVAuthorizationStatusRestricted:
132					return PermissionStatus::Denied;
133			}
134		}
135		break;
136	}
137	return PermissionStatus::Granted;
138}
139
140void RequestPermission(PermissionType type, Fn<void(PermissionStatus)> resultCallback) {
141	switch (type) {
142	case PermissionType::Microphone:
143	case PermissionType::Camera:
144		const auto nativeType = (type == PermissionType::Microphone)
145			? AVMediaTypeAudio
146			: AVMediaTypeVideo;
147		if ([AVCaptureDevice respondsToSelector: @selector(requestAccessForMediaType:completionHandler:)]) { // Available starting with 10.14
148			[AVCaptureDevice requestAccessForMediaType:nativeType completionHandler:^(BOOL granted) {
149				crl::on_main([=] {
150					resultCallback(granted ? PermissionStatus::Granted : PermissionStatus::Denied);
151				});
152			}];
153		}
154		break;
155	}
156	resultCallback(PermissionStatus::Granted);
157}
158#pragma clang diagnostic pop // -Wunguarded-availability
159
160void OpenSystemSettingsForPermission(PermissionType type) {
161	switch (type) {
162	case PermissionType::Microphone:
163		[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"]];
164		break;
165	case PermissionType::Camera:
166		[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_Camera"]];
167		break;
168	}
169}
170
171bool OpenSystemSettings(SystemSettingsType type) {
172	switch (type) {
173	case SystemSettingsType::Audio:
174		[[NSWorkspace sharedWorkspace] openFile:@"/System/Library/PreferencePanes/Sound.prefPane"];
175		break;
176	}
177	return true;
178}
179
180void IgnoreApplicationActivationRightNow() {
181	objc_ignoreApplicationActivationRightNow();
182}
183
184void AutostartToggle(bool enabled, Fn<void(bool)> done) {
185	if (done) {
186		done(false);
187	}
188}
189
190bool AutostartSkip() {
191	return !cAutoStart();
192}
193
194} // namespace Platform
195
196void psNewVersion() {
197}
198
199void psSendToMenu(bool send, bool silent) {
200}
201
202void psDownloadPathEnableAccess() {
203	objc_downloadPathEnableAccess(Core::App().settings().downloadPathBookmark());
204}
205
206QByteArray psDownloadPathBookmark(const QString &path) {
207	return objc_downloadPathBookmark(path);
208}
209
210bool psLaunchMaps(const Data::LocationPoint &point) {
211	return QDesktopServices::openUrl(qsl("https://maps.apple.com/?q=Point&z=16&ll=%1,%2").arg(point.latAsString()).arg(point.lonAsString()));
212}
213
214QString strNotificationAboutThemeChange() {
215	const uint32 letters[] = { 0x75E86256, 0xD03E11B1, 0x4D92201D, 0xA2144987, 0x99D5B34F, 0x037589C3, 0x38ED2A7C, 0xD2371ABC, 0xDC98BB02, 0x27964E1B, 0x01748AED, 0xE06679F8, 0x761C9580, 0x4F2595BF, 0x6B5FCBF4, 0xE4D9C24E, 0xBA2F6AB5, 0xE6E3FA71, 0xF2CFC255, 0x56A50C19, 0x43AE1239, 0x77CA4254, 0x7D189A89, 0xEA7663EE, 0x84CEB554, 0xA0ADF236, 0x886512D4, 0x7D3FBDAF, 0x85C4BE4F, 0x12C8255E, 0x9AD8BD41, 0xAC154683, 0xB117598B, 0xDFD9F947, 0x63F06C7B, 0x6340DCD6, 0x3AAE6B3E, 0x26CB125A };
216	return Platform::MakeFromLetters(letters);
217}
218
219QString strNotificationAboutScreenLocked() {
220	const uint32 letters[] = { 0x34B47F28, 0x47E95179, 0x73D05C42, 0xB4E2A933, 0x924F22D1, 0x4265D8EA, 0x9E4D2CC2, 0x02E8157B, 0x35BF7525, 0x75901A41, 0xB0400FCC, 0xE801169D, 0x4E04B589, 0xC1CEF054, 0xAB2A7EB0, 0x5C67C4F6, 0xA4E2B954, 0xB35E12D2, 0xD598B22B, 0x4E3B8AAB, 0xBEA5E439, 0xFDA8AA3C, 0x1632DBA8, 0x88FE8965 };
221	return Platform::MakeFromLetters(letters);
222}
223
224QString strNotificationAboutScreenUnlocked() {
225	const uint32 letters[] = { 0xF897900B, 0x19A04630, 0x144DA6DF, 0x643CA7ED, 0x81DDA343, 0x88C6B149, 0x5F9A3A15, 0x31804E13, 0xDF2202B8, 0x9BD1B500, 0x61B92735, 0x7DDF5D43, 0xB74E06C3, 0x16FF1665, 0x9098F702, 0x4461DAF0, 0xA3134FA5, 0x52B01D3C, 0x6BC35769, 0xA7CC945D, 0x8B5327C0, 0x7630B9A0, 0x4E52E3CE, 0xED7765E3, 0xCEB7862D, 0xA06B34F0 };
226	return Platform::MakeFromLetters(letters);
227}
228
229QString strStyleOfInterface() {
230	const uint32 letters[] = { 0x3BBB7F05, 0xED4C5EC3, 0xC62C15A3, 0x5D10B283, 0x1BB35729, 0x63FB674D, 0xDBE5C174, 0x401EA195, 0x87B0C82A, 0x311BD596, 0x7063ECFA, 0x4AB90C27, 0xDA587DC4, 0x0B6296F8, 0xAA5603FA, 0xE1140A9F, 0x3D12D094, 0x339B5708, 0x712BA5B1 };
231	return Platform::MakeFromLetters(letters);
232}
233
234QString strTitleWrapClass() {
235	const uint32 letters[] = { 0x066C95DD, 0xA289D425, 0x000EF1A5, 0xB53C76AA, 0x5096391D, 0x212BF5B8, 0xE6BCA526, 0x2A5B8EC6, 0xC1457BDB, 0xA1BEE033, 0xA8ADFA11, 0xFF151585, 0x36EC257D, 0x4D96241D, 0xD0341BAA, 0xDE2908BF, 0xFE7978E8, 0x26875E1D, 0x70DA5557, 0x14C02B69, 0x7EFF7E69, 0x008D7217, 0x5EB01138 };
236	return Platform::MakeFromLetters(letters);
237}
238
239QString strTitleClass() {
240	const uint32 letters[] = { 0x1054BBE5, 0xA39FC333, 0x54B51E1E, 0x24895213, 0x50B71830, 0xBF07478C, 0x10BA5503, 0x5C70D3E6, 0x65079D9D, 0xACAAF939, 0x6A56C3CD };
241	return Platform::MakeFromLetters(letters);
242}
243