Add: Overlay cargo icon in vehicle/depot list when holding shift+ctrl. (#12938)
[openttd-github.git] / src / os / macosx / macos.mm
blob1b1beacab494140a78eff5eaad076865028d943b
1 /*
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/>.
6  */
8 /** @file macos.mm Code related to MacOSX. */
10 #include "../../stdafx.h"
11 #include "../../core/bitmath_func.hpp"
12 #include "../../rev.h"
13 #include "macos.h"
14 #include "../../string_func.h"
15 #include "../../fileio_func.h"
16 #include <pthread.h>
18 #define Rect  OTTDRect
19 #define Point OTTDPoint
20 #include <AppKit/AppKit.h>
21 #undef Rect
22 #undef Point
24 #ifndef __clang__
25 #define __bridge
26 #endif
29  * This file contains objective C
30  * Apple uses objective C instead of plain C to interact with OS specific/native functions
31  */
34 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10)
35 typedef struct {
36         NSInteger majorVersion;
37         NSInteger minorVersion;
38         NSInteger patchVersion;
39 } OTTDOperatingSystemVersion;
41 #define NSOperatingSystemVersion OTTDOperatingSystemVersion
42 #endif
44 #ifdef WITH_COCOA
45 static NSAutoreleasePool *_ottd_autorelease_pool;
46 #endif
48 /**
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.
55  */
56 void GetMacOSVersion(int *return_major, int *return_minor, int *return_bugfix)
58         *return_major = -1;
59         *return_minor = -1;
60         *return_bugfix = -1;
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;
70                 return;
71         }
73 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10)
74 #ifdef __clang__
75 #       pragma clang diagnostic push
76 #       pragma clang diagnostic ignored "-Wdeprecated-declarations"
77 #endif
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;
84                 } else {
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);
88                 }
89         }
90 #ifdef __clang__
91 #       pragma clang diagnostic pop
92 #endif
93 #endif
96 #ifdef WITH_COCOA
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.
106  */
107 void ShowMacDialog(const char *title, const char *message, const char *buttonLabel)
109         CocoaDialog(title, message, buttonLabel);
113 #else
116  * Show the system dialogue message (console on MacOSX).
118  * @param title Window title.
119  * @param message Message text.
120  * @param buttonLabel Button text.
121  */
122 void ShowMacDialog(const char *title, const char *message, const char *buttonLabel)
124         fmt::print(stderr, "{}: {}\n", title, message);
127 #endif
131  * Show an error message.
133  * @param buf error message text.
134  * @param system message text originates from OS.
135  */
136 void ShowOSErrorBox(const char *buf, bool system)
138         /* Display the error in the best way possible. */
139         if (system) {
140                 ShowMacDialog("OpenTTD has encountered an error", buf, "Quit");
141         } else {
142                 ShowMacDialog(buf, "See the readme for more info.", "Quit");
143         }
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.
153  */
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 ];
164         return retbuf;
168 #ifdef WITH_COCOA
170  * Return the contents of the clipboard (COCOA).
172  * @return The (optional) clipboard contents.
173  */
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.
193  */
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]);
203         } else {
204                 _searchpaths[SP_APPLICATION_BUNDLE_DIR].clear();
205         }
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
213  */
214 void CocoaSetupAutoreleasePool()
216         _ottd_autorelease_pool = [ [ NSAutoreleasePool alloc ] init ];
220  * Autorelease the application pool.
221  */
222 void CocoaReleaseAutoreleasePool()
224         [ _ottd_autorelease_pool release ];
227 #endif
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.
233  */
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.
244  */
245 void MacOSSetThreadName(const char *name)
247         if (MacOSVersionIsAtLeast(10, 6, 0)) {
248                 pthread_setname_np(name);
249         }
251         NSThread *cur = [ NSThread currentThread ];
252         if (cur != nil && [ cur respondsToSelector:@selector(setName:) ]) {
253                 [ cur performSelector:@selector(setName:) withObject:[ NSString stringWithUTF8String:name ] ];
254         }
257 uint64_t MacOSGetPhysicalMemory()
259         return [ [ NSProcessInfo processInfo ] physicalMemory ];