2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file macos.mm Code related to MacOSX. */
10 #include "../../stdafx.h"
11 #include "../../core/bitmath_func.hpp"
12 #include "../../rev.h"
14 #include "../../string_func.h"
15 #include "../../fileio_func.h"
19 #define Point OTTDPoint
20 #include <AppKit/AppKit.h>
29 * This file contains objective C
30 * Apple uses objective C instead of plain C to interact with OS specific/native functions
34 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10)
36 NSInteger majorVersion;
37 NSInteger minorVersion;
38 NSInteger patchVersion;
39 } OTTDOperatingSystemVersion;
41 #define NSOperatingSystemVersion OTTDOperatingSystemVersion
45 static NSAutoreleasePool *_ottd_autorelease_pool;
49 * Get the version of the MacOS we are running under. Code adopted
50 * from http://www.cocoadev.com/index.pl?DeterminingOSVersion
51 * @param return_major major version of the os. This would be 10 in the case of 10.4.11
52 * @param return_minor minor version of the os. This would be 4 in the case of 10.4.11
53 * @param return_bugfix bugfix version of the os. This would be 11 in the case of 10.4.11
54 * A return value of -1 indicates that something went wrong and we don't know.
56 void GetMacOSVersion(int *return_major, int *return_minor, int *return_bugfix)
62 if ([[ NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion) ]) {
63 IMP sel = [ [ NSProcessInfo processInfo] methodForSelector:@selector(operatingSystemVersion) ];
64 NSOperatingSystemVersion ver = ((NSOperatingSystemVersion (*)(id, SEL))sel)([ NSProcessInfo processInfo], @selector(operatingSystemVersion));
66 *return_major = (int)ver.majorVersion;
67 *return_minor = (int)ver.minorVersion;
68 *return_bugfix = (int)ver.patchVersion;
73 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10)
75 # pragma clang diagnostic push
76 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
78 SInt32 systemVersion, version_major, version_minor, version_bugfix;
79 if (Gestalt(gestaltSystemVersion, &systemVersion) == noErr) {
80 if (systemVersion >= 0x1040) {
81 if (Gestalt(gestaltSystemVersionMajor, &version_major) == noErr) *return_major = (int)version_major;
82 if (Gestalt(gestaltSystemVersionMinor, &version_minor) == noErr) *return_minor = (int)version_minor;
83 if (Gestalt(gestaltSystemVersionBugFix, &version_bugfix) == noErr) *return_bugfix = (int)version_bugfix;
85 *return_major = (int)(GB(systemVersion, 12, 4) * 10 + GB(systemVersion, 8, 4));
86 *return_minor = (int)GB(systemVersion, 4, 4);
87 *return_bugfix = (int)GB(systemVersion, 0, 4);
91 # pragma clang diagnostic pop
98 extern void CocoaDialog(const char *title, const char *message, const char *buttonLabel);
101 * Show the system dialogue message (Cocoa on MacOSX).
103 * @param title Window title.
104 * @param message Message text.
105 * @param buttonLabel Button text.
107 void ShowMacDialog(const char *title, const char *message, const char *buttonLabel)
109 CocoaDialog(title, message, buttonLabel);
116 * Show the system dialogue message (console on MacOSX).
118 * @param title Window title.
119 * @param message Message text.
120 * @param buttonLabel Button text.
122 void ShowMacDialog(const char *title, const char *message, const char *buttonLabel)
124 fmt::print(stderr, "{}: {}\n", title, message);
131 * Show an error message.
133 * @param buf error message text.
134 * @param system message text originates from OS.
136 void ShowOSErrorBox(const char *buf, bool system)
138 /* Display the error in the best way possible. */
140 ShowMacDialog("OpenTTD has encountered an error", buf, "Quit");
142 ShowMacDialog(buf, "See the readme for more info.", "Quit");
146 void OSOpenBrowser(const std::string &url)
148 [ [ NSWorkspace sharedWorkspace ] openURL:[ NSURL URLWithString:[ NSString stringWithUTF8String:url.c_str() ] ] ];
152 * Determine and return the current user's locale.
154 const char *GetCurrentLocale(const char *)
156 static char retbuf[32] = { '\0' };
157 NSUserDefaults *defs = [ NSUserDefaults standardUserDefaults ];
158 NSArray *languages = [ defs objectForKey:@"AppleLanguages" ];
159 NSString *preferredLang = [ languages objectAtIndex:0 ];
160 /* preferredLang is either 2 or 5 characters long ("xx" or "xx_YY"). */
162 [ preferredLang getCString:retbuf maxLength:32 encoding:NSASCIIStringEncoding ];
170 * Return the contents of the clipboard (COCOA).
172 * @return The (optional) clipboard contents.
174 std::optional<std::string> GetClipboardContents()
176 NSPasteboard *pb = [ NSPasteboard generalPasteboard ];
177 NSArray *types = [ NSArray arrayWithObject:NSPasteboardTypeString ];
178 NSString *bestType = [ pb availableTypeFromArray:types ];
180 /* Clipboard has no text data available. */
181 if (bestType == nil) return std::nullopt;
183 NSString *string = [ pb stringForType:NSPasteboardTypeString ];
184 if (string == nil || [ string length ] == 0) return std::nullopt;
186 return [ string UTF8String ];
189 /** Set the application's bundle directory.
191 * This is needed since OS X application bundles do not have a
192 * current directory and the data files are 'somewhere' in the bundle.
194 void CocoaSetApplicationBundleDir()
196 extern std::array<std::string, NUM_SEARCHPATHS> _searchpaths;
198 char tmp[MAXPATHLEN];
199 CFAutoRelease<CFURLRef> url(CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle()));
200 if (CFURLGetFileSystemRepresentation(url.get(), true, (unsigned char *)tmp, MAXPATHLEN)) {
201 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = tmp;
202 AppendPathSeparator(_searchpaths[SP_APPLICATION_BUNDLE_DIR]);
204 _searchpaths[SP_APPLICATION_BUNDLE_DIR].clear();
209 * Setup autorelease for the application pool.
211 * These are called from main() to prevent a _NSAutoreleaseNoPool error when
212 * exiting before the cocoa video driver has been loaded
214 void CocoaSetupAutoreleasePool()
216 _ottd_autorelease_pool = [ [ NSAutoreleasePool alloc ] init ];
220 * Autorelease the application pool.
222 void CocoaReleaseAutoreleasePool()
224 [ _ottd_autorelease_pool release ];
230 * Check if a font is a monospace font.
231 * @param name Name of the font.
232 * @return True if the font is a monospace font.
234 bool IsMonospaceFont(CFStringRef name)
236 NSFont *font = [ NSFont fontWithName:(__bridge NSString *)name size:0.0f ];
238 return font != nil ? [ font isFixedPitch ] : false;
242 * Set the name of the current thread for the debugger.
243 * @param name The new name of the current thread.
245 void MacOSSetThreadName(const char *name)
247 if (MacOSVersionIsAtLeast(10, 6, 0)) {
248 pthread_setname_np(name);
251 NSThread *cur = [ NSThread currentThread ];
252 if (cur != nil && [ cur respondsToSelector:@selector(setName:) ]) {
253 [ cur performSelector:@selector(setName:) withObject:[ NSString stringWithUTF8String:name ] ];
257 uint64_t MacOSGetPhysicalMemory()
259 return [ [ NSProcessInfo processInfo ] physicalMemory ];