/*************************************************************************/ /* os_unix.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "os_unix.h" #ifdef UNIX_ENABLED #include "servers/visual_server.h" #include "thread_posix.h" #include "semaphore_posix.h" #include "mutex_posix.h" #include "rw_lock_posix.h" #include "core/os/thread_dummy.h" //#include "core/io/file_access_buffered_fa.h" #include "file_access_unix.h" #include "dir_access_unix.h" #include "tcp_server_posix.h" #include "stream_peer_tcp_posix.h" #include "packet_peer_udp_posix.h" #ifdef __APPLE__ #include #endif #ifdef __FreeBSD__ #include #endif #include #include #include #include #include #include #include #include #include #include "global_config.h" extern bool _print_error_enabled; void OS_Unix::print_error(const char* p_function,const char* p_file,int p_line,const char *p_code,const char*p_rationale,ErrorType p_type) { if (!_print_error_enabled) return; const char* err_details; if (p_rationale && p_rationale[0]) err_details=p_rationale; else err_details=p_code; switch(p_type) { case ERR_ERROR: print("\E[1;31mERROR: %s: \E[0m\E[1m%s\n",p_function,err_details); print("\E[0;31m At: %s:%i.\E[0m\n",p_file,p_line); break; case ERR_WARNING: print("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n",p_function,err_details); print("\E[0;33m At: %s:%i.\E[0m\n",p_file,p_line); break; case ERR_SCRIPT: print("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n",p_function,err_details); print("\E[0;35m At: %s:%i.\E[0m\n",p_file,p_line); break; case ERR_SHADER: print("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n",p_function,err_details); print("\E[0;36m At: %s:%i.\E[0m\n",p_file,p_line); break; } } void OS_Unix::debug_break() { assert(false); }; int OS_Unix::get_audio_driver_count() const { return 1; } const char * OS_Unix::get_audio_driver_name(int p_driver) const { return "dummy"; } int OS_Unix::unix_initialize_audio(int p_audio_driver) { return 0; } // Very simple signal handler to reap processes where ::execute was called with // !p_blocking void handle_sigchld(int sig) { int saved_errno = errno; while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {} errno = saved_errno; } void OS_Unix::initialize_core() { #ifdef NO_PTHREADS ThreadDummy::make_default(); SemaphoreDummy::make_default(); MutexDummy::make_default(); #else ThreadPosix::make_default(); SemaphorePosix::make_default(); MutexPosix::make_default(); RWLockPosix::make_default(); #endif FileAccess::make_default(FileAccess::ACCESS_RESOURCES); FileAccess::make_default(FileAccess::ACCESS_USERDATA); FileAccess::make_default(FileAccess::ACCESS_FILESYSTEM); //FileAccessBufferedFA::make_default(); DirAccess::make_default(DirAccess::ACCESS_RESOURCES); DirAccess::make_default(DirAccess::ACCESS_USERDATA); DirAccess::make_default(DirAccess::ACCESS_FILESYSTEM); #ifndef NO_NETWORK TCPServerPosix::make_default(); StreamPeerTCPPosix::make_default(); PacketPeerUDPPosix::make_default(); IP_Unix::make_default(); #endif ticks_start=0; ticks_start=get_ticks_usec(); struct sigaction sa; sa.sa_handler = &handle_sigchld; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, 0) == -1) { perror("ERROR sigaction() failed:"); } } void OS_Unix::finalize_core() { } void OS_Unix::vprint(const char* p_format, va_list p_list,bool p_stder) { if (p_stder) { vfprintf(stderr,p_format,p_list); fflush(stderr); } else { vprintf(p_format,p_list); fflush(stdout); } } void OS_Unix::print(const char *p_format, ... ) { va_list argp; va_start(argp, p_format); vprintf(p_format, argp ); va_end(argp); } void OS_Unix::alert(const String& p_alert,const String& p_title) { fprintf(stderr,"ERROR: %s\n",p_alert.utf8().get_data()); } static int has_data(FILE* p_fd, int timeout_usec = 0) { fd_set readset; int fd = fileno(p_fd); FD_ZERO(&readset); FD_SET(fd, &readset); timeval time; time.tv_sec = 0; time.tv_usec = timeout_usec; int res = 0;//select(fd + 1, &readset, NULL, NULL, &time); return res > 0; }; String OS_Unix::get_stdin_string(bool p_block) { String ret; if (p_block) { char buff[1024]; ret = stdin_buf + fgets(buff,1024,stdin); stdin_buf = ""; return ret; }; while (has_data(stdin)) { char ch; read(fileno(stdin), &ch, 1); if (ch == '\n') { ret = stdin_buf; stdin_buf = ""; return ret; } else { char str[2] = { ch, 0 }; stdin_buf += str; }; }; return ""; } String OS_Unix::get_name() { return "Unix"; } uint64_t OS_Unix::get_unix_time() const { return time(NULL); }; uint64_t OS_Unix::get_system_time_secs() const { struct timeval tv_now; gettimeofday(&tv_now, NULL); //localtime(&tv_now.tv_usec); //localtime((const long *)&tv_now.tv_usec); return uint64_t(tv_now.tv_sec); } OS::Date OS_Unix::get_date(bool utc) const { time_t t=time(NULL); struct tm *lt; if (utc) lt=gmtime(&t); else lt=localtime(&t); Date ret; ret.year=1900+lt->tm_year; // Index starting at 1 to match OS_Unix::get_date // and Windows SYSTEMTIME and tm_mon follows the typical structure // of 0-11, noted here: http://www.cplusplus.com/reference/ctime/tm/ ret.month=(Month)(lt->tm_mon + 1); ret.day=lt->tm_mday; ret.weekday=(Weekday)lt->tm_wday; ret.dst=lt->tm_isdst; return ret; } OS::Time OS_Unix::get_time(bool utc) const { time_t t=time(NULL); struct tm *lt; if (utc) lt=gmtime(&t); else lt=localtime(&t); Time ret; ret.hour=lt->tm_hour; ret.min=lt->tm_min; ret.sec=lt->tm_sec; get_time_zone_info(); return ret; } OS::TimeZoneInfo OS_Unix::get_time_zone_info() const { time_t t = time(NULL); struct tm *lt = localtime(&t); char name[16]; strftime(name, 16, "%Z", lt); name[15] = 0; TimeZoneInfo ret; ret.name = name; char bias_buf[16]; strftime(bias_buf, 16, "%z", lt); int bias; bias_buf[15] = 0; sscanf(bias_buf, "%d", &bias); // convert from ISO 8601 (1 minute=1, 1 hour=100) to minutes int hour = (int)bias / 100; int minutes = bias % 100; if (bias < 0) ret.bias = hour * 60 - minutes; else ret.bias = hour * 60 + minutes; return ret; } void OS_Unix::delay_usec(uint32_t p_usec) const { usleep(p_usec); } uint64_t OS_Unix::get_ticks_usec() const { struct timeval tv_now; gettimeofday(&tv_now,NULL); uint64_t longtime = (uint64_t)tv_now.tv_usec + (uint64_t)tv_now.tv_sec*1000000L; longtime-=ticks_start; return longtime; } Error OS_Unix::execute(const String& p_path, const List& p_arguments,bool p_blocking,ProcessID *r_child_id,String* r_pipe,int *r_exitcode) { if (p_blocking && r_pipe) { String argss; argss="\""+p_path+"\""; for(int i=0;i cs; cs.push_back(p_path.utf8()); for(int i=0;i args; for(int i=0;iget("application/use_shared_user_dir"); if (use_godot) return get_environment("HOME")+"/.godot/app_userdata/"+an; else return get_environment("HOME")+"/."+an; } } return GlobalConfig::get_singleton()->get_resource_path(); } bool OS_Unix::check_feature_support(const String& p_feature) { return VisualServer::get_singleton()->has_os_feature(p_feature); } String OS_Unix::get_installed_templates_path() const { String p=get_global_settings_path(); if (p!="") return p+"/templates/"; else return ""; } String OS_Unix::get_executable_path() const { #ifdef __linux__ //fix for running from a symlink char buf[256]; memset(buf,0,256); readlink("/proc/self/exe", buf, sizeof(buf)); String b; b.parse_utf8(buf); if (b=="") { WARN_PRINT("Couldn't get executable path from /proc/self/exe, using argv[0]"); return OS::get_executable_path(); } return b; #elif defined(__FreeBSD__) char resolved_path[MAXPATHLEN]; realpath(OS::get_executable_path().utf8().get_data(), resolved_path); return String(resolved_path); #elif defined(__APPLE__) char temp_path[1]; uint32_t buff_size=1; _NSGetExecutablePath(temp_path, &buff_size); char* resolved_path = new char[buff_size + 1]; if (_NSGetExecutablePath(resolved_path, &buff_size) == 1) WARN_PRINT("MAXPATHLEN is too small"); String path(resolved_path); delete[] resolved_path; return path; #else ERR_PRINT("Warning, don't know how to obtain executable path on this OS! Please override this function properly."); return OS::get_executable_path(); #endif } #endif