71044636380cd679639b711f6a39e26b8093780a
[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 #include "porting.h"
27
28 #if defined(__FreeBSD__)
29         #include <sys/types.h>
30         #include <sys/sysctl.h>
31 #elif defined(_WIN32)
32         #include <algorithm>
33 #endif
34 #if !defined(_WIN32)
35         #include <unistd.h>
36         #include <sys/utsname.h>
37 #endif
38
39 #if !defined(_WIN32) && !defined(__APPLE__) && \
40         !defined(__ANDROID__) && !defined(SERVER)
41         #define XORG_USED
42 #endif
43
44 #ifdef XORG_USED
45         #include <X11/Xlib.h>
46         #include <X11/Xutil.h>
47 #endif
48
49 #include "config.h"
50 #include "debug.h"
51 #include "filesys.h"
52 #include "log.h"
53 #include "util/string.h"
54 #include "main.h"
55 #include "settings.h"
56 #include <list>
57
58 namespace porting
59 {
60
61 /*
62         Signal handler (grabs Ctrl-C on POSIX systems)
63 */
64
65 bool g_killed = false;
66
67 bool * signal_handler_killstatus(void)
68 {
69         return &g_killed;
70 }
71
72 #if !defined(_WIN32) // POSIX
73         #include <signal.h>
74
75 void sigint_handler(int sig)
76 {
77         if(g_killed == false)
78         {
79                 dstream<<DTIME<<"INFO: sigint_handler(): "
80                                 <<"Ctrl-C pressed, shutting down."<<std::endl;
81
82                 // Comment out for less clutter when testing scripts
83                 /*dstream<<DTIME<<"INFO: sigint_handler(): "
84                                 <<"Printing debug stacks"<<std::endl;
85                 debug_stacks_print();*/
86
87                 g_killed = true;
88         }
89         else
90         {
91                 (void)signal(SIGINT, SIG_DFL);
92         }
93 }
94
95 void signal_handler_init(void)
96 {
97         (void)signal(SIGINT, sigint_handler);
98 }
99
100 #else // _WIN32
101         #include <signal.h>
102
103         BOOL WINAPI event_handler(DWORD sig)
104         {
105                 switch(sig)
106                 {
107                 case CTRL_C_EVENT:
108                 case CTRL_CLOSE_EVENT:
109                 case CTRL_LOGOFF_EVENT:
110                 case CTRL_SHUTDOWN_EVENT:
111
112                         if(g_killed == false)
113                         {
114                                 dstream<<DTIME<<"INFO: event_handler(): "
115                                                 <<"Ctrl+C, Close Event, Logoff Event or Shutdown Event, shutting down."<<std::endl;
116                                 // Comment out for less clutter when testing scripts
117                                 /*dstream<<DTIME<<"INFO: event_handler(): "
118                                                 <<"Printing debug stacks"<<std::endl;
119                                 debug_stacks_print();*/
120
121                                 g_killed = true;
122                         }
123                         else
124                         {
125                                 (void)signal(SIGINT, SIG_DFL);
126                         }
127
128                         break;
129                 case CTRL_BREAK_EVENT:
130                         break;
131                 }
132
133                 return TRUE;
134         }
135
136 void signal_handler_init(void)
137 {
138         SetConsoleCtrlHandler( (PHANDLER_ROUTINE)event_handler,TRUE);
139 }
140
141 #endif
142
143
144 /*
145         Multithreading support
146 */
147 int getNumberOfProcessors() {
148 #if defined(_SC_NPROCESSORS_ONLN)
149
150         return sysconf(_SC_NPROCESSORS_ONLN);
151
152 #elif defined(__FreeBSD__) || defined(__APPLE__)
153
154         unsigned int len, count;
155         len = sizeof(count);
156         return sysctlbyname("hw.ncpu", &count, &len, NULL, 0);
157
158 #elif defined(_GNU_SOURCE)
159
160         return get_nprocs();
161
162 #elif defined(_WIN32)
163
164         SYSTEM_INFO sysinfo;
165         GetSystemInfo(&sysinfo);
166         return sysinfo.dwNumberOfProcessors;
167
168 #elif defined(PTW32_VERSION) || defined(__hpux)
169
170         return pthread_num_processors_np();
171
172 #else
173
174         return 1;
175
176 #endif
177 }
178
179
180 #ifndef __ANDROID__
181 bool threadBindToProcessor(threadid_t tid, int pnumber) {
182 #if defined(_WIN32)
183
184         HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
185         if (!hThread)
186                 return false;
187
188         bool success = SetThreadAffinityMask(hThread, 1 << pnumber) != 0;
189
190         CloseHandle(hThread);
191         return success;
192
193 #elif (defined(__FreeBSD__) && (__FreeBSD_version >= 702106)) \
194         || defined(__linux) || defined(linux)
195
196         cpu_set_t cpuset;
197
198         CPU_ZERO(&cpuset);
199         CPU_SET(pnumber, &cpuset);
200         return pthread_setaffinity_np(tid, sizeof(cpuset), &cpuset) == 0;
201
202 #elif defined(__sun) || defined(sun)
203
204         return processor_bind(P_LWPID, MAKE_LWPID_PTHREAD(tid),
205                                                 pnumber, NULL) == 0;
206
207 #elif defined(_AIX)
208
209         return bindprocessor(BINDTHREAD, (tid_t)tid, pnumber) == 0;
210
211 #elif defined(__hpux) || defined(hpux)
212
213         pthread_spu_t answer;
214
215         return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
216                                                                         &answer, pnumber, tid) == 0;
217
218 #elif defined(__APPLE__)
219
220         struct thread_affinity_policy tapol;
221
222         thread_port_t threadport = pthread_mach_thread_np(tid);
223         tapol.affinity_tag = pnumber + 1;
224         return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
225                         (thread_policy_t)&tapol, THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
226
227 #else
228
229         return false;
230
231 #endif
232 }
233 #endif
234
235 bool threadSetPriority(threadid_t tid, int prio) {
236 #if defined(_WIN32)
237
238         HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
239         if (!hThread)
240                 return false;
241
242         bool success = SetThreadPriority(hThread, prio) != 0;
243
244         CloseHandle(hThread);
245         return success;
246
247 #else
248
249         struct sched_param sparam;
250         int policy;
251
252         if (pthread_getschedparam(tid, &policy, &sparam) != 0)
253                 return false;
254
255         int min = sched_get_priority_min(policy);
256         int max = sched_get_priority_max(policy);
257
258         sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
259         return pthread_setschedparam(tid, policy, &sparam) == 0;
260
261 #endif
262 }
263
264
265 /*
266         Path mangler
267 */
268
269 // Default to RUN_IN_PLACE style relative paths
270 std::string path_share = "..";
271 std::string path_user = "..";
272
273 std::string getDataPath(const char *subpath)
274 {
275         return path_share + DIR_DELIM + subpath;
276 }
277
278 void pathRemoveFile(char *path, char delim)
279 {
280         // Remove filename and path delimiter
281         int i;
282         for(i = strlen(path)-1; i>=0; i--)
283         {
284                 if(path[i] == delim)
285                         break;
286         }
287         path[i] = 0;
288 }
289
290 bool detectMSVCBuildDir(char *c_path)
291 {
292         std::string path(c_path);
293         const char *ends[] = {
294                 "bin\\Release",
295                 "bin\\Debug",
296                 "bin\\Build",
297                 NULL};
298         return (removeStringEnd(path, ends) != "");
299 }
300
301 std::string get_sysinfo()
302 {
303 #ifdef _WIN32
304         OSVERSIONINFO osvi;
305         std::ostringstream oss;
306         std::string tmp;
307         ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
308         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
309         GetVersionEx(&osvi);
310         tmp = osvi.szCSDVersion;
311         std::replace(tmp.begin(), tmp.end(), ' ', '_');
312
313         oss << "Windows/" << osvi.dwMajorVersion << "."
314                 << osvi.dwMinorVersion;
315         if(osvi.szCSDVersion[0])
316                 oss << "-" << tmp;
317         oss << " ";
318         #ifdef _WIN64
319         oss << "x86_64";
320         #else
321         BOOL is64 = FALSE;
322         if(IsWow64Process(GetCurrentProcess(), &is64) && is64)
323                 oss << "x86_64"; // 32-bit app on 64-bit OS
324         else
325                 oss << "x86";
326         #endif
327
328         return oss.str();
329 #else
330         struct utsname osinfo;
331         uname(&osinfo);
332         return std::string(osinfo.sysname) + "/"
333                 + osinfo.release + " " + osinfo.machine;
334 #endif
335 }
336
337 void initializePaths()
338 {
339 #if RUN_IN_PLACE
340         /*
341                 Use relative paths if RUN_IN_PLACE
342         */
343
344         infostream<<"Using relative paths (RUN_IN_PLACE)"<<std::endl;
345
346         /*
347                 Windows
348         */
349         #if defined(_WIN32)
350
351         const DWORD buflen = 1000;
352         char buf[buflen];
353         DWORD len;
354
355         // Find path of executable and set path_share relative to it
356         len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
357         assert(len < buflen);
358         pathRemoveFile(buf, '\\');
359
360         if(detectMSVCBuildDir(buf)){
361                 infostream<<"MSVC build directory detected"<<std::endl;
362                 path_share = std::string(buf) + "\\..\\..";
363                 path_user = std::string(buf) + "\\..\\..";
364         }
365         else{
366                 path_share = std::string(buf) + "\\..";
367                 path_user = std::string(buf) + "\\..";
368         }
369
370         /*
371                 Linux
372         */
373         #elif defined(linux)
374
375         char buf[BUFSIZ];
376         memset(buf, 0, BUFSIZ);
377         // Get path to executable
378         FATAL_ERROR_IF(readlink("/proc/self/exe", buf, BUFSIZ-1) == -1, "Failed to get cwd");
379
380         pathRemoveFile(buf, '/');
381
382         path_share = std::string(buf) + "/..";
383         path_user = std::string(buf) + "/..";
384
385         /*
386                 OS X
387         */
388         #elif defined(__APPLE__)
389
390         CFBundleRef main_bundle = CFBundleGetMainBundle();
391         CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
392         char path[PATH_MAX];
393         if (CFURLGetFileSystemRepresentation(resources_url, TRUE, (UInt8 *)path, PATH_MAX)) {
394                 path_share = std::string(path);
395                 path_user = std::string(path) + "/../User";
396         } else {
397                 dstream << "WARNING: Could not determine bundle resource path" << std::endl;
398         }
399         CFRelease(resources_url);
400
401         /*
402                 FreeBSD
403         */
404         #elif defined(__FreeBSD__)
405
406         int mib[4];
407         char buf[BUFSIZ];
408         size_t len = sizeof(buf);
409
410         mib[0] = CTL_KERN;
411         mib[1] = KERN_PROC;
412         mib[2] = KERN_PROC_PATHNAME;
413         mib[3] = -1;
414         FATAL_ERROR_IF(sysctl(mib, 4, buf, &len, NULL, 0) == -1, "");
415
416         pathRemoveFile(buf, '/');
417
418         path_share = std::string(buf) + "/..";
419         path_user = std::string(buf) + "/..";
420
421         #else
422
423         //TODO: Get path of executable. This assumes working directory is bin/
424         dstream<<"WARNING: Relative path not properly supported on this platform"
425                         <<std::endl;
426
427         /* scriptapi no longer allows paths that start with "..", so assuming that
428            the current working directory is bin/, strip off the last component. */
429         char *cwd = getcwd(NULL, 0);
430         pathRemoveFile(cwd, '/');
431         path_share = std::string(cwd);
432         path_user = std::string(cwd);
433
434         #endif
435
436 #else // RUN_IN_PLACE
437
438         /*
439                 Use platform-specific paths otherwise
440         */
441
442         infostream<<"Using system-wide paths (NOT RUN_IN_PLACE)"<<std::endl;
443
444         /*
445                 Windows
446         */
447         #if defined(_WIN32)
448
449         const DWORD buflen = 1000; // FIXME: Surely there is a better way to do this
450         char buf[buflen];
451         DWORD len;
452
453         // Find path of executable and set path_share relative to it
454         len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
455         FATAL_ERROR_IF(len >= buflen, "Overlow");
456         pathRemoveFile(buf, '\\');
457
458         // Use ".\bin\.."
459         path_share = std::string(buf) + "\\..";
460
461         // Use "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
462         len = GetEnvironmentVariable("APPDATA", buf, buflen);
463         FATAL_ERROR_IF(len >= buflen, "Overlow");
464         path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME;
465
466         /*
467                 Linux
468         */
469         #elif defined(linux)
470
471         // Get path to executable
472         std::string bindir = "";
473         {
474                 char buf[BUFSIZ];
475                 memset(buf, 0, BUFSIZ);
476                 if (readlink("/proc/self/exe", buf, BUFSIZ-1) == -1) {
477                         errorstream << "Unable to read bindir "<< std::endl;
478 #ifndef __ANDROID__
479                         FATAL_ERROR("Unable to read bindir");
480 #endif
481                 } else {
482                         pathRemoveFile(buf, '/');
483                         bindir = buf;
484                 }
485         }
486
487         // Find share directory from these.
488         // It is identified by containing the subdirectory "builtin".
489         std::list<std::string> trylist;
490         std::string static_sharedir = STATIC_SHAREDIR;
491         if(static_sharedir != "" && static_sharedir != ".")
492                 trylist.push_back(static_sharedir);
493         trylist.push_back(
494                         bindir + DIR_DELIM + ".." + DIR_DELIM + "share" + DIR_DELIM + PROJECT_NAME);
495         trylist.push_back(bindir + DIR_DELIM + "..");
496 #ifdef __ANDROID__
497         trylist.push_back(path_user);
498 #endif
499
500         for(std::list<std::string>::const_iterator i = trylist.begin();
501                         i != trylist.end(); i++)
502         {
503                 const std::string &trypath = *i;
504                 if(!fs::PathExists(trypath) || !fs::PathExists(trypath + DIR_DELIM + "builtin")){
505                         dstream<<"WARNING: system-wide share not found at \""
506                                         <<trypath<<"\""<<std::endl;
507                         continue;
508                 }
509                 // Warn if was not the first alternative
510                 if(i != trylist.begin()){
511                         dstream<<"WARNING: system-wide share found at \""
512                                         <<trypath<<"\""<<std::endl;
513                 }
514                 path_share = trypath;
515                 break;
516         }
517 #ifndef __ANDROID__
518         path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
519 #endif
520
521         /*
522                 OS X
523         */
524         #elif defined(__APPLE__)
525
526         CFBundleRef main_bundle = CFBundleGetMainBundle();
527         CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
528         char path[PATH_MAX];
529         if (CFURLGetFileSystemRepresentation(resources_url, TRUE, (UInt8 *)path, PATH_MAX)) {
530                 path_share = std::string(path);
531         } else {
532                 dstream << "WARNING: Could not determine bundle resource path" << std::endl;
533         }
534         CFRelease(resources_url);
535         path_user = std::string(getenv("HOME")) + "/Library/Application Support/" + PROJECT_NAME;
536
537         #else // FreeBSD, and probably many other POSIX-like systems.
538
539         path_share = STATIC_SHAREDIR;
540         path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
541
542         #endif
543
544 #endif // RUN_IN_PLACE
545 }
546
547 static irr::IrrlichtDevice *device;
548
549 void initIrrlicht(irr::IrrlichtDevice *device_)
550 {
551         device = device_;
552 }
553
554 void setXorgClassHint(const video::SExposedVideoData &video_data,
555         const std::string &name)
556 {
557 #ifdef XORG_USED
558         if (video_data.OpenGLLinux.X11Display == NULL)
559                 return;
560
561         XClassHint *classhint = XAllocClassHint();
562         classhint->res_name  = (char *)name.c_str();
563         classhint->res_class = (char *)name.c_str();
564
565         XSetClassHint((Display *)video_data.OpenGLLinux.X11Display,
566                 video_data.OpenGLLinux.X11Window, classhint);
567         XFree(classhint);
568 #endif
569 }
570
571 #ifndef SERVER
572 v2u32 getWindowSize()
573 {
574         return device->getVideoDriver()->getScreenSize();
575 }
576
577
578 std::vector<core::vector3d<u32> > getVideoModes()
579 {
580         std::vector<core::vector3d<u32> > mlist;
581         video::IVideoModeList *modelist = device->getVideoModeList();
582
583         u32 num_modes = modelist->getVideoModeCount();
584         for (u32 i = 0; i != num_modes; i++) {
585                 core::dimension2d<u32> mode_res = modelist->getVideoModeResolution(i);
586                 s32 mode_depth = modelist->getVideoModeDepth(i);
587                 mlist.push_back(core::vector3d<u32>(mode_res.Width, mode_res.Height, mode_depth));
588         }
589
590         return mlist;
591 }
592
593 std::vector<irr::video::E_DRIVER_TYPE> getSupportedVideoDrivers()
594 {
595         std::vector<irr::video::E_DRIVER_TYPE> drivers;
596
597         for (int i = 0; i != irr::video::EDT_COUNT; i++) {
598                 if (irr::IrrlichtDevice::isDriverSupported((irr::video::E_DRIVER_TYPE)i))
599                         drivers.push_back((irr::video::E_DRIVER_TYPE)i);
600         }
601
602         return drivers;
603 }
604
605 const char *getVideoDriverName(irr::video::E_DRIVER_TYPE type)
606 {
607         static const char *driver_ids[] = {
608                 "null",
609                 "software",
610                 "burningsvideo",
611                 "direct3d8",
612                 "direct3d9",
613                 "opengl",
614                 "ogles1",
615                 "ogles2",
616         };
617
618         return driver_ids[type];
619 }
620
621
622 const char *getVideoDriverFriendlyName(irr::video::E_DRIVER_TYPE type)
623 {
624         static const char *driver_names[] = {
625                 "NULL Driver",
626                 "Software Renderer",
627                 "Burning's Video",
628                 "Direct3D 8",
629                 "Direct3D 9",
630                 "OpenGL",
631                 "OpenGL ES1",
632                 "OpenGL ES2",
633         };
634
635         return driver_names[type];
636 }
637
638
639 #ifndef __ANDROID__
640 #ifdef XORG_USED
641
642 static float calcDisplayDensity()
643 {
644         const char* current_display = getenv("DISPLAY");
645
646         if (current_display != NULL) {
647                         Display * x11display = XOpenDisplay(current_display);
648
649                         if (x11display != NULL) {
650                                 /* try x direct */
651                                 float dpi_height =
652                                                 floor(DisplayHeight(x11display, 0) /
653                                                                 (DisplayHeightMM(x11display, 0) * 0.039370) + 0.5);
654                                 float dpi_width =
655                                                 floor(DisplayWidth(x11display, 0) /
656                                                                 (DisplayWidthMM(x11display, 0) * 0.039370) +0.5);
657
658                                 XCloseDisplay(x11display);
659
660                                 return std::max(dpi_height,dpi_width) / 96.0;
661                         }
662                 }
663
664         /* return manually specified dpi */
665         return g_settings->getFloat("screen_dpi")/96.0;
666 }
667
668
669 float getDisplayDensity()
670 {
671         static float cached_display_density = calcDisplayDensity();
672         return cached_display_density;
673 }
674
675
676 #else
677 float getDisplayDensity()
678 {
679         return g_settings->getFloat("screen_dpi")/96.0;
680 }
681 #endif
682
683 v2u32 getDisplaySize()
684 {
685         IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
686
687         core::dimension2d<u32> deskres = nulldevice->getVideoModeList()->getDesktopResolution();
688         nulldevice -> drop();
689
690         return deskres;
691 }
692 #endif
693 #endif
694
695 } //namespace porting
696