Re-add jungles, apple trees
[oweals/minetest.git] / src / porting.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21         Random portability stuff
22
23         See comments in porting.h
24 */
25
26 #if defined(linux)
27         #include <unistd.h>
28 #elif defined(__APPLE__)
29         #include <unistd.h>
30         #include <mach-o/dyld.h>
31 #elif defined(__FreeBSD__)
32         #include <unistd.h>
33         #include <sys/types.h>
34         #include <sys/sysctl.h>
35 #endif
36
37 #include "porting.h"
38 #include "config.h"
39 #include "debug.h"
40 #include "filesys.h"
41 #include "log.h"
42 #include "util/string.h"
43 #include <list>
44
45 #ifdef __APPLE__
46         #include "CoreFoundation/CoreFoundation.h"
47 #endif
48
49 namespace porting
50 {
51
52 /*
53         Signal handler (grabs Ctrl-C on POSIX systems)
54 */
55
56 bool g_killed = false;
57
58 bool * signal_handler_killstatus(void)
59 {
60         return &g_killed;
61 }
62
63 #if !defined(_WIN32) // POSIX
64         #include <signal.h>
65
66 void sigint_handler(int sig)
67 {
68         if(g_killed == false)
69         {
70                 dstream<<DTIME<<"INFO: sigint_handler(): "
71                                 <<"Ctrl-C pressed, shutting down."<<std::endl;
72
73                 // Comment out for less clutter when testing scripts
74                 /*dstream<<DTIME<<"INFO: sigint_handler(): "
75                                 <<"Printing debug stacks"<<std::endl;
76                 debug_stacks_print();*/
77
78                 g_killed = true;
79         }
80         else
81         {
82                 (void)signal(SIGINT, SIG_DFL);
83         }
84 }
85
86 void signal_handler_init(void)
87 {
88         (void)signal(SIGINT, sigint_handler);
89 }
90
91 #else // _WIN32
92         #include <signal.h>
93
94         BOOL WINAPI event_handler(DWORD sig)
95         {
96                 switch(sig)
97                 {
98                 case CTRL_C_EVENT:
99                 case CTRL_CLOSE_EVENT:
100                 case CTRL_LOGOFF_EVENT:
101                 case CTRL_SHUTDOWN_EVENT:
102
103                         if(g_killed == false)
104                         {
105                                 dstream<<DTIME<<"INFO: event_handler(): "
106                                                 <<"Ctrl+C, Close Event, Logoff Event or Shutdown Event, shutting down."<<std::endl;
107                                 // Comment out for less clutter when testing scripts
108                                 /*dstream<<DTIME<<"INFO: event_handler(): "
109                                                 <<"Printing debug stacks"<<std::endl;
110                                 debug_stacks_print();*/
111
112                                 g_killed = true;
113                         }
114                         else
115                         {
116                                 (void)signal(SIGINT, SIG_DFL);
117                         }
118
119                         break;
120                 case CTRL_BREAK_EVENT:
121                         break;
122                 }
123
124                 return TRUE;
125         }
126
127 void signal_handler_init(void)
128 {
129         SetConsoleCtrlHandler( (PHANDLER_ROUTINE)event_handler,TRUE);
130 }
131
132 #endif
133
134
135 /*
136         Multithreading support
137 */
138 int getNumberOfProcessors() {
139 #if defined(_SC_NPROCESSORS_ONLN)
140
141         return sysconf(_SC_NPROCESSORS_ONLN);
142
143 #elif defined(__FreeBSD__) || defined(__APPLE__)
144
145         unsigned int len, count;
146         len = sizeof(count);
147         return sysctlbyname("hw.ncpu", &count, &len, NULL, 0);
148
149 #elif defined(_GNU_SOURCE)
150
151         return get_nprocs();
152
153 #elif defined(_WIN32)
154
155         SYSTEM_INFO sysinfo;
156         GetSystemInfo(&sysinfo);
157         return sysinfo.dwNumberOfProcessors;
158
159 #elif defined(PTW32_VERSION) || defined(__hpux)
160
161         return pthread_num_processors_np();
162
163 #else
164
165         return 1;
166
167 #endif
168 }
169
170
171 bool threadBindToProcessor(threadid_t tid, int pnumber) {
172 #if defined(_WIN32)
173
174         HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
175         if (!hThread)
176                 return false;
177
178         bool success = SetThreadAffinityMask(hThread, 1 << pnumber) != 0;
179
180         CloseHandle(hThread);
181         return success;
182
183 #elif (defined(__FreeBSD__) && (__FreeBSD_version >= 702106)) \
184         || defined(__linux) || defined(linux)
185
186         cpu_set_t cpuset;
187
188         CPU_ZERO(&cpuset);
189         CPU_SET(pnumber, &cpuset);
190         return pthread_setaffinity_np(tid, sizeof(cpuset), &cpuset) == 0;
191
192 #elif defined(__sun) || defined(sun)
193
194         return processor_bind(P_LWPID, MAKE_LWPID_PTHREAD(tid), 
195                                                 pnumber, NULL) == 0;
196
197 #elif defined(_AIX)
198         
199         return bindprocessor(BINDTHREAD, (tid_t)tid, pnumber) == 0;
200
201 #elif defined(__hpux) || defined(hpux)
202
203         pthread_spu_t answer;
204
205         return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
206                                                                         &answer, pnumber, tid) == 0;
207         
208 #elif defined(__APPLE__)
209
210         struct thread_affinity_policy tapol;
211         
212         thread_port_t threadport = pthread_mach_thread_np(tid);
213         tapol.affinity_tag = pnumber + 1;
214         return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
215                         (thread_policy_t)&tapol, THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
216
217 #else
218
219         return false;
220
221 #endif
222 }
223
224
225 bool threadSetPriority(threadid_t tid, int prio) {
226 #if defined(_WIN32)
227
228         HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
229         if (!hThread)
230                 return false;
231
232         bool success = SetThreadPriority(hThread, prio) != 0;
233
234         CloseHandle(hThread);
235         return success;
236         
237 #else
238
239         struct sched_param sparam;
240         int policy;
241         
242         if (pthread_getschedparam(tid, &policy, &sparam) != 0)
243                 return false;
244                 
245         int min = sched_get_priority_min(policy);
246         int max = sched_get_priority_max(policy);
247
248         sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
249         return pthread_setschedparam(tid, policy, &sparam) == 0;
250         
251 #endif
252 }
253
254
255 /*
256         Path mangler
257 */
258
259 // Default to RUN_IN_PLACE style relative paths
260 std::string path_share = "..";
261 std::string path_user = "..";
262
263 std::string getDataPath(const char *subpath)
264 {
265         return path_share + DIR_DELIM + subpath;
266 }
267
268 void pathRemoveFile(char *path, char delim)
269 {
270         // Remove filename and path delimiter
271         int i;
272         for(i = strlen(path)-1; i>=0; i--)
273         {
274                 if(path[i] == delim)
275                         break;
276         }
277         path[i] = 0;
278 }
279
280 bool detectMSVCBuildDir(char *c_path)
281 {
282         std::string path(c_path);
283         const char *ends[] = {"bin\\Release", "bin\\Build", NULL};
284         return (removeStringEnd(path, ends) != "");
285 }
286
287 void initializePaths()
288 {
289 #if RUN_IN_PLACE
290         /*
291                 Use relative paths if RUN_IN_PLACE
292         */
293
294         infostream<<"Using relative paths (RUN_IN_PLACE)"<<std::endl;
295
296         /*
297                 Windows
298         */
299         #if defined(_WIN32)
300
301         const DWORD buflen = 1000;
302         char buf[buflen];
303         DWORD len;
304
305         // Find path of executable and set path_share relative to it
306         len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
307         assert(len < buflen);
308         pathRemoveFile(buf, '\\');
309
310         if(detectMSVCBuildDir(buf)){
311                 infostream<<"MSVC build directory detected"<<std::endl;
312                 path_share = std::string(buf) + "\\..\\..";
313                 path_user = std::string(buf) + "\\..\\..";
314         }
315         else{
316                 path_share = std::string(buf) + "\\..";
317                 path_user = std::string(buf) + "\\..";
318         }
319
320         /*
321                 Linux
322         */
323         #elif defined(linux)
324
325         char buf[BUFSIZ];
326         memset(buf, 0, BUFSIZ);
327         // Get path to executable
328         assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
329
330         pathRemoveFile(buf, '/');
331
332         path_share = std::string(buf) + "/..";
333         path_user = std::string(buf) + "/..";
334
335         /*
336                 OS X
337         */
338         #elif defined(__APPLE__)
339
340         //https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dyld.3.html
341         //TODO: Test this code
342         char buf[BUFSIZ];
343         uint32_t len = sizeof(buf);
344         assert(_NSGetExecutablePath(buf, &len) != -1);
345
346         pathRemoveFile(buf, '/');
347
348         path_share = std::string(buf) + "/..";
349         path_user = std::string(buf) + "/..";
350
351         /*
352                 FreeBSD
353         */
354         #elif defined(__FreeBSD__)
355
356         int mib[4];
357         char buf[BUFSIZ];
358         size_t len = sizeof(buf);
359
360         mib[0] = CTL_KERN;
361         mib[1] = KERN_PROC;
362         mib[2] = KERN_PROC_PATHNAME;
363         mib[3] = -1;
364         assert(sysctl(mib, 4, buf, &len, NULL, 0) != -1);
365
366         pathRemoveFile(buf, '/');
367
368         path_share = std::string(buf) + "/..";
369         path_user = std::string(buf) + "/..";
370
371         #else
372
373         //TODO: Get path of executable. This assumes working directory is bin/
374         dstream<<"WARNING: Relative path not properly supported on this platform"
375                         <<std::endl;
376         path_share = std::string("..");
377         path_user = std::string("..");
378
379         #endif
380
381 #else // RUN_IN_PLACE
382
383         /*
384                 Use platform-specific paths otherwise
385         */
386
387         infostream<<"Using system-wide paths (NOT RUN_IN_PLACE)"<<std::endl;
388
389         /*
390                 Windows
391         */
392         #if defined(_WIN32)
393
394         const DWORD buflen = 1000;
395         char buf[buflen];
396         DWORD len;
397
398         // Find path of executable and set path_share relative to it
399         len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
400         assert(len < buflen);
401         pathRemoveFile(buf, '\\');
402
403         // Use ".\bin\.."
404         path_share = std::string(buf) + "\\..";
405
406         // Use "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
407         len = GetEnvironmentVariable("APPDATA", buf, buflen);
408         assert(len < buflen);
409         path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME;
410
411         /*
412                 Linux
413         */
414         #elif defined(linux)
415
416         // Get path to executable
417         std::string bindir = "";
418         {
419                 char buf[BUFSIZ];
420                 memset(buf, 0, BUFSIZ);
421                 assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
422                 pathRemoveFile(buf, '/');
423                 bindir = buf;
424         }
425
426         // Find share directory from these.
427         // It is identified by containing the subdirectory "builtin".
428         std::list<std::string> trylist;
429         std::string static_sharedir = STATIC_SHAREDIR;
430         if(static_sharedir != "" && static_sharedir != ".")
431                 trylist.push_back(static_sharedir);
432         trylist.push_back(bindir + "/../share/" + PROJECT_NAME);
433         trylist.push_back(bindir + "/..");
434
435         for(std::list<std::string>::const_iterator i = trylist.begin();
436                         i != trylist.end(); i++)
437         {
438                 const std::string &trypath = *i;
439                 if(!fs::PathExists(trypath) || !fs::PathExists(trypath + "/builtin")){
440                         dstream<<"WARNING: system-wide share not found at \""
441                                         <<trypath<<"\""<<std::endl;
442                         continue;
443                 }
444                 // Warn if was not the first alternative
445                 if(i != trylist.begin()){
446                         dstream<<"WARNING: system-wide share found at \""
447                                         <<trypath<<"\""<<std::endl;
448                 }
449                 path_share = trypath;
450                 break;
451         }
452
453         path_user = std::string(getenv("HOME")) + "/." + PROJECT_NAME;
454
455         /*
456                 OS X
457         */
458         #elif defined(__APPLE__)
459
460     // Code based on
461     // http://stackoverflow.com/questions/516200/relative-paths-not-working-in-xcode-c
462     CFBundleRef main_bundle = CFBundleGetMainBundle();
463     CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
464     char path[PATH_MAX];
465     if(CFURLGetFileSystemRepresentation(resources_url, TRUE, (UInt8 *)path, PATH_MAX))
466         {
467                 dstream<<"Bundle resource path: "<<path<<std::endl;
468                 //chdir(path);
469                 path_share = std::string(path) + "/share";
470         }
471         else
472     {
473         // error!
474                 dstream<<"WARNING: Could not determine bundle resource path"<<std::endl;
475     }
476     CFRelease(resources_url);
477
478         path_user = std::string(getenv("HOME")) + "/Library/Application Support/" + PROJECT_NAME;
479
480         #elif defined(__FreeBSD__)
481
482         path_share = STATIC_SHAREDIR;
483         path_user = std::string(getenv("HOME")) + "/." + PROJECT_NAME;
484
485         #endif
486
487 #endif // RUN_IN_PLACE
488 }
489
490 } //namespace porting
491