]> git.lizzy.rs Git - dragonfireclient.git/blob - src/porting.cpp
Set WM_CLASS window hint for Xorg
[dragonfireclient.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[] = {"bin\\Release", "bin\\Build", NULL};
294         return (removeStringEnd(path, ends) != "");
295 }
296
297 std::string get_sysinfo()
298 {
299 #ifdef _WIN32
300         OSVERSIONINFO osvi;
301         std::ostringstream oss;
302         std::string tmp;
303         ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
304         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
305         GetVersionEx(&osvi);
306         tmp = osvi.szCSDVersion;
307         std::replace(tmp.begin(), tmp.end(), ' ', '_');
308
309         oss << "Windows/" << osvi.dwMajorVersion << "."
310                 << osvi.dwMinorVersion;
311         if(osvi.szCSDVersion[0])
312                 oss << "-" << tmp;
313         oss << " ";
314         #ifdef _WIN64
315         oss << "x86_64";
316         #else
317         BOOL is64 = FALSE;
318         if(IsWow64Process(GetCurrentProcess(), &is64) && is64)
319                 oss << "x86_64"; // 32-bit app on 64-bit OS
320         else
321                 oss << "x86";
322         #endif
323
324         return oss.str();
325 #else
326         struct utsname osinfo;
327         uname(&osinfo);
328         return std::string(osinfo.sysname) + "/"
329                 + osinfo.release + " " + osinfo.machine;
330 #endif
331 }
332
333 void initializePaths()
334 {
335 #if RUN_IN_PLACE
336         /*
337                 Use relative paths if RUN_IN_PLACE
338         */
339
340         infostream<<"Using relative paths (RUN_IN_PLACE)"<<std::endl;
341
342         /*
343                 Windows
344         */
345         #if defined(_WIN32)
346
347         const DWORD buflen = 1000;
348         char buf[buflen];
349         DWORD len;
350
351         // Find path of executable and set path_share relative to it
352         len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
353         assert(len < buflen);
354         pathRemoveFile(buf, '\\');
355
356         if(detectMSVCBuildDir(buf)){
357                 infostream<<"MSVC build directory detected"<<std::endl;
358                 path_share = std::string(buf) + "\\..\\..";
359                 path_user = std::string(buf) + "\\..\\..";
360         }
361         else{
362                 path_share = std::string(buf) + "\\..";
363                 path_user = std::string(buf) + "\\..";
364         }
365
366         /*
367                 Linux
368         */
369         #elif defined(linux)
370
371         char buf[BUFSIZ];
372         memset(buf, 0, BUFSIZ);
373         // Get path to executable
374         assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
375
376         pathRemoveFile(buf, '/');
377
378         path_share = std::string(buf) + "/..";
379         path_user = std::string(buf) + "/..";
380
381         /*
382                 OS X
383         */
384         #elif defined(__APPLE__)
385
386         //https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dyld.3.html
387         //TODO: Test this code
388         char buf[BUFSIZ];
389         uint32_t len = sizeof(buf);
390         assert(_NSGetExecutablePath(buf, &len) != -1);
391
392         pathRemoveFile(buf, '/');
393
394         path_share = std::string(buf) + "/..";
395         path_user = std::string(buf) + "/..";
396
397         /*
398                 FreeBSD
399         */
400         #elif defined(__FreeBSD__)
401
402         int mib[4];
403         char buf[BUFSIZ];
404         size_t len = sizeof(buf);
405
406         mib[0] = CTL_KERN;
407         mib[1] = KERN_PROC;
408         mib[2] = KERN_PROC_PATHNAME;
409         mib[3] = -1;
410         assert(sysctl(mib, 4, buf, &len, NULL, 0) != -1);
411
412         pathRemoveFile(buf, '/');
413
414         path_share = std::string(buf) + "/..";
415         path_user = std::string(buf) + "/..";
416
417         #else
418
419         //TODO: Get path of executable. This assumes working directory is bin/
420         dstream<<"WARNING: Relative path not properly supported on this platform"
421                         <<std::endl;
422
423         /* scriptapi no longer allows paths that start with "..", so assuming that
424            the current working directory is bin/, strip off the last component. */
425         char *cwd = getcwd(NULL, 0);
426         pathRemoveFile(cwd, '/');
427         path_share = std::string(cwd);
428         path_user = std::string(cwd);
429
430         #endif
431
432 #else // RUN_IN_PLACE
433
434         /*
435                 Use platform-specific paths otherwise
436         */
437
438         infostream<<"Using system-wide paths (NOT RUN_IN_PLACE)"<<std::endl;
439
440         /*
441                 Windows
442         */
443         #if defined(_WIN32)
444
445         const DWORD buflen = 1000;
446         char buf[buflen];
447         DWORD len;
448
449         // Find path of executable and set path_share relative to it
450         len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
451         assert(len < buflen);
452         pathRemoveFile(buf, '\\');
453
454         // Use ".\bin\.."
455         path_share = std::string(buf) + "\\..";
456
457         // Use "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
458         len = GetEnvironmentVariable("APPDATA", buf, buflen);
459         assert(len < buflen);
460         path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME;
461
462         /*
463                 Linux
464         */
465         #elif defined(linux)
466
467         // Get path to executable
468         std::string bindir = "";
469         {
470                 char buf[BUFSIZ];
471                 memset(buf, 0, BUFSIZ);
472                 if (readlink("/proc/self/exe", buf, BUFSIZ-1) == -1) {
473                         errorstream << "Unable to read bindir "<< std::endl;
474 #ifndef __ANDROID__
475                         assert("Unable to read bindir" == 0);
476 #endif
477                 } else {
478                         pathRemoveFile(buf, '/');
479                         bindir = buf;
480                 }
481         }
482
483         // Find share directory from these.
484         // It is identified by containing the subdirectory "builtin".
485         std::list<std::string> trylist;
486         std::string static_sharedir = STATIC_SHAREDIR;
487         if(static_sharedir != "" && static_sharedir != ".")
488                 trylist.push_back(static_sharedir);
489         trylist.push_back(
490                         bindir + DIR_DELIM + ".." + DIR_DELIM + "share" + DIR_DELIM + PROJECT_NAME);
491         trylist.push_back(bindir + DIR_DELIM + "..");
492 #ifdef __ANDROID__
493         trylist.push_back(path_user);
494 #endif
495
496         for(std::list<std::string>::const_iterator i = trylist.begin();
497                         i != trylist.end(); i++)
498         {
499                 const std::string &trypath = *i;
500                 if(!fs::PathExists(trypath) || !fs::PathExists(trypath + DIR_DELIM + "builtin")){
501                         dstream<<"WARNING: system-wide share not found at \""
502                                         <<trypath<<"\""<<std::endl;
503                         continue;
504                 }
505                 // Warn if was not the first alternative
506                 if(i != trylist.begin()){
507                         dstream<<"WARNING: system-wide share found at \""
508                                         <<trypath<<"\""<<std::endl;
509                 }
510                 path_share = trypath;
511                 break;
512         }
513 #ifndef __ANDROID__
514         path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
515 #endif
516
517         /*
518                 OS X
519         */
520         #elif defined(__APPLE__)
521
522         // Code based on
523         // http://stackoverflow.com/questions/516200/relative-paths-not-working-in-xcode-c
524         CFBundleRef main_bundle = CFBundleGetMainBundle();
525         CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
526         char path[PATH_MAX];
527         if(CFURLGetFileSystemRepresentation(resources_url, TRUE, (UInt8 *)path, PATH_MAX))
528         {
529                 dstream<<"Bundle resource path: "<<path<<std::endl;
530                 //chdir(path);
531                 path_share = std::string(path) + DIR_DELIM + "share";
532         }
533         else
534         {
535                 // error!
536                 dstream<<"WARNING: Could not determine bundle resource path"<<std::endl;
537         }
538         CFRelease(resources_url);
539
540         path_user = std::string(getenv("HOME")) + "/Library/Application Support/" + PROJECT_NAME;
541
542         #else // FreeBSD, and probably many other POSIX-like systems.
543
544         path_share = STATIC_SHAREDIR;
545         path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
546
547         #endif
548
549 #endif // RUN_IN_PLACE
550 }
551
552 static irr::IrrlichtDevice* device;
553
554 void initIrrlicht(irr::IrrlichtDevice * _device) {
555         device = _device;
556 }
557
558 void setXorgClassHint(const video::SExposedVideoData &video_data,
559         const std::string &name)
560 {
561 #ifdef XORG_USED
562         XClassHint *classhint = XAllocClassHint();
563         classhint->res_name  = (char *)name.c_str();
564         classhint->res_class = (char *)name.c_str();
565
566         XSetClassHint((Display *)video_data.OpenGLLinux.X11Display,
567                 video_data.OpenGLLinux.X11Window, classhint);
568         XFree(classhint);
569 #endif
570 }
571
572 #ifndef SERVER
573 v2u32 getWindowSize() {
574         return device->getVideoDriver()->getScreenSize();
575 }
576
577 #ifndef __ANDROID__
578
579 float getDisplayDensity() {
580         return g_settings->getFloat("screen_dpi")/96.0;
581 }
582
583 v2u32 getDisplaySize() {
584         IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
585
586         core::dimension2d<u32> deskres = nulldevice->getVideoModeList()->getDesktopResolution();
587         nulldevice -> drop();
588
589         return deskres;
590 }
591 #endif
592 #endif
593
594 } //namespace porting
595