1/* 2 This file is part of Warzone 2100. 3 Copyright (C) 1999-2004 Eidos Interactive 4 Copyright (C) 2005-2020 Warzone 2100 Project 5 6 Warzone 2100 is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 Warzone 2100 is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with Warzone 2100; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#import "cocoa_wrapper.h" 22 23#ifdef WZ_OS_MAC 24 25#if ! __has_feature(objc_arc) 26#error "Objective-C ARC (Automatic Reference Counting) is off" 27#endif 28 29#import <AppKit/AppKit.h> 30#import <ApplicationServices/ApplicationServices.h> 31 32static inline NSString * _Nonnull nsstringify(const char *str) 33{ 34 NSString * nsString = [NSString stringWithUTF8String:str]; 35 if (nsString == nil) 36 { 37 return @"stringWithUTF8String failed"; 38 } 39 return nsString; 40} 41 42int cocoaShowAlert(const char *message, const char *information, unsigned style, 43 const char *buttonTitle, ...) 44{ 45 NSInteger buttonID = -1; 46 @autoreleasepool { 47 NSAlert *alert = [[NSAlert alloc] init]; 48 [alert setMessageText:nsstringify(message)]; 49 [alert setInformativeText:nsstringify(information)]; 50 [alert setAlertStyle:(NSAlertStyle)style]; 51 52 va_list args; 53 va_start(args, buttonTitle); 54 const char *currentButtonTitle = buttonTitle; 55 do { 56 [alert addButtonWithTitle:nsstringify(currentButtonTitle)]; 57 } while ((currentButtonTitle = va_arg(args, const char *))); 58 va_end(args); 59 60 buttonID = [alert runModal]; 61 } 62 return buttonID - NSAlertFirstButtonReturn; 63} 64 65bool cocoaSelectFileInFinder(const char *filename) 66{ 67 if (filename == nullptr) return false; 68 BOOL success = NO; 69 @autoreleasepool { 70 success = [[NSWorkspace sharedWorkspace] selectFile:nsstringify(filename) inFileViewerRootedAtPath:@""]; 71 } 72 return success; 73} 74 75bool cocoaSelectFolderInFinder(const char* path) 76{ 77 if (path == nullptr) return false; 78 BOOL success = NO; 79 @autoreleasepool { 80 NSURL *pathURL = [NSURL fileURLWithPath:nsstringify(path) isDirectory:YES]; 81 if (pathURL == nil) return false; 82 success = [[NSWorkspace sharedWorkspace] openURL:pathURL]; 83 } 84 return success; 85} 86 87bool cocoaOpenURL(const char *url) 88{ 89 assert(url != nullptr); 90 BOOL success = NO; 91 @autoreleasepool { 92 success = [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:nsstringify(url)]]; 93 } 94 return success; 95} 96 97bool cocoaOpenUserCrashReportFolder() 98{ 99 BOOL success = NO; 100 @autoreleasepool { 101 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); 102 NSString *libraryPath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; 103 if (libraryPath == nil) return false; 104 NSURL *libraryURL = [NSURL fileURLWithPath:libraryPath isDirectory:YES]; 105 NSURL *crashReportsURL = [NSURL URLWithString:@"Logs/DiagnosticReports" relativeToURL:libraryURL]; 106 success = [[NSWorkspace sharedWorkspace] openURL:crashReportsURL]; 107 } 108 return success; 109} 110 111bool cocoaGetApplicationSupportDir(char *const tmpstr, size_t const size) 112{ 113 @autoreleasepool { 114 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, TRUE); 115 NSString *path = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; 116 if (path == nil) return false; 117 BOOL success = [path getCString:tmpstr maxLength:size encoding:NSUTF8StringEncoding]; 118 return success; 119 } 120} 121 122bool cocoaSetFileQuarantineAttribute(const char *path) 123{ 124 @autoreleasepool { 125 // kLSQuarantineTypeKey -> kLSQuarantineTypeOtherDownload 126 NSDictionary * quarantineProperties = @{ 127 (NSString *)kLSQuarantineTypeKey : (NSString *)kLSQuarantineTypeOtherDownload 128 }; 129 130// if (@available(macOS 10.10, *)) { // "@available" is only available on Xcode 9+ 131 if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_10) { // alternative to @available 132 NSURL *fileURL = [NSURL fileURLWithFileSystemRepresentation:path isDirectory:NO relativeToURL:NULL]; 133 if (fileURL == nil) return false; 134 135#pragma clang diagnostic push 136#pragma clang diagnostic ignored "-Wunguarded-availability" // only required until we can use @available 137 NSError *error = nil; 138 BOOL success = [fileURL setResourceValue:quarantineProperties forKey:NSURLQuarantinePropertiesKey error:&error]; 139#pragma clang diagnostic pop 140 if (!success) 141 { 142 // Failed to set resource value 143 NSLog(@"Failed to set resource file: %@", error); 144 return false; 145 } 146 } else { 147 // macOS 10.9 and earlier require now-deprecated APIs 148 return false; 149 } 150 return true; 151 } 152} 153 154bool TransformProcessState(ProcessApplicationTransformState newState) 155{ 156 @autoreleasepool { 157 ProcessSerialNumber psn = {0, kCurrentProcess}; 158 OSStatus result = TransformProcessType(&psn, newState); 159 160 if (result != 0) 161 { 162 NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:result userInfo:nil]; 163 NSLog(@"TransformProcessType failed with error - %@", error); 164 } 165 return (result == 0); 166 } 167} 168 169bool cocoaTransformToBackgroundApplication() 170{ 171 return TransformProcessState(kProcessTransformToBackgroundApplication); 172} 173 174#endif // WZ_OS_MAC 175