annotate system/System.cpp @ 1821:472865574b1a background-mode

Use slightly lighter version of accent colour in dark mode - it generally seems more appropriate in practice
author Chris Cannam
date Fri, 24 Jan 2020 12:38:55 +0000
parents fc18ce2d99d7
children 029d7d3f0a3a
rev   line source
Chris@223 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@223 2
Chris@223 3 /*
Chris@1356 4 Sonic Visualiser
Chris@1356 5 An audio file viewer and annotation editor.
Chris@1356 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1475 7 This file copyright 2006-2018 Chris Cannam and QMUL.
Chris@223 8
Chris@1356 9 This program is free software; you can redistribute it and/or
Chris@1356 10 modify it under the terms of the GNU General Public License as
Chris@1356 11 published by the Free Software Foundation; either version 2 of the
Chris@1356 12 License, or (at your option) any later version. See the file
Chris@1356 13 COPYING included with this distribution for more information.
Chris@223 14 */
Chris@223 15
Chris@223 16 #include "System.h"
Chris@223 17
Chris@223 18 #include <QStringList>
Chris@223 19 #include <QString>
Chris@223 20
Chris@223 21 #include <stdint.h>
Chris@223 22
Chris@223 23 #ifndef _WIN32
Chris@223 24 #include <signal.h>
Chris@223 25 #include <sys/statvfs.h>
Chris@608 26 #include <locale.h>
Chris@608 27 #include <unistd.h>
Chris@223 28 #endif
Chris@223 29
Chris@1818 30 #ifdef _MSC_VER
Chris@1818 31 #include <winrt/Windows.UI.ViewManagement.h>
Chris@1818 32 #endif
Chris@1818 33
Chris@223 34 #ifdef __APPLE__
Chris@223 35 #include <sys/param.h>
Chris@223 36 #include <sys/sysctl.h>
Chris@223 37 #endif
Chris@223 38
Chris@405 39 #include <limits.h>
Chris@405 40 #include <cstdlib>
Chris@405 41
Chris@223 42 #include <iostream>
Chris@223 43
Chris@255 44 #ifdef __APPLE__
Chris@255 45 extern "C" {
Chris@1356 46 void *
Chris@1356 47 rpl_realloc (void *p, size_t n)
Chris@255 48 {
Chris@1356 49 p = realloc(p, n);
Chris@1356 50 if (p == 0 && n == 0)
Chris@1356 51 {
Chris@1356 52 p = malloc(0);
Chris@1356 53 }
Chris@1356 54 return p;
Chris@255 55 }
Chris@255 56 }
Chris@255 57 #endif
Chris@255 58
Chris@223 59 #ifdef _WIN32
Chris@223 60
Chris@223 61 extern "C" {
Chris@223 62
Chris@1218 63 #ifdef _MSC_VER
Chris@1356 64 void usleep(unsigned long usec)
Chris@1356 65 {
Chris@1356 66 ::Sleep(usec / 1000);
Chris@1356 67 }
Chris@1218 68 #endif
Chris@223 69
Chris@1475 70 int gettimeofday(struct timeval *tv, void * /* tz */)
Chris@1356 71 {
Chris@1356 72 union {
Chris@1356 73 long long ns100;
Chris@1356 74 FILETIME ft;
Chris@1356 75 } now;
Chris@223 76
Chris@1356 77 ::GetSystemTimeAsFileTime(&now.ft);
Chris@1356 78 tv->tv_usec = (long)((now.ns100 / 10LL) % 1000000LL);
Chris@1356 79 tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL);
Chris@1356 80 return 0;
Chris@1356 81 }
Chris@223 82
Chris@223 83 }
Chris@223 84
Chris@223 85 #endif
Chris@223 86
Chris@223 87 ProcessStatus
Chris@223 88 GetProcessStatus(int pid)
Chris@223 89 {
Chris@223 90 #ifdef _WIN32
Chris@223 91 HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
Chris@223 92 if (!handle) {
Chris@223 93 return ProcessNotRunning;
Chris@223 94 } else {
Chris@223 95 CloseHandle(handle);
Chris@223 96 return ProcessRunning;
Chris@223 97 }
Chris@223 98 #else
Chris@223 99 if (kill(getpid(), 0) == 0) {
Chris@223 100 if (kill(pid, 0) == 0) {
Chris@223 101 return ProcessRunning;
Chris@223 102 } else {
Chris@223 103 return ProcessNotRunning;
Chris@223 104 }
Chris@223 105 } else {
Chris@223 106 return UnknownProcessStatus;
Chris@223 107 }
Chris@223 108 #endif
Chris@223 109 }
Chris@223 110
Chris@223 111 #ifdef _WIN32
Chris@223 112 /* MEMORYSTATUSEX is missing from older Windows headers, so define a
Chris@223 113 local replacement. This trick from MinGW source code. Ugh */
Chris@223 114 typedef struct
Chris@223 115 {
Chris@223 116 DWORD dwLength;
Chris@223 117 DWORD dwMemoryLoad;
Chris@223 118 DWORDLONG ullTotalPhys;
Chris@223 119 DWORDLONG ullAvailPhys;
Chris@223 120 DWORDLONG ullTotalPageFile;
Chris@223 121 DWORDLONG ullAvailPageFile;
Chris@223 122 DWORDLONG ullTotalVirtual;
Chris@223 123 DWORDLONG ullAvailVirtual;
Chris@223 124 DWORDLONG ullAvailExtendedVirtual;
Chris@223 125 } lMEMORYSTATUSEX;
Chris@1218 126 typedef BOOL (WINAPI *PFN_MS_EX) (lMEMORYSTATUSEX*);
Chris@223 127 #endif
Chris@223 128
Chris@223 129 void
Chris@1038 130 GetRealMemoryMBAvailable(ssize_t &available, ssize_t &total)
Chris@223 131 {
Chris@223 132 available = -1;
Chris@223 133 total = -1;
Chris@223 134
Chris@223 135 #ifdef _WIN32
Chris@223 136
Chris@223 137 static bool checked = false;
Chris@223 138 static bool exFound = false;
Chris@223 139 static PFN_MS_EX ex;
Chris@223 140
Chris@223 141 if (!checked) {
Chris@223 142
Chris@223 143 HMODULE h = GetModuleHandleA("kernel32.dll");
Chris@223 144
Chris@223 145 if (h) {
Chris@223 146 if ((ex = (PFN_MS_EX)GetProcAddress(h, "GlobalMemoryStatusEx"))) {
Chris@223 147 exFound = true;
Chris@223 148 }
Chris@223 149 }
Chris@223 150
Chris@223 151 checked = true;
Chris@223 152 }
Chris@223 153
Chris@223 154 DWORDLONG wavail = 0;
Chris@223 155 DWORDLONG wtotal = 0;
Chris@223 156
Chris@223 157 if (exFound) {
Chris@223 158
Chris@223 159 lMEMORYSTATUSEX lms;
Chris@1356 160 lms.dwLength = sizeof(lms);
Chris@1356 161 if (!ex(&lms)) {
Chris@843 162 cerr << "WARNING: GlobalMemoryStatusEx failed: error code "
Chris@1356 163 << GetLastError() << endl;
Chris@223 164 return;
Chris@223 165 }
Chris@223 166 wavail = lms.ullAvailPhys;
Chris@223 167 wtotal = lms.ullTotalPhys;
Chris@223 168
Chris@223 169 } else {
Chris@223 170
Chris@223 171 /* Fall back to GlobalMemoryStatus which is always available.
Chris@223 172 but returns wrong results for physical memory > 4GB */
Chris@223 173
Chris@1356 174 MEMORYSTATUS ms;
Chris@1356 175 GlobalMemoryStatus(&ms);
Chris@1356 176 wavail = ms.dwAvailPhys;
Chris@223 177 wtotal = ms.dwTotalPhys;
Chris@223 178 }
Chris@223 179
Chris@223 180 DWORDLONG size = wavail / 1048576;
Chris@223 181 if (size > INT_MAX) size = INT_MAX;
Chris@1038 182 available = ssize_t(size);
Chris@223 183
Chris@223 184 size = wtotal / 1048576;
Chris@223 185 if (size > INT_MAX) size = INT_MAX;
Chris@1038 186 total = ssize_t(size);
Chris@223 187
Chris@223 188 return;
Chris@223 189
Chris@223 190 #else
Chris@223 191 #ifdef __APPLE__
Chris@223 192
Chris@1779 193 unsigned int val32;
Chris@1779 194 int64_t val64;
Chris@223 195 int mib[2];
Chris@223 196 size_t size_sys;
Chris@223 197
Chris@223 198 mib[0] = CTL_HW;
Chris@223 199
Chris@1779 200 mib[1] = HW_MEMSIZE;
Chris@1779 201 size_sys = sizeof(val64);
Chris@1779 202 sysctl(mib, 2, &val64, &size_sys, NULL, 0);
Chris@1779 203 if (val64) total = val64 / 1048576;
Chris@223 204
Chris@223 205 mib[1] = HW_USERMEM;
Chris@1779 206 size_sys = sizeof(val32);
Chris@1779 207 sysctl(mib, 2, &val32, &size_sys, NULL, 0);
Chris@1779 208 if (val32) available = val32 / 1048576;
Chris@1779 209
Chris@1779 210 // The newer memsize sysctl returns a 64-bit value, but usermem is
Chris@1779 211 // an old 32-bit value that doesn't seem to have an updated
Chris@1779 212 // alternative (?) - so it can't return more than 2G. In practice
Chris@1779 213 // it seems to return values far lower than that, even where more
Chris@1779 214 // than 2G of real memory is free. So we can't actually tell when
Chris@1779 215 // we're getting low on memory at all. Most of the time I think we
Chris@1779 216 // just need to use an arbitrary value like this one.
Chris@1779 217 if (available < total/4) {
Chris@1779 218 available = total/4;
Chris@1779 219 }
Chris@223 220
Chris@223 221 return;
Chris@223 222
Chris@223 223 #else
Chris@223 224
Chris@223 225 FILE *meminfo = fopen("/proc/meminfo", "r");
Chris@223 226 if (!meminfo) return;
Chris@223 227
Chris@223 228 char buf[256];
Chris@223 229 while (!feof(meminfo)) {
Chris@1363 230 if (!fgets(buf, 256, meminfo)) {
Chris@1424 231 fclose(meminfo);
Chris@1363 232 return;
Chris@1363 233 }
Chris@223 234 bool isMemFree = (strncmp(buf, "MemFree:", 8) == 0);
Chris@223 235 bool isMemTotal = (!isMemFree && (strncmp(buf, "MemTotal:", 9) == 0));
Chris@223 236 if (isMemFree || isMemTotal) {
Chris@223 237 QString line = QString(buf).trimmed();
Chris@223 238 QStringList elements = line.split(' ', QString::SkipEmptyParts);
Chris@223 239 QString unit = "kB";
Chris@223 240 if (elements.size() > 2) unit = elements[2];
Chris@223 241 int size = elements[1].toInt();
Chris@843 242 // cerr << "have size \"" << size << "\", unit \""
Chris@843 243 // << unit << "\"" << endl;
Chris@223 244 if (unit.toLower() == "gb") size = size * 1024;
Chris@223 245 else if (unit.toLower() == "mb") size = size;
Chris@223 246 else if (unit.toLower() == "kb") size = size / 1024;
Chris@223 247 else size = size / 1048576;
Chris@223 248
Chris@223 249 if (isMemFree) available = size;
Chris@223 250 else total = size;
Chris@223 251 }
Chris@223 252 if (available != -1 && total != -1) {
Chris@223 253 fclose(meminfo);
Chris@223 254 return;
Chris@223 255 }
Chris@223 256 }
Chris@223 257 fclose(meminfo);
Chris@223 258
Chris@223 259 return;
Chris@223 260
Chris@223 261 #endif
Chris@223 262 #endif
Chris@223 263 }
Chris@223 264
Chris@1038 265 ssize_t
Chris@223 266 GetDiscSpaceMBAvailable(const char *path)
Chris@223 267 {
Chris@223 268 #ifdef _WIN32
Chris@223 269 ULARGE_INTEGER available, total, totalFree;
Chris@223 270 if (GetDiskFreeSpaceExA(path, &available, &total, &totalFree)) {
Chris@1356 271 __int64 a = available.QuadPart;
Chris@223 272 a /= 1048576;
Chris@223 273 if (a > INT_MAX) a = INT_MAX;
Chris@1038 274 return ssize_t(a);
Chris@223 275 } else {
Chris@843 276 cerr << "WARNING: GetDiskFreeSpaceEx failed: error code "
Chris@1356 277 << GetLastError() << endl;
Chris@223 278 return -1;
Chris@223 279 }
Chris@223 280 #else
Chris@223 281 struct statvfs buf;
Chris@223 282 if (!statvfs(path, &buf)) {
Chris@223 283 // do the multiplies and divides in this order to reduce the
Chris@223 284 // likelihood of arithmetic overflow
Chris@843 285 // cerr << "statvfs(" << path << ") says available: " << buf.f_bavail << ", block size: " << buf.f_bsize << endl;
Chris@223 286 uint64_t available = ((buf.f_bavail / 1024) * buf.f_bsize) / 1024;
Chris@223 287 if (available > INT_MAX) available = INT_MAX;
Chris@1038 288 return ssize_t(available);
Chris@223 289 } else {
Chris@223 290 perror("statvfs failed");
Chris@223 291 return -1;
Chris@223 292 }
Chris@223 293 #endif
Chris@223 294 }
Chris@303 295
Chris@835 296 #ifdef _WIN32
Chris@835 297 extern void SystemMemoryBarrier()
Chris@835 298 {
Chris@1218 299 #ifdef _MSC_VER
Chris@835 300 MemoryBarrier();
Chris@835 301 #else /* mingw */
Chris@835 302 LONG Barrier = 0;
Chris@835 303 __asm__ __volatile__("xchgl %%eax,%0 "
Chris@835 304 : "=r" (Barrier));
Chris@835 305 #endif
Chris@835 306 }
Chris@835 307 #else /* !_WIN32 */
Chris@1357 308 #if !defined(__APPLE__) && defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ == 0))
Chris@835 309 void
Chris@835 310 SystemMemoryBarrier()
Chris@835 311 {
Chris@835 312 pthread_mutex_t dummy = PTHREAD_MUTEX_INITIALIZER;
Chris@835 313 pthread_mutex_lock(&dummy);
Chris@835 314 pthread_mutex_unlock(&dummy);
Chris@835 315 }
Chris@835 316 #endif /* !defined(__APPLE__) etc */
Chris@835 317 #endif /* !_WIN32 */
Chris@835 318
Chris@835 319
Chris@1582 320 static char *startupLocale = nullptr;
Chris@303 321
Chris@303 322 void
Chris@303 323 StoreStartupLocale()
Chris@303 324 {
Chris@1582 325 char *loc = setlocale(LC_ALL, nullptr);
Chris@303 326 if (!loc) return;
Chris@303 327 if (startupLocale) free(startupLocale);
Chris@303 328 startupLocale = strdup(loc);
Chris@303 329 }
Chris@303 330
Chris@303 331 void
Chris@303 332 RestoreStartupLocale()
Chris@303 333 {
Chris@303 334 if (!startupLocale) {
Chris@303 335 setlocale(LC_ALL, "");
Chris@303 336 } else {
Chris@303 337 setlocale(LC_ALL, startupLocale);
Chris@303 338 }
Chris@303 339 }
Chris@223 340
Chris@1818 341 bool
Chris@1819 342 OSReportsDarkThemeActive()
Chris@1818 343 {
Chris@1819 344 SVCERR << "OSReportsDarkThemeActive() called" << endl;
Chris@1818 345 #ifdef _MSC_VER
Chris@1818 346 using namespace winrt::Windows::UI::ViewManagement;
Chris@1818 347 UISettings settings;
Chris@1818 348 auto background = settings.GetColorValue(UIColorType::Background);
Chris@1818 349 if (int(background.R) + int(background.G) + int(background.B) < 384) {
Chris@1818 350 return true;
Chris@1818 351 }
Chris@1818 352 #endif
Chris@1818 353 return false;
Chris@1818 354 }
Chris@1818 355
Chris@1820 356 bool
Chris@1820 357 OSQueryAccentColour(int &r, int &g, int &b)
Chris@1820 358 {
Chris@1820 359 SVCERR << "OSQueryAccentColour() called" << endl;
Chris@1820 360 #ifdef _MSC_VER
Chris@1820 361 using namespace winrt::Windows::UI::ViewManagement;
Chris@1821 362 bool dark = OSReportsDarkThemeActive();
Chris@1820 363 UISettings settings;
Chris@1821 364 auto accent = settings.GetColorValue
Chris@1821 365 (dark ? UIColorType::AccentLight1 : UIColorType::Accent);
Chris@1821 366 r = accent.R;
Chris@1821 367 g = accent.G;
Chris@1821 368 b = accent.B;
Chris@1820 369 return true;
Chris@1820 370 #endif
Chris@1820 371 return false;
Chris@1820 372 }
Chris@1820 373
Chris@223 374 double mod(double x, double y) { return x - (y * floor(x / y)); }
Chris@223 375 float modf(float x, float y) { return x - (y * floorf(x / y)); }
Chris@223 376
Chris@223 377 double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; }
Chris@1039 378 float princargf(float a) { return float(princarg(a)); }
Chris@223 379
Chris@1475 380 bool
Chris@1475 381 getEnvUtf8(std::string variable, std::string &value)
Chris@1475 382 {
Chris@1475 383 value = "";
Chris@1475 384
Chris@1475 385 #ifdef _WIN32
Chris@1475 386 int wvarlen = MultiByteToWideChar(CP_UTF8, 0,
Chris@1475 387 variable.c_str(), int(variable.length()),
Chris@1475 388 0, 0);
Chris@1475 389 if (wvarlen < 0) {
Chris@1475 390 SVCERR << "WARNING: Unable to convert environment variable name "
Chris@1475 391 << variable << " to wide characters" << endl;
Chris@1475 392 return false;
Chris@1475 393 }
Chris@1475 394
Chris@1475 395 wchar_t *wvarbuf = new wchar_t[wvarlen + 1];
Chris@1475 396 (void)MultiByteToWideChar(CP_UTF8, 0,
Chris@1475 397 variable.c_str(), int(variable.length()),
Chris@1475 398 wvarbuf, wvarlen);
Chris@1475 399 wvarbuf[wvarlen] = L'\0';
Chris@1475 400
Chris@1475 401 wchar_t *wvalue = _wgetenv(wvarbuf);
Chris@1176 402
Chris@1475 403 delete[] wvarbuf;
Chris@1475 404
Chris@1475 405 if (!wvalue) {
Chris@1475 406 return false;
Chris@1475 407 }
Chris@1475 408
Chris@1480 409 int wvallen = int(wcslen(wvalue));
Chris@1475 410 int vallen = WideCharToMultiByte(CP_UTF8, 0,
Chris@1480 411 wvalue, wvallen,
Chris@1475 412 0, 0, 0, 0);
Chris@1475 413 if (vallen < 0) {
Chris@1475 414 SVCERR << "WARNING: Unable to convert environment value to UTF-8"
Chris@1475 415 << endl;
Chris@1475 416 return false;
Chris@1475 417 }
Chris@1475 418
Chris@1475 419 char *val = new char[vallen + 1];
Chris@1475 420 (void)WideCharToMultiByte(CP_UTF8, 0,
Chris@1480 421 wvalue, wvallen,
Chris@1475 422 val, vallen, 0, 0);
Chris@1475 423 val[vallen] = '\0';
Chris@1475 424
Chris@1475 425 value = val;
Chris@1475 426
Chris@1475 427 delete[] val;
Chris@1475 428 return true;
Chris@1475 429
Chris@1475 430 #else
Chris@1475 431
Chris@1475 432 char *val = getenv(variable.c_str());
Chris@1475 433 if (!val) {
Chris@1475 434 return false;
Chris@1475 435 }
Chris@1475 436
Chris@1475 437 value = val;
Chris@1475 438 return true;
Chris@1475 439
Chris@1475 440 #endif
Chris@1475 441 }
Chris@1475 442
Chris@1475 443 bool
Chris@1475 444 putEnvUtf8(std::string variable, std::string value)
Chris@1475 445 {
Chris@1477 446 #ifdef _WIN32
Chris@1475 447 std::string entry = variable + "=" + value;
Chris@1475 448
Chris@1475 449 int wentlen = MultiByteToWideChar(CP_UTF8, 0,
Chris@1475 450 entry.c_str(), int(entry.length()),
Chris@1475 451 0, 0);
Chris@1475 452 if (wentlen < 0) {
Chris@1475 453 SVCERR << "WARNING: Unable to convert environment entry to "
Chris@1475 454 << "wide characters" << endl;
Chris@1475 455 return false;
Chris@1475 456 }
Chris@1475 457
Chris@1475 458 wchar_t *wentbuf = new wchar_t[wentlen + 1];
Chris@1475 459 (void)MultiByteToWideChar(CP_UTF8, 0,
Chris@1475 460 entry.c_str(), int(entry.length()),
Chris@1475 461 wentbuf, wentlen);
Chris@1475 462 wentbuf[wentlen] = L'\0';
Chris@1475 463
Chris@1475 464 int rv = _wputenv(wentbuf);
Chris@1475 465
Chris@1475 466 delete[] wentbuf;
Chris@1475 467
Chris@1475 468 if (rv != 0) {
Chris@1475 469 SVCERR << "WARNING: Failed to set environment entry" << endl;
Chris@1475 470 return false;
Chris@1475 471 }
Chris@1475 472 return true;
Chris@1475 473
Chris@1475 474 #else
Chris@1475 475
Chris@1477 476 int rv = setenv(variable.c_str(), value.c_str(), 1);
Chris@1475 477 if (rv != 0) {
Chris@1475 478 SVCERR << "WARNING: Failed to set environment entry" << endl;
Chris@1475 479 return false;
Chris@1475 480 }
Chris@1475 481 return true;
Chris@1475 482
Chris@1475 483 #endif
Chris@1475 484 }
Chris@1475 485