Chris@223: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@223: Chris@223: /* Chris@1356: Sonic Visualiser Chris@1356: An audio file viewer and annotation editor. Chris@1356: Centre for Digital Music, Queen Mary, University of London. Chris@1475: This file copyright 2006-2018 Chris Cannam and QMUL. Chris@223: Chris@1356: This program is free software; you can redistribute it and/or Chris@1356: modify it under the terms of the GNU General Public License as Chris@1356: published by the Free Software Foundation; either version 2 of the Chris@1356: License, or (at your option) any later version. See the file Chris@1356: COPYING included with this distribution for more information. Chris@223: */ Chris@223: Chris@223: #include "System.h" Chris@223: Chris@223: #include Chris@223: #include Chris@223: Chris@223: #include Chris@223: Chris@223: #ifndef _WIN32 Chris@223: #include Chris@223: #include Chris@608: #include Chris@608: #include Chris@223: #endif Chris@223: Chris@1825: #ifndef AVOID_WINRT_DEPENDENCY Chris@1818: #ifdef _MSC_VER Chris@1818: #include Chris@1818: #endif Chris@1825: #endif Chris@1818: Chris@223: #ifdef __APPLE__ Chris@223: #include Chris@223: #include Chris@223: #endif Chris@223: Chris@405: #include Chris@405: #include Chris@405: Chris@223: #include Chris@223: Chris@255: #ifdef __APPLE__ Chris@255: extern "C" { Chris@1356: void * Chris@1356: rpl_realloc (void *p, size_t n) Chris@255: { Chris@1356: p = realloc(p, n); Chris@1356: if (p == 0 && n == 0) Chris@1356: { Chris@1356: p = malloc(0); Chris@1356: } Chris@1356: return p; Chris@255: } Chris@255: } Chris@255: #endif Chris@255: Chris@223: #ifdef _WIN32 Chris@223: Chris@223: extern "C" { Chris@223: Chris@1218: #ifdef _MSC_VER Chris@1356: void usleep(unsigned long usec) Chris@1356: { Chris@1356: ::Sleep(usec / 1000); Chris@1356: } Chris@1218: #endif Chris@223: Chris@1475: int gettimeofday(struct timeval *tv, void * /* tz */) Chris@1356: { Chris@1356: union { Chris@1356: long long ns100; Chris@1356: FILETIME ft; Chris@1356: } now; Chris@223: Chris@1356: ::GetSystemTimeAsFileTime(&now.ft); Chris@1356: tv->tv_usec = (long)((now.ns100 / 10LL) % 1000000LL); Chris@1356: tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL); Chris@1356: return 0; Chris@1356: } Chris@223: Chris@223: } Chris@223: Chris@223: #endif Chris@223: Chris@223: ProcessStatus Chris@223: GetProcessStatus(int pid) Chris@223: { Chris@223: #ifdef _WIN32 Chris@223: HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); Chris@223: if (!handle) { Chris@223: return ProcessNotRunning; Chris@223: } else { Chris@223: CloseHandle(handle); Chris@223: return ProcessRunning; Chris@223: } Chris@223: #else Chris@223: if (kill(getpid(), 0) == 0) { Chris@223: if (kill(pid, 0) == 0) { Chris@223: return ProcessRunning; Chris@223: } else { Chris@223: return ProcessNotRunning; Chris@223: } Chris@223: } else { Chris@223: return UnknownProcessStatus; Chris@223: } Chris@223: #endif Chris@223: } Chris@223: Chris@223: #ifdef _WIN32 Chris@223: /* MEMORYSTATUSEX is missing from older Windows headers, so define a Chris@223: local replacement. This trick from MinGW source code. Ugh */ Chris@223: typedef struct Chris@223: { Chris@223: DWORD dwLength; Chris@223: DWORD dwMemoryLoad; Chris@223: DWORDLONG ullTotalPhys; Chris@223: DWORDLONG ullAvailPhys; Chris@223: DWORDLONG ullTotalPageFile; Chris@223: DWORDLONG ullAvailPageFile; Chris@223: DWORDLONG ullTotalVirtual; Chris@223: DWORDLONG ullAvailVirtual; Chris@223: DWORDLONG ullAvailExtendedVirtual; Chris@223: } lMEMORYSTATUSEX; Chris@1218: typedef BOOL (WINAPI *PFN_MS_EX) (lMEMORYSTATUSEX*); Chris@223: #endif Chris@223: Chris@223: void Chris@1038: GetRealMemoryMBAvailable(ssize_t &available, ssize_t &total) Chris@223: { Chris@223: available = -1; Chris@223: total = -1; Chris@223: Chris@223: #ifdef _WIN32 Chris@223: Chris@223: static bool checked = false; Chris@223: static bool exFound = false; Chris@223: static PFN_MS_EX ex; Chris@223: Chris@223: if (!checked) { Chris@223: Chris@223: HMODULE h = GetModuleHandleA("kernel32.dll"); Chris@223: Chris@223: if (h) { Chris@223: if ((ex = (PFN_MS_EX)GetProcAddress(h, "GlobalMemoryStatusEx"))) { Chris@223: exFound = true; Chris@223: } Chris@223: } Chris@223: Chris@223: checked = true; Chris@223: } Chris@223: Chris@223: DWORDLONG wavail = 0; Chris@223: DWORDLONG wtotal = 0; Chris@223: Chris@223: if (exFound) { Chris@223: Chris@223: lMEMORYSTATUSEX lms; Chris@1356: lms.dwLength = sizeof(lms); Chris@1356: if (!ex(&lms)) { Chris@843: cerr << "WARNING: GlobalMemoryStatusEx failed: error code " Chris@1356: << GetLastError() << endl; Chris@223: return; Chris@223: } Chris@223: wavail = lms.ullAvailPhys; Chris@223: wtotal = lms.ullTotalPhys; Chris@223: Chris@223: } else { Chris@223: Chris@223: /* Fall back to GlobalMemoryStatus which is always available. Chris@223: but returns wrong results for physical memory > 4GB */ Chris@223: Chris@1356: MEMORYSTATUS ms; Chris@1356: GlobalMemoryStatus(&ms); Chris@1356: wavail = ms.dwAvailPhys; Chris@223: wtotal = ms.dwTotalPhys; Chris@223: } Chris@223: Chris@223: DWORDLONG size = wavail / 1048576; Chris@223: if (size > INT_MAX) size = INT_MAX; Chris@1038: available = ssize_t(size); Chris@223: Chris@223: size = wtotal / 1048576; Chris@223: if (size > INT_MAX) size = INT_MAX; Chris@1038: total = ssize_t(size); Chris@223: Chris@223: return; Chris@223: Chris@223: #else Chris@223: #ifdef __APPLE__ Chris@223: Chris@1779: unsigned int val32; Chris@1779: int64_t val64; Chris@223: int mib[2]; Chris@223: size_t size_sys; Chris@223: Chris@223: mib[0] = CTL_HW; Chris@223: Chris@1779: mib[1] = HW_MEMSIZE; Chris@1779: size_sys = sizeof(val64); Chris@1779: sysctl(mib, 2, &val64, &size_sys, NULL, 0); Chris@1779: if (val64) total = val64 / 1048576; Chris@223: Chris@223: mib[1] = HW_USERMEM; Chris@1779: size_sys = sizeof(val32); Chris@1779: sysctl(mib, 2, &val32, &size_sys, NULL, 0); Chris@1779: if (val32) available = val32 / 1048576; Chris@1779: Chris@1779: // The newer memsize sysctl returns a 64-bit value, but usermem is Chris@1779: // an old 32-bit value that doesn't seem to have an updated Chris@1779: // alternative (?) - so it can't return more than 2G. In practice Chris@1779: // it seems to return values far lower than that, even where more Chris@1779: // than 2G of real memory is free. So we can't actually tell when Chris@1779: // we're getting low on memory at all. Most of the time I think we Chris@1779: // just need to use an arbitrary value like this one. Chris@1779: if (available < total/4) { Chris@1779: available = total/4; Chris@1779: } Chris@223: Chris@223: return; Chris@223: Chris@223: #else Chris@223: Chris@223: FILE *meminfo = fopen("/proc/meminfo", "r"); Chris@223: if (!meminfo) return; Chris@223: Chris@223: char buf[256]; Chris@223: while (!feof(meminfo)) { Chris@1363: if (!fgets(buf, 256, meminfo)) { Chris@1424: fclose(meminfo); Chris@1363: return; Chris@1363: } Chris@223: bool isMemFree = (strncmp(buf, "MemFree:", 8) == 0); Chris@223: bool isMemTotal = (!isMemFree && (strncmp(buf, "MemTotal:", 9) == 0)); Chris@223: if (isMemFree || isMemTotal) { Chris@223: QString line = QString(buf).trimmed(); Chris@223: QStringList elements = line.split(' ', QString::SkipEmptyParts); Chris@223: QString unit = "kB"; Chris@223: if (elements.size() > 2) unit = elements[2]; Chris@223: int size = elements[1].toInt(); Chris@843: // cerr << "have size \"" << size << "\", unit \"" Chris@843: // << unit << "\"" << endl; Chris@223: if (unit.toLower() == "gb") size = size * 1024; Chris@223: else if (unit.toLower() == "mb") size = size; Chris@223: else if (unit.toLower() == "kb") size = size / 1024; Chris@223: else size = size / 1048576; Chris@223: Chris@223: if (isMemFree) available = size; Chris@223: else total = size; Chris@223: } Chris@223: if (available != -1 && total != -1) { Chris@223: fclose(meminfo); Chris@223: return; Chris@223: } Chris@223: } Chris@223: fclose(meminfo); Chris@223: Chris@223: return; Chris@223: Chris@223: #endif Chris@223: #endif Chris@223: } Chris@223: Chris@1038: ssize_t Chris@223: GetDiscSpaceMBAvailable(const char *path) Chris@223: { Chris@223: #ifdef _WIN32 Chris@223: ULARGE_INTEGER available, total, totalFree; Chris@223: if (GetDiskFreeSpaceExA(path, &available, &total, &totalFree)) { Chris@1356: __int64 a = available.QuadPart; Chris@223: a /= 1048576; Chris@223: if (a > INT_MAX) a = INT_MAX; Chris@1038: return ssize_t(a); Chris@223: } else { Chris@843: cerr << "WARNING: GetDiskFreeSpaceEx failed: error code " Chris@1356: << GetLastError() << endl; Chris@223: return -1; Chris@223: } Chris@223: #else Chris@223: struct statvfs buf; Chris@223: if (!statvfs(path, &buf)) { Chris@223: // do the multiplies and divides in this order to reduce the Chris@223: // likelihood of arithmetic overflow Chris@843: // cerr << "statvfs(" << path << ") says available: " << buf.f_bavail << ", block size: " << buf.f_bsize << endl; Chris@223: uint64_t available = ((buf.f_bavail / 1024) * buf.f_bsize) / 1024; Chris@223: if (available > INT_MAX) available = INT_MAX; Chris@1038: return ssize_t(available); Chris@223: } else { Chris@223: perror("statvfs failed"); Chris@223: return -1; Chris@223: } Chris@223: #endif Chris@223: } Chris@303: Chris@835: #ifdef _WIN32 Chris@835: extern void SystemMemoryBarrier() Chris@835: { Chris@1218: #ifdef _MSC_VER Chris@835: MemoryBarrier(); Chris@835: #else /* mingw */ Chris@835: LONG Barrier = 0; Chris@835: __asm__ __volatile__("xchgl %%eax,%0 " Chris@835: : "=r" (Barrier)); Chris@835: #endif Chris@835: } Chris@835: #else /* !_WIN32 */ Chris@1357: #if !defined(__APPLE__) && defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ == 0)) Chris@835: void Chris@835: SystemMemoryBarrier() Chris@835: { Chris@835: pthread_mutex_t dummy = PTHREAD_MUTEX_INITIALIZER; Chris@835: pthread_mutex_lock(&dummy); Chris@835: pthread_mutex_unlock(&dummy); Chris@835: } Chris@835: #endif /* !defined(__APPLE__) etc */ Chris@835: #endif /* !_WIN32 */ Chris@835: Chris@835: Chris@1582: static char *startupLocale = nullptr; Chris@303: Chris@303: void Chris@303: StoreStartupLocale() Chris@303: { Chris@1582: char *loc = setlocale(LC_ALL, nullptr); Chris@303: if (!loc) return; Chris@303: if (startupLocale) free(startupLocale); Chris@303: startupLocale = strdup(loc); Chris@303: } Chris@303: Chris@303: void Chris@303: RestoreStartupLocale() Chris@303: { Chris@303: if (!startupLocale) { Chris@303: setlocale(LC_ALL, ""); Chris@303: } else { Chris@303: setlocale(LC_ALL, startupLocale); Chris@303: } Chris@303: } Chris@223: Chris@1818: bool Chris@1819: OSReportsDarkThemeActive() Chris@1818: { Chris@1825: #ifndef AVOID_WINRT_DEPENDENCY Chris@1818: #ifdef _MSC_VER Chris@1818: using namespace winrt::Windows::UI::ViewManagement; Chris@1818: UISettings settings; Chris@1818: auto background = settings.GetColorValue(UIColorType::Background); Chris@1818: if (int(background.R) + int(background.G) + int(background.B) < 384) { Chris@1818: return true; Chris@1818: } Chris@1818: #endif Chris@1825: #endif Chris@1818: return false; Chris@1818: } Chris@1818: Chris@1820: bool Chris@1820: OSQueryAccentColour(int &r, int &g, int &b) Chris@1820: { Chris@1820: SVCERR << "OSQueryAccentColour() called" << endl; Chris@1825: #ifndef AVOID_WINRT_DEPENDENCY Chris@1820: #ifdef _MSC_VER Chris@1820: using namespace winrt::Windows::UI::ViewManagement; Chris@1821: bool dark = OSReportsDarkThemeActive(); Chris@1820: UISettings settings; Chris@1821: auto accent = settings.GetColorValue Chris@1821: (dark ? UIColorType::AccentLight1 : UIColorType::Accent); Chris@1821: r = accent.R; Chris@1821: g = accent.G; Chris@1821: b = accent.B; Chris@1820: return true; Chris@1820: #endif Chris@1825: #endif Chris@1826: (void)r; Chris@1826: (void)g; Chris@1826: (void)b; Chris@1820: return false; Chris@1820: } Chris@1820: Chris@223: double mod(double x, double y) { return x - (y * floor(x / y)); } Chris@223: float modf(float x, float y) { return x - (y * floorf(x / y)); } Chris@223: Chris@223: double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; } Chris@1039: float princargf(float a) { return float(princarg(a)); } Chris@223: Chris@1475: bool Chris@1475: getEnvUtf8(std::string variable, std::string &value) Chris@1475: { Chris@1475: value = ""; Chris@1475: Chris@1475: #ifdef _WIN32 Chris@1475: int wvarlen = MultiByteToWideChar(CP_UTF8, 0, Chris@1475: variable.c_str(), int(variable.length()), Chris@1475: 0, 0); Chris@1475: if (wvarlen < 0) { Chris@1475: SVCERR << "WARNING: Unable to convert environment variable name " Chris@1475: << variable << " to wide characters" << endl; Chris@1475: return false; Chris@1475: } Chris@1475: Chris@1475: wchar_t *wvarbuf = new wchar_t[wvarlen + 1]; Chris@1475: (void)MultiByteToWideChar(CP_UTF8, 0, Chris@1475: variable.c_str(), int(variable.length()), Chris@1475: wvarbuf, wvarlen); Chris@1475: wvarbuf[wvarlen] = L'\0'; Chris@1475: Chris@1475: wchar_t *wvalue = _wgetenv(wvarbuf); Chris@1176: Chris@1475: delete[] wvarbuf; Chris@1475: Chris@1475: if (!wvalue) { Chris@1475: return false; Chris@1475: } Chris@1475: Chris@1480: int wvallen = int(wcslen(wvalue)); Chris@1475: int vallen = WideCharToMultiByte(CP_UTF8, 0, Chris@1480: wvalue, wvallen, Chris@1475: 0, 0, 0, 0); Chris@1475: if (vallen < 0) { Chris@1475: SVCERR << "WARNING: Unable to convert environment value to UTF-8" Chris@1475: << endl; Chris@1475: return false; Chris@1475: } Chris@1475: Chris@1475: char *val = new char[vallen + 1]; Chris@1475: (void)WideCharToMultiByte(CP_UTF8, 0, Chris@1480: wvalue, wvallen, Chris@1475: val, vallen, 0, 0); Chris@1475: val[vallen] = '\0'; Chris@1475: Chris@1475: value = val; Chris@1475: Chris@1475: delete[] val; Chris@1475: return true; Chris@1475: Chris@1475: #else Chris@1475: Chris@1475: char *val = getenv(variable.c_str()); Chris@1475: if (!val) { Chris@1475: return false; Chris@1475: } Chris@1475: Chris@1475: value = val; Chris@1475: return true; Chris@1475: Chris@1475: #endif Chris@1475: } Chris@1475: Chris@1475: bool Chris@1475: putEnvUtf8(std::string variable, std::string value) Chris@1475: { Chris@1477: #ifdef _WIN32 Chris@1475: std::string entry = variable + "=" + value; Chris@1475: Chris@1475: int wentlen = MultiByteToWideChar(CP_UTF8, 0, Chris@1475: entry.c_str(), int(entry.length()), Chris@1475: 0, 0); Chris@1475: if (wentlen < 0) { Chris@1475: SVCERR << "WARNING: Unable to convert environment entry to " Chris@1475: << "wide characters" << endl; Chris@1475: return false; Chris@1475: } Chris@1475: Chris@1475: wchar_t *wentbuf = new wchar_t[wentlen + 1]; Chris@1475: (void)MultiByteToWideChar(CP_UTF8, 0, Chris@1475: entry.c_str(), int(entry.length()), Chris@1475: wentbuf, wentlen); Chris@1475: wentbuf[wentlen] = L'\0'; Chris@1475: Chris@1475: int rv = _wputenv(wentbuf); Chris@1475: Chris@1475: delete[] wentbuf; Chris@1475: Chris@1475: if (rv != 0) { Chris@1475: SVCERR << "WARNING: Failed to set environment entry" << endl; Chris@1475: return false; Chris@1475: } Chris@1475: return true; Chris@1475: Chris@1475: #else Chris@1475: Chris@1477: int rv = setenv(variable.c_str(), value.c_str(), 1); Chris@1475: if (rv != 0) { Chris@1475: SVCERR << "WARNING: Failed to set environment entry" << endl; Chris@1475: return false; Chris@1475: } Chris@1475: return true; Chris@1475: Chris@1475: #endif Chris@1475: } Chris@1475: