annotate system/System.cpp @ 1851:91056142abd0

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