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