30#include <malloc/malloc.h>
31#include <mach/vm_map.h>
36#include "androidUTIL.h"
56#include <sys/sysinfo.h>
65#include <wx/apptrait.h>
67#include <wx/filename.h>
68#include <wx/platinfo.h>
69#include <wx/stdpaths.h>
70#include <wx/textfile.h>
71#include <wx/tokenzr.h>
88static const char PATH_SEP =
';';
90static const char PATH_SEP =
':';
93bool AbstractPlatform::m_isBusy =
false;
95static const char*
const DEFAULT_XDG_DATA_DIRS =
96 "~/.local/share:/usr/local/share:/usr/share";
98void appendOSDirSlash(wxString* pString);
106static inline bool IsWindows() {
107 return wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS;
110static bool checkIfFlatpacked() {
112 if (!wxGetEnv(
"FLATPAK_ID", &
id)) {
115 return id ==
"org.opencpn.OpenCPN";
120static wxString GetLinuxDataPath() {
122 if (wxGetEnv(
"XDG_DATA_DIRS", &dirs)) {
123 dirs = wxString(
"~/.local/share:") + dirs;
125 dirs = DEFAULT_XDG_DATA_DIRS;
128 wxStringTokenizer tokens(dirs,
':');
129 while (tokens.HasMoreTokens()) {
130 wxString dir = tokens.GetNextToken();
131 if (dir.EndsWith(
"/")) {
132 dir = dir.SubString(0, dir.length() - 1);
134 if (!dir.EndsWith(
"/opencpn/plugins")) {
135 dir +=
"/opencpn/plugins";
137 s += dir + (tokens.HasMoreTokens() ?
";" :
"");
143 wxStringTokenizer tokens(paths,
';');
145 while (tokens.HasMoreTokens()) {
146 wxFileName filename(tokens.GetNextToken());
147 filename.Normalize();
148 s += platform->NormalizePath(filename.GetFullPath());
149 if (tokens.HasMoreTokens()) {
157BasePlatform::BasePlatform() {
159 m_isFlatpacked = checkIfFlatpacked();
161 DetectOSDetail(m_osDetail);
170BasePlatform::~BasePlatform() {
172 delete wxLog::SetActiveTarget(
new wxLogStderr());
179wxStandardPaths& AbstractPlatform::GetStdPaths() {
181 return wxStandardPaths::Get();
183 return *
dynamic_cast<wxStandardPaths*
>(
184 &(wxTheApp->GetTraits())->GetStandardPaths());
188wxString AbstractPlatform::NormalizePath(
const wxString& full_path) {
192 wxString path(full_path);
196 path = f.GetFullPath();
202wxString& AbstractPlatform::GetHomeDir() {
203 if (m_homeDir.IsEmpty()) {
205 wxStandardPaths& std_path = GetStdPaths();
210 std_path.SetInstallPrefix(wxString(PREFIX, wxConvUTF8));
218 m_homeDir = std_path.GetUserConfigDir();
222 m_homeDir = androidGetHomeDir();
226 wxFileName path(GetExePath());
227 m_homeDir = path.GetPath();
231 appendOSDirSlash(&m_homeDir);
232 m_homeDir.Append(
"opencpn");
235 appendOSDirSlash(&m_homeDir);
241wxString& AbstractPlatform::GetExePath() {
242 if (m_exePath.IsEmpty()) {
243 wxStandardPaths& std_path = GetStdPaths();
244 m_exePath = std_path.GetExecutablePath();
250wxString* AbstractPlatform::GetSharedDataDirPtr() {
251 if (m_SData_Dir.IsEmpty()) GetSharedDataDir();
257 return &m_PrivateDataDir;
260wxString& AbstractPlatform::GetSharedDataDir() {
261 if (m_SData_Dir.IsEmpty()) {
272 wxStandardPaths& std_path = GetStdPaths();
273 m_SData_Dir = std_path.GetDataDir();
274 appendOSDirSlash(&m_SData_Dir);
277 m_SData_Dir = androidGetSharedDir();
280 if (g_bportable) m_SData_Dir = GetHomeDir();
289 lic = androidGetSupplementalLicense();
295 static const wxString sep = wxFileName::GetPathSeparator();
298 wxLogMessage(
"PlugInManager: Using data dirs from: " + datadirs);
299 wxStringTokenizer dirs(datadirs,
";");
300 while (dirs.HasMoreTokens()) {
301 wxString dir = dirs.GetNextToken();
302 wxFileName tryDirName(dir);
304 if (!tryDir.Open(tryDirName.GetFullPath()))
continue;
306 bool more = tryDir.GetFirst(&next);
308 if (next == plugin_name) {
309 next = next.Prepend(tryDirName.GetFullPath() + sep);
310 wxLogMessage(
"PlugInManager: using data dir: %s", next);
313 more = tryDir.GetNext(&next);
317 wxLogMessage(
"Warning: no data directory found, using \"\"");
322 if (!m_PrivateDataDir.IsEmpty() && g_configdir.empty())
323 return m_PrivateDataDir;
324 if (!g_configdir.empty()) {
325 wxString path = g_configdir;
326 if (path.Last() == wxFileName::GetPathSeparator()) path.RemoveLast();
327 m_default_private_datadir = path;
328 return m_default_private_datadir;
335 if (m_PrivateDataDir.IsEmpty()) {
337 wxStandardPaths& std_path = GetStdPaths();
343 std::string config_home;
344 if (getenv(
"XDG_CONFIG_HOME")) {
345 config_home = getenv(
"XDG_CONFIG_HOME");
347 config_home = getenv(
"HOME");
348 config_home +=
"/.var/app/org.opencpn.OpenCPN/config";
350 m_PrivateDataDir = config_home +
"/opencpn";
352#elif defined __WXOSX__
354 std_path.GetUserConfigDir();
355 appendOSDirSlash(&m_PrivateDataDir);
356 m_PrivateDataDir.Append(
"opencpn");
358 m_PrivateDataDir = std_path.GetUserDataDir();
361 if (g_bportable) m_PrivateDataDir = GetHomeDir();
362 if (m_PrivateDataDir.Last() == wxFileName::GetPathSeparator())
363 m_PrivateDataDir.RemoveLast();
366 m_PrivateDataDir = androidGetPrivateDir();
369 return m_PrivateDataDir;
374 wxLogMessage(
"winPluginDir: Using value from ini file.");
376 if (!fn.DirExists()) {
377 wxLogWarning(
"Plugin dir %s does not exist",
378 fn.GetFullPath().mb_str().data());
381 return fn.GetFullPath();
383 wxString winPluginDir;
386 winPluginDir = (GetHomeDir() +
"plugins");
388 wxLogMessage(
"Using portable plugin dir: %s", winPluginDir);
393 bool ok = wxGetEnv(
"LOCALAPPDATA", &winPluginDir);
395 wxLogMessage(
"winPluginDir: Cannot lookup LOCALAPPDATA");
397 std::string path(wxGetHomeDir().ToStdString());
398 path +=
"\\AppData\\Local";
400 winPluginDir = wxString(path.c_str());
401 wxLogMessage(
"winPluginDir: using %s", winPluginDir.mb_str().data());
407 ok = wxGetEnv(
"APPDATA", &winPluginDir);
411 wxLogMessage(
"winPluginDir: Cannot lookup APPDATA");
412 std::string path(wxGetHomeDir().ToStdString());
413 path +=
"\\AppData\\Roaming";
415 winPluginDir = wxString(path.c_str());
417 wxLogMessage(
"winPluginDir: using %s", winPluginDir.mb_str().data());
422 winPluginDir = GetHomeDir();
424 wxFileName path(winPluginDir);
426 winPluginDir = path.GetFullPath() +
"\\opencpn\\plugins";
427 wxLogMessage(
"Using private plugin dir: %s", winPluginDir);
432 if (m_PluginsDir.IsEmpty()) {
433 wxStandardPaths& std_path = GetStdPaths();
436 m_PluginsDir = std_path.GetPluginsDir();
439 m_PluginsDir +=
"\\plugins";
442 m_PluginsDir = GetHomeDir();
443 m_PluginsDir +=
"plugins";
448 wxFileName fdir = wxFileName::DirName(std_path.GetUserConfigDir());
449 fdir.RemoveLastDir();
450 m_PluginsDir = fdir.GetPath();
456wxString* AbstractPlatform::GetPluginDirPtr() {
458 return &m_PluginsDir;
461bool AbstractPlatform::isPlatformCapable(
int flag) {
465 if (flag == PLATFORM_CAP_PLUGINS) {
467 wxString tsdk(android_plat_spc.msdk);
468 if (tsdk.ToLong(&platver)) {
469 if (platver >= 11)
return true;
471 }
else if (flag == PLATFORM_CAP_FASTPAN) {
473 wxString tsdk(android_plat_spc.msdk);
474 if (tsdk.ToLong(&platver)) {
475 if (platver >= 14)
return true;
483void appendOSDirSlash(wxString* pString) {
484 wxChar sep = wxFileName::GetPathSeparator();
485 if (pString->Last() != sep) pString->Append(sep);
488wxString AbstractPlatform::GetWritableDocumentsDir() {
492 dir = androidGetExtStorageDir();
494 wxStandardPaths& std_path = GetStdPaths();
495 dir = std_path.GetDocumentsDir();
500bool AbstractPlatform::DetectOSDetail(
OCPN_OSDetail* detail) {
501 if (!detail)
return false;
504 detail->osd_name = std::string(PKG_TARGET);
505 detail->osd_version = std::string(PKG_TARGET_VERSION);
509 if (wxFileExists(
"/etc/os-release")) {
510 wxTextFile release_file(
"/etc/os-release");
511 if (release_file.Open()) {
513 for (wxString str = release_file.GetFirstLine(); !release_file.Eof();
514 str = release_file.GetNextLine()) {
515 if (str.StartsWith(
"NAME")) {
516 val = str.AfterFirst(
'=').Mid(1);
517 val = val.Mid(0, val.Length() - 1);
518 if (val.Length()) detail->osd_name = std::string(val.mb_str());
519 }
else if (str.StartsWith(
"VERSION_ID")) {
520 val = str.AfterFirst(
'=').Mid(1);
521 val = val.Mid(0, val.Length() - 1);
522 if (val.Length()) detail->osd_version = std::string(val.mb_str());
523 }
else if (str.StartsWith(
"ID=")) {
524 val = str.AfterFirst(
'=');
525 if (val.Length()) detail->osd_ID =
ocpn::split(val.mb_str(),
" ")[0];
526 }
else if (str.StartsWith(
"ID_LIKE")) {
527 if (val.StartsWith(
'"')) {
528 val = str.AfterFirst(
'=').Mid(1);
529 val = val.Mid(0, val.Length() - 1);
531 val = str.AfterFirst(
'=');
535 detail->osd_names_like =
ocpn::split(val.mb_str(),
" ");
540 release_file.Close();
542 if (detail->osd_name ==
"Linux Mint") {
543 if (wxFileExists(
"/etc/upstream-release/lsb-release")) {
544 wxTextFile upstream_release_file(
"/etc/upstream-release/lsb-release");
545 if (upstream_release_file.Open()) {
547 for (wxString str = upstream_release_file.GetFirstLine();
548 !upstream_release_file.Eof();
549 str = upstream_release_file.GetNextLine()) {
550 if (str.StartsWith(
"DISTRIB_RELEASE")) {
551 val = str.AfterFirst(
'=').Mid(0);
552 val = val.Mid(0, val.Length());
553 if (val.Length()) detail->osd_version = std::string(val.mb_str());
556 upstream_release_file.Close();
564 detail->osd_arch = std::string(
"x86_64");
567 wxPlatformInfo platformInfo = wxPlatformInfo::Get();
568 wxArchitecture arch = platformInfo.GetArchitecture();
569 if (arch == wxARCH_32) detail->osd_arch = std::string(
"i386");
575 detail->osd_arch = std::string(
"arm64");
577 detail->osd_arch = std::string(
"armhf");
582 detail->osd_arch = std::string(
"arm64");
583 if (arch == wxARCH_32) detail->osd_arch = std::string(
"armhf");
587 if (IsAppleSilicon() == 1) {
588 if (ProcessIsTranslated() != 1) {
589 detail->osd_arch = std::string(
"arm64");
591 detail->osd_arch = std::string(
"x86_64");
594 detail->osd_arch = std::string(
"x86_64");
601wxString& AbstractPlatform::GetConfigFileName() {
602 if (m_config_file_name.IsEmpty()) {
604 wxStandardPaths& std_path = GetStdPaths();
607 m_config_file_name =
"opencpn.ini";
608 m_config_file_name.Prepend(GetHomeDir());
610#elif defined __WXOSX__
612 std_path.GetUserConfigDir();
613 appendOSDirSlash(&m_config_file_name);
614 m_config_file_name.Append(
"opencpn");
615 appendOSDirSlash(&m_config_file_name);
616 m_config_file_name.Append(
"opencpn.ini");
619 m_config_file_name.Append(
"/opencpn.conf");
622 m_config_file_name = std_path.GetUserDataDir();
623 appendOSDirSlash(&m_config_file_name);
624 m_config_file_name.Append(
"opencpn.conf");
628 m_config_file_name = GetHomeDir();
630 m_config_file_name +=
"opencpn.ini";
631#elif defined __WXOSX__
632 m_config_file_name +=
"opencpn.ini";
634 m_config_file_name +=
"opencpn.conf";
639 m_config_file_name = androidGetPrivateDir();
640 appendOSDirSlash(&m_config_file_name);
641 m_config_file_name +=
"opencpn.conf";
643 if (!g_configdir.empty()) {
644 m_config_file_name = g_configdir;
645 m_config_file_name.Append(
"/opencpn.conf");
648 return m_config_file_name;
651bool BasePlatform::InitializeLogFile() {
654 appendOSDirSlash(&mlog_file);
658 wxFileName LibPref(mlog_file);
659 LibPref.RemoveLastDir();
660 LibPref.RemoveLastDir();
662 mlog_file = LibPref.GetFullPath();
663 appendOSDirSlash(&mlog_file);
665 mlog_file.Append(
"Logs/");
671 wxFileName wxHomeFiledir(GetHomeDir());
672 if (
true != wxHomeFiledir.DirExists(wxHomeFiledir.GetPath()))
673 if (!wxHomeFiledir.Mkdir(wxHomeFiledir.GetPath())) {
674 wxASSERT_MSG(
false,
"Cannot create opencpn home directory");
679 wxFileName wxLogFiledir(mlog_file);
680 if (
true != wxLogFiledir.DirExists(wxLogFiledir.GetPath())) {
681 if (!wxLogFiledir.Mkdir(wxLogFiledir.GetPath())) {
682 wxASSERT_MSG(
false,
"Cannot create opencpn log directory");
687 mlog_file.Append(
"opencpn.log");
688 wxString logit = mlog_file;
691 wxCharBuffer abuf = mlog_file.ToUTF8();
692 qDebug() <<
"logfile " << abuf.data();
696 if (::wxFileExists(mlog_file)) {
697 if (wxFileName::GetSize(mlog_file) > 1000000) {
698 wxString oldlog = mlog_file;
699 oldlog.Append(
".log");
702 large_log_message = (
"Old log will be moved to opencpn.log.log");
703 ::wxRenameFile(mlog_file, oldlog);
707 if (::wxFileExists(mlog_file)) {
710 ::wxRemoveFile(mlog_file);
714 if (wxLog::GetLogLevel() > wxLOG_User) wxLog::SetLogLevel(wxLOG_Info);
716 auto logger =
new OcpnLog(mlog_file.mb_str());
720 m_old_logger = wxLog::SetActiveTarget(logger);
725void AbstractPlatform::CloseLogFile() {
727 delete wxLog::SetActiveTarget(m_old_logger);
728 m_old_logger =
nullptr;
734 wxString sep = wxFileName::GetPathSeparator();
739 if (m_pluginDataPath !=
"") {
740 return m_pluginDataPath;
747 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
748 if (isFlatpacked()) {
749 dirs =
"~/.var/app/org.opencpn.OpenCPN/data/opencpn/plugins";
750 }
else if (osSystemId & wxOS_UNIX_LINUX) {
751 dirs = GetLinuxDataPath();
752 }
else if (osSystemId & wxOS_WINDOWS) {
754 }
else if (osSystemId & wxOS_MAC) {
755 dirs =
"/Applications/OpenCPN.app/Contents/SharedSupport/plugins;";
757 "~/Library/Application Support/OpenCPN/Contents/SharedSupport/plugins";
761 m_pluginDataPath = ExpandPaths(dirs,
this);
762 if (m_pluginDataPath !=
"") {
763 m_pluginDataPath +=
";";
766 if (m_pluginDataPath.EndsWith(wxFileName::GetPathSeparator())) {
767 m_pluginDataPath.RemoveLast();
769 wxLogMessage(
"Using plugin data path: %s", m_pluginDataPath.mb_str().data());
770 return m_pluginDataPath;
774void AbstractPlatform::ShowBusySpinner() {
776 androidShowBusyIcon();
781void AbstractPlatform::ShowBusySpinner() {
783 ::wxBeginBusyCursor();
790void AbstractPlatform::HideBusySpinner() {
792 androidHideBusyIcon();
797void AbstractPlatform::HideBusySpinner() {
807#if defined(__ANDROID__)
808wxSize BasePlatform::getDisplaySize() {
return getAndroidDisplayDimensions(); }
811wxSize BasePlatform::getDisplaySize() {
return wxSize(0, 0); }
815double BasePlatform::GetDisplaySizeMM() {
816 if (m_displaySizeMMOverride.size() > 0 && m_displaySizeMMOverride[0] > 0) {
817 return m_displaySizeMMOverride[0];
822 ret = GetAndroidDisplaySize();
825 wxLogDebug(
"Detected display size (horizontal): %d mm", (
int)ret);
829#if defined(__ANDROID__)
830double BasePlatform::GetDisplayDPmm() {
return getAndroidDPmm(); }
833double BasePlatform::GetDisplayDPmm() {
834 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
835 double r = getDisplaySize().x;
836 return r / GetDisplaySizeMM();
847 if (win) rv = (double)(win->ToDIP(100)) / 100.;
852unsigned int AbstractPlatform::GetSelectRadiusPix() {
853 return GetDisplayDPmm() *
854 (g_btouch ? g_selection_radius_touch_mm : g_selection_radius_mm);
861const GUID GUID_CLASS_MONITOR = {0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08,
862 0x00, 0x2b, 0xe1, 0x03, 0x18};
865bool GetMonitorSizeFromEDID(
const HKEY hDevRegKey,
int* WidthMm,
867 DWORD dwType, AcutalValueNameLength = NAME_SIZE;
868 TCHAR valueName[NAME_SIZE];
871 DWORD edidsize =
sizeof(EDIDdata);
873 for (LONG i = 0, retValue = ERROR_SUCCESS; retValue != ERROR_NO_MORE_ITEMS;
875 retValue = RegEnumValue(hDevRegKey, i, &valueName[0],
876 &AcutalValueNameLength, NULL, &dwType,
880 if (retValue != ERROR_SUCCESS || 0 != _tcscmp(valueName, L
"EDID"))
continue;
882 *WidthMm = ((EDIDdata[68] & 0xF0) << 4) + EDIDdata[66];
883 *HeightMm = ((EDIDdata[68] & 0x0F) << 8) + EDIDdata[67];
891bool GetSizeForDevID(wxString& TargetDevID,
int* WidthMm,
int* HeightMm) {
893 SetupDiGetClassDevsEx(&GUID_CLASS_MONITOR,
901 if (NULL == devInfo)
return false;
905 for (ULONG i = 0; ERROR_NO_MORE_ITEMS != GetLastError(); ++i) {
906 SP_DEVINFO_DATA devInfoData;
907 memset(&devInfoData, 0,
sizeof(devInfoData));
908 devInfoData.cbSize =
sizeof(devInfoData);
910 if (SetupDiEnumDeviceInfo(devInfo, i, &devInfoData)) {
911 wchar_t Instance[80];
912 SetupDiGetDeviceInstanceId(devInfo, &devInfoData, Instance, MAX_PATH,
914 wxString instance(Instance);
915 if (instance.Upper().Find(TargetDevID.Upper()) == wxNOT_FOUND)
continue;
917 HKEY hDevRegKey = SetupDiOpenDevRegKey(
918 devInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
920 if (!hDevRegKey || (hDevRegKey == INVALID_HANDLE_VALUE))
continue;
922 bRes = GetMonitorSizeFromEDID(hDevRegKey, WidthMm, HeightMm);
924 RegCloseKey(hDevRegKey);
927 SetupDiDestroyDeviceInfoList(devInfo);
931bool AbstractPlatform::GetWindowsMonitorSize(
int* width,
int* height) {
932 bool bFoundDevice =
true;
934 if (m_monitorWidth < 10) {
944 bFoundDevice =
false;
945 while (EnumDisplayDevices(0, dev, &dd, 0) && !bFoundDevice) {
946 DISPLAY_DEVICE ddMon;
947 ZeroMemory(&ddMon,
sizeof(ddMon));
948 ddMon.cb =
sizeof(ddMon);
951 while (EnumDisplayDevices(dd.DeviceName, devMon, &ddMon, 0) &&
953 if (ddMon.StateFlags & DISPLAY_DEVICE_ACTIVE &&
954 !(ddMon.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)) {
955 DeviceID = wxString(ddMon.DeviceID, wxConvUTF8);
956 DeviceID = DeviceID.Mid(8);
957 DeviceID = DeviceID.Mid(0, DeviceID.Find(
'\\'));
959 bFoundDevice = GetSizeForDevID(DeviceID, &WidthMm, &HeightMm);
963 ZeroMemory(&ddMon,
sizeof(ddMon));
964 ddMon.cb =
sizeof(ddMon);
967 ZeroMemory(&dd,
sizeof(dd));
971 m_monitorWidth = WidthMm;
972 m_monitorHeight = HeightMm;
975 if (width) *width = m_monitorWidth;
976 if (height) *height = m_monitorHeight;
984 double size = w->GetCharHeight() * (IsWindows() ? 1.3 : 1.0);
985#if wxCHECK_VERSION(3, 1, 2)
988 size *=
static_cast<double>(w->ToDIP(100)) / 100.;
992 double pixel_per_mm = wxGetDisplaySize().x / GetDisplaySizeMM();
993 size = std::max(size, 7.0 * pixel_per_mm);
995 return std::round(size);
1000 return androidGetMemoryStatus(mem_total, mem_used);
1003#if defined(__linux__)
1007 struct sysinfo sys_info;
1008 if (sysinfo(&sys_info) != -1)
1009 *mem_total = ((uint64_t)sys_info.totalram * sys_info.mem_unit) / 1024;
1023 FILE* file = fopen(
"/proc/self/statm",
"r");
1025 if (fscanf(file,
"%d", mem_used) != 1) {
1026 wxLogWarning(
"Cannot parse /proc/self/statm (!)");
1039 PROCESS_MEMORY_COUNTERS pmc;
1041 unsigned long processID = wxGetProcessId();
1044 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
1047 if (hProcess && GetProcessMemoryInfo(hProcess, &pmc,
sizeof(pmc))) {
1065 *mem_used = pmc.WorkingSetSize / 1024;
1068 CloseHandle(hProcess);
1072 MEMORYSTATUSEX statex;
1074 statex.dwLength =
sizeof(statex);
1076 GlobalMemoryStatusEx(&statex);
1094 *mem_total = statex.ullTotalPhys / 1024;
1101 if (g_tick != g_lastMemTick) {
1102 malloc_zone_pressure_relief(NULL, 0);
1105 int blocksInUse = 0;
1106 int sizeAllocated = 0;
1108 malloc_statistics_t stats;
1109 stats.blocks_in_use = 0;
1110 stats.size_in_use = 0;
1111 stats.max_size_in_use = 0;
1112 stats.size_allocated = 0;
1113 malloc_zone_statistics(NULL, &stats);
1114 bytesInUse += stats.size_in_use;
1115 blocksInUse += stats.blocks_in_use;
1116 sizeAllocated += stats.size_allocated;
1118 g_memUsed = sizeAllocated >> 10;
1121 g_lastMemTick = g_tick;
1124 if (mem_used) *mem_used = g_memUsed;
1127 FILE* fpIn = popen(
"sysctl -n hw.memsize",
"r");
1129 double pagesUsed = 0.0, totalPages = 0.0;
1131 if (fgets(buf,
sizeof(buf), fpIn) != NULL) {
1132 *mem_total = atol(buf) >> 10;
1140 if (mem_used) *mem_used = 0;
1141 if (mem_total) *mem_total = 0;
Customized logger class appending to a file providing:
Global variables reflecting command line options and arguments.
wxString g_winPluginDir
Base plugin directory on Windows.
Global variables stored in configuration file.
Miscellaneous globals primarely used by gui layer, not persisted in configuration file.
Enhanced logging interface on top of wx/log.h.
MacOS hardware probing functions.
std::vector< std::string > split(const char *token_string, const std::string &delimiter)
Return vector of items in s separated by delimiter.
bool exists(const std::string &name)
PlugIn Object Definition/API.
Miscellaneous utilities, many of which string related.