specific_mac.mm 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "platform/mac/specific_mac.h"
  8. #include "lang/lang_keys.h"
  9. #include "mainwidget.h"
  10. #include "history/history_widget.h"
  11. #include "core/crash_reports.h"
  12. #include "core/sandbox.h"
  13. #include "core/application.h"
  14. #include "core/core_settings.h"
  15. #include "storage/localstorage.h"
  16. #include "window/window_controller.h"
  17. #include "mainwindow.h"
  18. #include "history/history_location_manager.h"
  19. #include "base/platform/mac/base_confirm_quit.h"
  20. #include "base/platform/mac/base_utilities_mac.h"
  21. #include "base/platform/base_platform_info.h"
  22. #include <QtGui/QDesktopServices>
  23. #include <QtWidgets/QApplication>
  24. #include <cstdlib>
  25. #include <execinfo.h>
  26. #include <sys/xattr.h>
  27. #include <Cocoa/Cocoa.h>
  28. #include <CoreFoundation/CFURL.h>
  29. #include <IOKit/IOKitLib.h>
  30. #include <IOKit/hidsystem/ev_keymap.h>
  31. #include <mach-o/dyld.h>
  32. #include <AVFoundation/AVFoundation.h>
  33. namespace {
  34. [[nodiscard]] QImage ImageFromNS(NSImage *icon) {
  35. CGImageRef image = [icon CGImageForProposedRect:NULL context:nil hints:nil];
  36. const int width = CGImageGetWidth(image);
  37. const int height = CGImageGetHeight(image);
  38. auto result = QImage(width, height, QImage::Format_ARGB32_Premultiplied);
  39. result.fill(Qt::transparent);
  40. CGColorSpaceRef space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
  41. CGBitmapInfo info = CGBitmapInfo(kCGImageAlphaPremultipliedFirst) | kCGBitmapByteOrder32Host;
  42. CGContextRef context = CGBitmapContextCreate(
  43. result.bits(),
  44. width,
  45. height,
  46. 8,
  47. result.bytesPerLine(),
  48. space,
  49. info);
  50. CGRect rect = CGRectMake(0, 0, width, height);
  51. CGContextDrawImage(context, rect, image);
  52. CFRelease(space);
  53. CFRelease(context);
  54. return result;
  55. }
  56. [[nodiscard]] QImage ResolveBundleIconDefault() {
  57. NSString *path = [[NSBundle mainBundle] bundlePath];
  58. NSString *icon = [path stringByAppendingString:@"/Contents/Resources/Icon.icns"];
  59. NSImage *image = [[NSImage alloc] initWithContentsOfFile:icon];
  60. if (!image) {
  61. return Window::Logo();
  62. }
  63. auto result = ImageFromNS(image);
  64. [image release];
  65. return result;
  66. }
  67. } // namespace
  68. QString psAppDataPath() {
  69. return objc_appDataPath();
  70. }
  71. void psDoCleanup() {
  72. try {
  73. Platform::AutostartToggle(false);
  74. psSendToMenu(false, true);
  75. } catch (...) {
  76. }
  77. }
  78. int psCleanup() {
  79. psDoCleanup();
  80. return 0;
  81. }
  82. void psDoFixPrevious() {
  83. }
  84. int psFixPrevious() {
  85. psDoFixPrevious();
  86. return 0;
  87. }
  88. namespace Platform {
  89. void start() {
  90. objc_start();
  91. }
  92. void finish() {
  93. objc_finish();
  94. }
  95. QString SingleInstanceLocalServerName(const QString &hash) {
  96. #ifndef OS_MAC_STORE
  97. return u"/tmp/"_q + hash + '-' + cGUIDStr();
  98. #else // OS_MAC_STORE
  99. return objc_documentsPath() + hash.left(4);
  100. #endif // OS_MAC_STORE
  101. }
  102. #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
  103. namespace {
  104. QString strStyleOfInterface() {
  105. const uint32 letters[] = { 0x3BBB7F05, 0xED4C5EC3, 0xC62C15A3, 0x5D10B283, 0x1BB35729, 0x63FB674D, 0xDBE5C174, 0x401EA195, 0x87B0C82A, 0x311BD596, 0x7063ECFA, 0x4AB90C27, 0xDA587DC4, 0x0B6296F8, 0xAA5603FA, 0xE1140A9F, 0x3D12D094, 0x339B5708, 0x712BA5B1 };
  106. return Platform::MakeFromLetters(letters);
  107. }
  108. bool IsDarkMenuBar() {
  109. bool result = false;
  110. @autoreleasepool {
  111. NSDictionary *dict = [[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain];
  112. id style = [dict objectForKey:Q2NSString(strStyleOfInterface())];
  113. BOOL darkModeOn = (style && [style isKindOfClass:[NSString class]] && NSOrderedSame == [style caseInsensitiveCompare:@"dark"]);
  114. result = darkModeOn ? true : false;
  115. }
  116. return result;
  117. }
  118. } // namespace
  119. std::optional<bool> IsDarkMode() {
  120. return IsMac10_14OrGreater()
  121. ? std::make_optional(IsDarkMenuBar())
  122. : std::nullopt;
  123. }
  124. #endif // Qt < 6.5.0
  125. void WriteCrashDumpDetails() {
  126. #ifndef TDESKTOP_DISABLE_CRASH_REPORTS
  127. double v = objc_appkitVersion();
  128. CrashReports::dump() << "OS-Version: " << v;
  129. #endif // TDESKTOP_DISABLE_CRASH_REPORTS
  130. }
  131. // I do check for availability, just not in the exact way clang is content with
  132. #pragma clang diagnostic push
  133. #pragma clang diagnostic ignored "-Wunguarded-availability"
  134. PermissionStatus GetPermissionStatus(PermissionType type) {
  135. switch (type) {
  136. case PermissionType::Microphone:
  137. case PermissionType::Camera:
  138. const auto nativeType = (type == PermissionType::Microphone)
  139. ? AVMediaTypeAudio
  140. : AVMediaTypeVideo;
  141. if ([AVCaptureDevice respondsToSelector: @selector(authorizationStatusForMediaType:)]) { // Available starting with 10.14
  142. switch ([AVCaptureDevice authorizationStatusForMediaType:nativeType]) {
  143. case AVAuthorizationStatusNotDetermined:
  144. return PermissionStatus::CanRequest;
  145. case AVAuthorizationStatusAuthorized:
  146. return PermissionStatus::Granted;
  147. case AVAuthorizationStatusDenied:
  148. case AVAuthorizationStatusRestricted:
  149. return PermissionStatus::Denied;
  150. }
  151. }
  152. break;
  153. }
  154. return PermissionStatus::Granted;
  155. }
  156. void RequestPermission(PermissionType type, Fn<void(PermissionStatus)> resultCallback) {
  157. switch (type) {
  158. case PermissionType::Microphone:
  159. case PermissionType::Camera:
  160. const auto nativeType = (type == PermissionType::Microphone)
  161. ? AVMediaTypeAudio
  162. : AVMediaTypeVideo;
  163. if ([AVCaptureDevice respondsToSelector: @selector(requestAccessForMediaType:completionHandler:)]) { // Available starting with 10.14
  164. [AVCaptureDevice requestAccessForMediaType:nativeType completionHandler:^(BOOL granted) {
  165. crl::on_main([=] {
  166. resultCallback(granted ? PermissionStatus::Granted : PermissionStatus::Denied);
  167. });
  168. }];
  169. }
  170. break;
  171. }
  172. resultCallback(PermissionStatus::Granted);
  173. }
  174. #pragma clang diagnostic pop // -Wunguarded-availability
  175. void OpenSystemSettingsForPermission(PermissionType type) {
  176. switch (type) {
  177. case PermissionType::Microphone:
  178. [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"]];
  179. break;
  180. case PermissionType::Camera:
  181. [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_Camera"]];
  182. break;
  183. }
  184. }
  185. bool OpenSystemSettings(SystemSettingsType type) {
  186. switch (type) {
  187. case SystemSettingsType::Audio:
  188. [[NSWorkspace sharedWorkspace] openFile:@"/System/Library/PreferencePanes/Sound.prefPane"];
  189. break;
  190. }
  191. return true;
  192. }
  193. void IgnoreApplicationActivationRightNow() {
  194. objc_ignoreApplicationActivationRightNow();
  195. }
  196. void AutostartToggle(bool enabled, Fn<void(bool)> done) {
  197. if (done) {
  198. done(false);
  199. }
  200. }
  201. bool AutostartSkip() {
  202. return !cAutoStart();
  203. }
  204. void NewVersionLaunched(int oldVersion) {
  205. }
  206. QImage DefaultApplicationIcon() {
  207. static auto result = ResolveBundleIconDefault();
  208. return result;
  209. }
  210. bool PreventsQuit(Core::QuitReason reason) {
  211. // Thanks Chromium, see
  212. // chromium.org/developers/design-documents/confirm-to-quit-experiment
  213. return (reason == Core::QuitReason::QtQuitEvent)
  214. && Core::App().settings().macWarnBeforeQuit()
  215. && ([[NSApp currentEvent] type] == NSEventTypeKeyDown)
  216. && !ConfirmQuit::RunModal(
  217. tr::lng_mac_hold_to_quit(
  218. tr::now,
  219. lt_text,
  220. ConfirmQuit::QuitKeysString()));
  221. }
  222. void ActivateThisProcess() {
  223. const auto window = Core::App().activeWindow();
  224. objc_activateProgram(window ? window->widget()->winId() : 0);
  225. }
  226. } // namespace Platform
  227. void psSendToMenu(bool send, bool silent) {
  228. }
  229. void psDownloadPathEnableAccess() {
  230. objc_downloadPathEnableAccess(Core::App().settings().downloadPathBookmark());
  231. }
  232. QByteArray psDownloadPathBookmark(const QString &path) {
  233. return objc_downloadPathBookmark(path);
  234. }
  235. bool psLaunchMaps(const Data::LocationPoint &point) {
  236. return QDesktopServices::openUrl(u"https://maps.apple.com/?q=Point&z=16&ll=%1,%2"_q.arg(point.latAsString()).arg(point.lonAsString()));
  237. }