1 /*
2 porting.cpp
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 */
5
6 /*
7 This file is part of Freeminer.
8
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Freeminer is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /*
24 Random portability stuff
25
26 See comments in porting.h
27 */
28
29 #include "porting.h"
30
31 #if defined(__FreeBSD__) || defined(__DragonFly__)
32 #include <sys/types.h>
33 #include <sys/sysctl.h>
34 #elif defined(_WIN32)
35 #include <algorithm>
36 #endif
37 #if !defined(_WIN32)
38 #include <unistd.h>
39 #include <sys/utsname.h>
40 #endif
41
42 #include "config.h"
43 #include "debug.h"
44 #include "filesys.h"
45 #include "log.h"
46 #include "util/string.h"
47 #include "settings.h"
48 #include <list>
49
50 namespace porting
51 {
52
53 /*
54 Signal handler (grabs Ctrl-C on POSIX systems)
55 */
56
57 bool g_killed = false;
58
signal_handler_killstatus(void)59 bool * signal_handler_killstatus(void)
60 {
61 return &g_killed;
62 }
63
64 std::atomic_bool g_sighup, g_siginfo;
65
66 #if !defined(_WIN32) // POSIX
67 #include <signal.h>
68
sigint_handler(int sig)69 void sigint_handler(int sig)
70 {
71 switch(sig) {
72 #if defined(SIGINFO)
73 case SIGINFO:
74 g_siginfo = true;
75 break;
76 #endif
77 case SIGHUP:
78 g_sighup = true;
79 break;
80 case SIGINT:
81 case SIGTERM:
82
83 if(g_killed == false)
84 {
85 g_killed = true;
86
87 dstream<<"INFO: sigint_handler(): "
88 <<"Ctrl-C pressed, shutting down."<<std::endl;
89
90 // Comment out for less clutter when testing scripts
91 /*dstream<<DTIME<<"INFO: sigint_handler(): "
92 <<"Printing debug stacks"<<std::endl;
93 debug_stacks_print();*/
94 }
95 break;
96
97 default:
98 (void)signal(sig, SIG_DFL);
99 }
100
101 }
102
signal_handler_init(void)103 void signal_handler_init(void)
104 {
105 g_sighup = false;
106 g_siginfo = false;
107
108 signal(SIGINT, sigint_handler);
109 signal(SIGTERM, sigint_handler);
110 signal(SIGHUP, sigint_handler);
111 #if defined(SIGINFO)
112 signal(SIGINFO, sigint_handler);
113 #endif
114 }
115
116 #else // _WIN32
117 #include <signal.h>
118
event_handler(DWORD sig)119 BOOL WINAPI event_handler(DWORD sig)
120 {
121 switch(sig)
122 {
123 case CTRL_C_EVENT:
124 case CTRL_CLOSE_EVENT:
125 case CTRL_LOGOFF_EVENT:
126 case CTRL_SHUTDOWN_EVENT:
127
128 if(g_killed == false)
129 {
130 dstream<<DTIME<<"INFO: event_handler(): "
131 <<"Ctrl+C, Close Event, Logoff Event or Shutdown Event, shutting down."<<std::endl;
132 // Comment out for less clutter when testing scripts
133 /*dstream<<DTIME<<"INFO: event_handler(): "
134 <<"Printing debug stacks"<<std::endl;
135 debug_stacks_print();*/
136
137 g_killed = true;
138 }
139 else
140 {
141 (void)signal(SIGINT, SIG_DFL);
142 }
143
144 break;
145 case CTRL_BREAK_EVENT:
146 break;
147 }
148
149 return TRUE;
150 }
151
signal_handler_init(void)152 void signal_handler_init(void)
153 {
154 SetConsoleCtrlHandler( (PHANDLER_ROUTINE)event_handler,TRUE);
155 }
156
157 #endif
158
159
160 /*
161 Multithreading support
162 */
getNumberOfProcessors()163 int getNumberOfProcessors() {
164 #if defined(_SC_NPROCESSORS_CONF)
165 return sysconf(_SC_NPROCESSORS_CONF);
166
167 #elif defined(_SC_NPROCESSORS_ONLN)
168
169 return sysconf(_SC_NPROCESSORS_ONLN);
170
171 #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
172
173 unsigned int len, count;
174 len = sizeof(count);
175 return sysctlbyname("hw.ncpu", &count, &len, NULL, 0);
176
177 #elif defined(_GNU_SOURCE)
178
179 return get_nprocs_conf();
180
181 #elif defined(_WIN32)
182
183 SYSTEM_INFO sysinfo;
184 GetSystemInfo(&sysinfo);
185 return sysinfo.dwNumberOfProcessors;
186
187 #elif defined(PTW32_VERSION) || defined(__hpux)
188
189 return pthread_num_processors_np();
190
191 #else
192
193 return 1;
194
195 #endif
196 }
197
198
199 #ifndef __ANDROID__
threadBindToProcessor(threadid_t tid,int pnumber)200 bool threadBindToProcessor(threadid_t tid, int pnumber) {
201 #if defined(_WIN32)
202
203 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
204 if (!hThread)
205 return false;
206
207 bool success = SetThreadAffinityMask(hThread, 1 << pnumber) != 0;
208
209 CloseHandle(hThread);
210 return success;
211
212 #elif (defined(__FreeBSD__) && (__FreeBSD_version >= 702106)) \
213 || defined(__linux) || defined(linux)
214
215 cpu_set_t cpuset;
216
217 CPU_ZERO(&cpuset);
218 CPU_SET(pnumber, &cpuset);
219 return pthread_setaffinity_np(tid, sizeof(cpuset), &cpuset) == 0;
220
221 #elif defined(__sun) || defined(sun)
222
223 return processor_bind(P_LWPID, MAKE_LWPID_PTHREAD(tid),
224 pnumber, NULL) == 0;
225
226 #elif defined(_AIX)
227
228 return bindprocessor(BINDTHREAD, (tid_t)tid, pnumber) == 0;
229
230 #elif defined(__hpux) || defined(hpux)
231
232 pthread_spu_t answer;
233
234 return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
235 &answer, pnumber, tid) == 0;
236
237 #elif defined(__APPLE__)
238
239 struct thread_affinity_policy tapol;
240
241 thread_port_t threadport = pthread_mach_thread_np(tid);
242 tapol.affinity_tag = pnumber + 1;
243 return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
244 (thread_policy_t)&tapol, THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
245
246 #else
247
248 return false;
249
250 #endif
251 }
252 #endif
253
threadSetPriority(threadid_t tid,int prio)254 bool threadSetPriority(threadid_t tid, int prio) {
255 #if defined(_WIN32)
256
257 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
258 if (!hThread)
259 return false;
260
261 bool success = SetThreadPriority(hThread, prio) != 0;
262
263 CloseHandle(hThread);
264 return success;
265
266 #else
267
268 struct sched_param sparam;
269 int policy;
270
271 if (pthread_getschedparam(tid, &policy, &sparam) != 0)
272 return false;
273
274 int min = sched_get_priority_min(policy);
275 int max = sched_get_priority_max(policy);
276
277 sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
278 return pthread_setschedparam(tid, policy, &sparam) == 0;
279
280 #endif
281 }
282
283
284 /*
285 Path mangler
286 */
287
288 // Default to RUN_IN_PLACE style relative paths
289 std::string path_share = "..";
290 std::string path_user = "..";
291
getDataPath(const char * subpath)292 std::string getDataPath(const char *subpath)
293 {
294 return path_share + DIR_DELIM + subpath;
295 }
296
pathRemoveFile(char * path,char delim)297 void pathRemoveFile(char *path, char delim)
298 {
299 // Remove filename and path delimiter
300 int i;
301 for(i = strlen(path)-1; i>=0; i--)
302 {
303 if(path[i] == delim)
304 break;
305 }
306 path[i] = 0;
307 }
308
detectMSVCBuildDir(char * c_path)309 bool detectMSVCBuildDir(char *c_path)
310 {
311 std::string path(c_path);
312 const char *ends[] = {"bin\\Release", "bin\\Build", "bin\\Debug", NULL};
313 return (removeStringEnd(path, ends) != "");
314 }
315
get_sysinfo()316 std::string get_sysinfo()
317 {
318 #ifdef _WIN32
319 OSVERSIONINFO osvi;
320 std::ostringstream oss;
321 std::string tmp;
322 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
323 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
324 GetVersionEx(&osvi);
325 tmp = osvi.szCSDVersion;
326 std::replace(tmp.begin(), tmp.end(), ' ', '_');
327
328 oss << "Windows/" << osvi.dwMajorVersion << "."
329 << osvi.dwMinorVersion;
330 if(osvi.szCSDVersion[0])
331 oss << "-" << tmp;
332 oss << " ";
333 #ifdef _WIN64
334 oss << "x86_64";
335 #else
336 BOOL is64 = FALSE;
337 if(IsWow64Process(GetCurrentProcess(), &is64) && is64)
338 oss << "x86_64"; // 32-bit app on 64-bit OS
339 else
340 oss << "x86";
341 #endif
342
343 return oss.str();
344 #else
345 struct utsname osinfo;
346 uname(&osinfo);
347 return std::string(osinfo.sysname) + "/"
348 + osinfo.release + " " + osinfo.machine;
349 #endif
350 }
351
initializePaths()352 void initializePaths()
353 {
354 #if RUN_IN_PLACE
355 /*
356 Use relative paths if RUN_IN_PLACE
357 */
358
359 infostream<<"Using relative paths (RUN_IN_PLACE)"<<std::endl;
360
361 /*
362 Windows
363 */
364 #if defined(_WIN32)
365
366 const DWORD buflen = 1000;
367 char buf[buflen];
368 DWORD len;
369
370 // Find path of executable and set path_share relative to it
371 len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
372 assert(len < buflen);
373 pathRemoveFile(buf, '\\');
374
375 if(detectMSVCBuildDir(buf)){
376 infostream<<"MSVC build directory detected"<<std::endl;
377 path_share = std::string(buf) + "\\..\\..";
378 path_user = std::string(buf) + "\\..\\..";
379 }
380 else{
381 #if STATIC_BUILD
382 path_share = std::string(buf) + "\\.";
383 path_user = std::string(buf) + "\\.";
384 #else
385 path_share = std::string(buf) + "\\..";
386 path_user = std::string(buf) + "\\..";
387 #endif
388 }
389
390 /*
391 Linux
392 */
393 #elif defined(linux) || defined(__linux)
394
395 char buf[BUFSIZ];
396 memset(buf, 0, BUFSIZ);
397 // Get path to executable
398 assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
399
400 pathRemoveFile(buf, '/');
401
402 path_share = std::string(buf) + "/..";
403 path_user = std::string(buf) + "/..";
404
405 /*
406 OS X
407 */
408 #elif defined(__APPLE__)
409
410 //https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dyld.3.html
411 //TODO: Test this code
412 char buf[BUFSIZ];
413 uint32_t len = sizeof(buf);
414 assert(_NSGetExecutablePath(buf, &len) != -1);
415
416 pathRemoveFile(buf, '/');
417
418 path_share = std::string(buf) + "/..";
419 path_user = std::string(buf) + "/..";
420
421 /*
422 FreeBSD
423 */
424 #elif defined(__FreeBSD__) || defined(__DragonFly__)
425
426 int mib[4];
427 char buf[BUFSIZ];
428 size_t len = sizeof(buf);
429
430 mib[0] = CTL_KERN;
431 mib[1] = KERN_PROC;
432 mib[2] = KERN_PROC_PATHNAME;
433 mib[3] = -1;
434 assert(sysctl(mib, 4, buf, &len, NULL, 0) != -1);
435
436 pathRemoveFile(buf, '/');
437
438 path_share = std::string(buf) + "/..";
439 path_user = std::string(buf) + "/..";
440
441 #else
442
443 //TODO: Get path of executable. This assumes working directory is bin/
444 dstream<<"WARNING: Relative path not properly supported on this platform"
445 <<std::endl;
446
447 /* scriptapi no longer allows paths that start with "..", so assuming that
448 the current working directory is bin/, strip off the last component. */
449 char *cwd = getcwd(NULL, 0);
450 pathRemoveFile(cwd, '/');
451 path_share = std::string(cwd);
452 path_user = std::string(cwd);
453
454 #endif
455
456 #else // RUN_IN_PLACE
457
458 /*
459 Use platform-specific paths otherwise
460 */
461
462 infostream<<"Using system-wide paths (NOT RUN_IN_PLACE)"<<std::endl;
463
464 /*
465 Windows
466 */
467 #if defined(_WIN32)
468
469 const DWORD buflen = 1000;
470 char buf[buflen];
471 DWORD len;
472
473 // Find path of executable and set path_share relative to it
474 len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
475 assert(len < buflen);
476 pathRemoveFile(buf, '\\');
477
478 // Use ".\bin\.."
479 path_share = std::string(buf) + "\\..";
480
481 // Use "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
482 len = GetEnvironmentVariable("APPDATA", buf, buflen);
483 assert(len < buflen);
484 path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME;
485
486 /*
487 Linux
488 */
489 #elif defined(linux) || defined(__linux)
490
491 // Get path to executable
492 std::string bindir = "";
493 {
494 char buf[BUFSIZ];
495 memset(buf, 0, BUFSIZ);
496 if (readlink("/proc/self/exe", buf, BUFSIZ-1) == -1) {
497 errorstream << "Unable to read bindir "<< std::endl;
498 #ifndef __ANDROID__
499 assert("Unable to read bindir" == 0);
500 #endif
501 } else {
502 pathRemoveFile(buf, '/');
503 bindir = buf;
504 }
505 }
506
507 // Find share directory from these.
508 // It is identified by containing the subdirectory "builtin".
509 std::list<std::string> trylist;
510 std::string static_sharedir = STATIC_SHAREDIR;
511 if(static_sharedir != "" && static_sharedir != ".")
512 trylist.push_back(static_sharedir);
513 trylist.push_back(
514 bindir + DIR_DELIM + ".." + DIR_DELIM + "share" + DIR_DELIM + PROJECT_NAME);
515 trylist.push_back(bindir + DIR_DELIM + "..");
516 #ifdef __ANDROID__
517 trylist.push_back(path_user);
518 #endif
519
520 for(std::list<std::string>::const_iterator i = trylist.begin();
521 i != trylist.end(); i++)
522 {
523 const std::string &trypath = *i;
524 if(!fs::PathExists(trypath) || !fs::PathExists(trypath + DIR_DELIM + "builtin")){
525 dstream<<"WARNING: system-wide share not found at \""
526 <<trypath<<"\""<<std::endl;
527 continue;
528 }
529 // Warn if was not the first alternative
530 if(i != trylist.begin()){
531 dstream<<"WARNING: system-wide share found at \""
532 <<trypath<<"\""<<std::endl;
533 }
534 path_share = trypath;
535 break;
536 }
537 #ifndef __ANDROID__
538 path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
539 #endif
540
541 /*
542 OS X
543 */
544 #elif defined(__APPLE__)
545
546 // Code based on
547 // http://stackoverflow.com/questions/516200/relative-paths-not-working-in-xcode-c
548 CFBundleRef main_bundle = CFBundleGetMainBundle();
549 CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
550 char path[PATH_MAX];
551 if(CFURLGetFileSystemRepresentation(resources_url, TRUE, (UInt8 *)path, PATH_MAX))
552 {
553 dstream<<"Bundle resource path: "<<path<<std::endl;
554 //chdir(path);
555 path_share = std::string(path) + DIR_DELIM + "share";
556 }
557 else
558 {
559 // error!
560 dstream<<"WARNING: Could not determine bundle resource path"<<std::endl;
561 }
562 CFRelease(resources_url);
563
564 path_user = std::string(getenv("HOME")) + "/Library/Application Support/" + PROJECT_NAME;
565
566 #else // FreeBSD, and probably many other POSIX-like systems.
567
568 path_share = STATIC_SHAREDIR;
569 path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
570
571 #endif
572
573 #endif // RUN_IN_PLACE
574 }
575
576 static irr::IrrlichtDevice* device;
577
initIrrlicht(irr::IrrlichtDevice * _device)578 void initIrrlicht(irr::IrrlichtDevice * _device) {
579 device = _device;
580 }
581
582 #ifndef SERVER
getWindowSize()583 v2u32 getWindowSize() {
584 return device->getVideoDriver()->getScreenSize();
585 }
586
587 #ifndef __ANDROID__
588
getDisplayDensity()589 float getDisplayDensity() {
590 float gui_scaling = g_settings->getFloat("gui_scaling");
591 // using Y here feels like a bug, this needs to be discussed later!
592 if (getWindowSize().Y <= 800) {
593 return (2.0/3.0) * gui_scaling;
594 }
595 if (getWindowSize().Y <= 1280) {
596 return 1.0 * gui_scaling;
597 }
598
599 return (4.0/3.0) * gui_scaling;
600 }
601
getDisplaySize()602 v2u32 getDisplaySize() {
603 IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
604
605 core::dimension2d<u32> deskres = nulldevice->getVideoModeList()->getDesktopResolution();
606 nulldevice -> drop();
607
608 return deskres;
609 }
610 #endif
611 #endif
612
613 } //namespace porting
614
615
get_time_us()616 extern "C" unsigned int get_time_us() {
617 return porting::getTimeUs();
618 }
619