porting.cpp 12 KB

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