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 if (getenv(
"OCPN_TEST_HOMEDIR"))
359 m_PrivateDataDir = getenv(
"OCPN_TEST_HOMEDIR");
361 m_PrivateDataDir = std_path.GetUserDataDir();
364 if (g_bportable) m_PrivateDataDir = GetHomeDir();
365 if (m_PrivateDataDir.Last() == wxFileName::GetPathSeparator())
366 m_PrivateDataDir.RemoveLast();
369 m_PrivateDataDir = androidGetPrivateDir();
372 return m_PrivateDataDir;
377 wxLogMessage(
"winPluginDir: Using value from ini file.");
379 if (!fn.DirExists()) {
380 wxLogWarning(
"Plugin dir %s does not exist",
381 fn.GetFullPath().mb_str().data());
384 return fn.GetFullPath();
386 wxString winPluginDir;
389 winPluginDir = (GetHomeDir() +
"plugins");
391 wxLogMessage(
"Using portable plugin dir: %s", winPluginDir);
396 bool ok = wxGetEnv(
"LOCALAPPDATA", &winPluginDir);
398 wxLogMessage(
"winPluginDir: Cannot lookup LOCALAPPDATA");
400 std::string path(wxGetHomeDir().ToStdString());
401 path +=
"\\AppData\\Local";
403 winPluginDir = wxString(path.c_str());
404 wxLogMessage(
"winPluginDir: using %s", winPluginDir.mb_str().data());
410 ok = wxGetEnv(
"APPDATA", &winPluginDir);
414 wxLogMessage(
"winPluginDir: Cannot lookup APPDATA");
415 std::string path(wxGetHomeDir().ToStdString());
416 path +=
"\\AppData\\Roaming";
418 winPluginDir = wxString(path.c_str());
420 wxLogMessage(
"winPluginDir: using %s", winPluginDir.mb_str().data());
425 winPluginDir = GetHomeDir();
427 wxFileName path(winPluginDir);
429 winPluginDir = path.GetFullPath() +
"\\opencpn\\plugins";
430 wxLogMessage(
"Using private plugin dir: %s", winPluginDir);
435 if (m_PluginsDir.IsEmpty()) {
436 wxStandardPaths& std_path = GetStdPaths();
439 m_PluginsDir = std_path.GetPluginsDir();
442 m_PluginsDir +=
"\\plugins";
445 m_PluginsDir = GetHomeDir();
446 m_PluginsDir +=
"plugins";
451 wxFileName fdir = wxFileName::DirName(std_path.GetUserConfigDir());
452 fdir.RemoveLastDir();
453 m_PluginsDir = fdir.GetPath();
459wxString* AbstractPlatform::GetPluginDirPtr() {
461 return &m_PluginsDir;
464bool AbstractPlatform::isPlatformCapable(
int flag) {
468 if (flag == PLATFORM_CAP_PLUGINS) {
470 wxString tsdk(android_plat_spc.msdk);
471 if (tsdk.ToLong(&platver)) {
472 if (platver >= 11)
return true;
474 }
else if (flag == PLATFORM_CAP_FASTPAN) {
476 wxString tsdk(android_plat_spc.msdk);
477 if (tsdk.ToLong(&platver)) {
478 if (platver >= 14)
return true;
486void appendOSDirSlash(wxString* pString) {
487 wxChar sep = wxFileName::GetPathSeparator();
488 if (pString->Last() != sep) pString->Append(sep);
491wxString AbstractPlatform::GetWritableDocumentsDir() {
495 dir = androidGetExtStorageDir();
497 wxStandardPaths& std_path = GetStdPaths();
498 dir = std_path.GetDocumentsDir();
503bool AbstractPlatform::DetectOSDetail(
OCPN_OSDetail* detail) {
504 if (!detail)
return false;
507 detail->osd_name = std::string(PKG_TARGET);
508 detail->osd_version = std::string(PKG_TARGET_VERSION);
512 if (wxFileExists(
"/etc/os-release")) {
513 wxTextFile release_file(
"/etc/os-release");
514 if (release_file.Open()) {
516 for (wxString str = release_file.GetFirstLine(); !release_file.Eof();
517 str = release_file.GetNextLine()) {
518 if (str.StartsWith(
"NAME")) {
519 val = str.AfterFirst(
'=').Mid(1);
520 val = val.Mid(0, val.Length() - 1);
521 if (val.Length()) detail->osd_name = std::string(val.mb_str());
522 }
else if (str.StartsWith(
"VERSION_ID")) {
523 val = str.AfterFirst(
'=').Mid(1);
524 val = val.Mid(0, val.Length() - 1);
525 if (val.Length()) detail->osd_version = std::string(val.mb_str());
526 }
else if (str.StartsWith(
"ID=")) {
527 val = str.AfterFirst(
'=');
528 if (val.Length()) detail->osd_ID =
ocpn::split(val.mb_str(),
" ")[0];
529 }
else if (str.StartsWith(
"ID_LIKE")) {
530 if (val.StartsWith(
'"')) {
531 val = str.AfterFirst(
'=').Mid(1);
532 val = val.Mid(0, val.Length() - 1);
534 val = str.AfterFirst(
'=');
538 detail->osd_names_like =
ocpn::split(val.mb_str(),
" ");
543 release_file.Close();
545 if (detail->osd_name ==
"Linux Mint") {
546 if (wxFileExists(
"/etc/upstream-release/lsb-release")) {
547 wxTextFile upstream_release_file(
"/etc/upstream-release/lsb-release");
548 if (upstream_release_file.Open()) {
550 for (wxString str = upstream_release_file.GetFirstLine();
551 !upstream_release_file.Eof();
552 str = upstream_release_file.GetNextLine()) {
553 if (str.StartsWith(
"DISTRIB_RELEASE")) {
554 val = str.AfterFirst(
'=').Mid(0);
555 val = val.Mid(0, val.Length());
556 if (val.Length()) detail->osd_version = std::string(val.mb_str());
559 upstream_release_file.Close();
567 detail->osd_arch = std::string(
"x86_64");
570 wxPlatformInfo platformInfo = wxPlatformInfo::Get();
571 wxArchitecture arch = platformInfo.GetArchitecture();
572 if (arch == wxARCH_32) detail->osd_arch = std::string(
"i386");
578 detail->osd_arch = std::string(
"arm64");
580 detail->osd_arch = std::string(
"armhf");
585 detail->osd_arch = std::string(
"arm64");
586 if (arch == wxARCH_32) detail->osd_arch = std::string(
"armhf");
590 if (IsAppleSilicon() == 1) {
591 if (ProcessIsTranslated() != 1) {
592 detail->osd_arch = std::string(
"arm64");
594 detail->osd_arch = std::string(
"x86_64");
597 detail->osd_arch = std::string(
"x86_64");
604wxString& AbstractPlatform::GetConfigFileName() {
605 if (m_config_file_name.IsEmpty()) {
607 wxStandardPaths& std_path = GetStdPaths();
610 m_config_file_name =
"opencpn.ini";
611 m_config_file_name.Prepend(GetHomeDir());
613#elif defined __WXOSX__
615 std_path.GetUserConfigDir();
616 appendOSDirSlash(&m_config_file_name);
617 m_config_file_name.Append(
"opencpn");
618 appendOSDirSlash(&m_config_file_name);
619 m_config_file_name.Append(
"opencpn.ini");
622 m_config_file_name.Append(
"/opencpn.conf");
625 m_config_file_name = std_path.GetUserDataDir();
626 appendOSDirSlash(&m_config_file_name);
627 m_config_file_name.Append(
"opencpn.conf");
631 m_config_file_name = GetHomeDir();
633 m_config_file_name +=
"opencpn.ini";
634#elif defined __WXOSX__
635 m_config_file_name +=
"opencpn.ini";
637 m_config_file_name +=
"opencpn.conf";
642 m_config_file_name = androidGetPrivateDir();
643 appendOSDirSlash(&m_config_file_name);
644 m_config_file_name +=
"opencpn.conf";
646 if (!g_configdir.empty()) {
647 m_config_file_name = g_configdir;
648 m_config_file_name.Append(
"/opencpn.conf");
651 return m_config_file_name;
654bool BasePlatform::InitializeLogFile() {
657 appendOSDirSlash(&mlog_file);
661 wxFileName LibPref(mlog_file);
662 LibPref.RemoveLastDir();
663 LibPref.RemoveLastDir();
665 mlog_file = LibPref.GetFullPath();
666 appendOSDirSlash(&mlog_file);
668 mlog_file.Append(
"Logs/");
674 wxFileName wxHomeFiledir(GetHomeDir());
675 if (
true != wxHomeFiledir.DirExists(wxHomeFiledir.GetPath()))
676 if (!wxHomeFiledir.Mkdir(wxHomeFiledir.GetPath())) {
677 wxASSERT_MSG(
false,
"Cannot create opencpn home directory");
682 wxFileName wxLogFiledir(mlog_file);
683 if (
true != wxLogFiledir.DirExists(wxLogFiledir.GetPath())) {
684 if (!wxLogFiledir.Mkdir(wxLogFiledir.GetPath())) {
685 wxASSERT_MSG(
false, wxString(
"Cannot create opencpn log directory: ") +
686 wxLogFiledir.GetPath());
691 mlog_file.Append(
"opencpn.log");
692 wxString logit = mlog_file;
695 wxCharBuffer abuf = mlog_file.ToUTF8();
696 qDebug() <<
"logfile " << abuf.data();
700 if (::wxFileExists(mlog_file)) {
701 if (wxFileName::GetSize(mlog_file) > 1000000) {
702 wxString oldlog = mlog_file;
703 oldlog.Append(
".log");
706 large_log_message = (
"Old log will be moved to opencpn.log.log");
707 ::wxRenameFile(mlog_file, oldlog);
711 if (::wxFileExists(mlog_file)) {
714 ::wxRemoveFile(mlog_file);
718 if (wxLog::GetLogLevel() > wxLOG_User) wxLog::SetLogLevel(wxLOG_Info);
720 auto logger =
new OcpnLog(mlog_file.mb_str());
724 m_old_logger = wxLog::SetActiveTarget(logger);
729void AbstractPlatform::CloseLogFile() {
731 delete wxLog::SetActiveTarget(m_old_logger);
732 m_old_logger =
nullptr;
738 wxString sep = wxFileName::GetPathSeparator();
743 if (m_pluginDataPath !=
"") {
744 return m_pluginDataPath;
751 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
752 if (isFlatpacked()) {
753 dirs =
"~/.var/app/org.opencpn.OpenCPN/data/opencpn/plugins";
754 }
else if (osSystemId & wxOS_UNIX_LINUX) {
755 dirs = GetLinuxDataPath();
756 }
else if (osSystemId & wxOS_WINDOWS) {
758 }
else if (osSystemId & wxOS_MAC) {
759 dirs =
"/Applications/OpenCPN.app/Contents/SharedSupport/plugins;";
761 "~/Library/Application Support/OpenCPN/Contents/SharedSupport/plugins";
765 m_pluginDataPath = ExpandPaths(dirs,
this);
766 if (m_pluginDataPath !=
"") {
767 m_pluginDataPath +=
";";
770 if (m_pluginDataPath.EndsWith(wxFileName::GetPathSeparator())) {
771 m_pluginDataPath.RemoveLast();
773 wxLogMessage(
"Using plugin data path: %s", m_pluginDataPath.mb_str().data());
774 return m_pluginDataPath;
778void AbstractPlatform::ShowBusySpinner() {
780 androidShowBusyIcon();
785void AbstractPlatform::ShowBusySpinner() {
787 ::wxBeginBusyCursor();
794void AbstractPlatform::HideBusySpinner() {
796 androidHideBusyIcon();
801void AbstractPlatform::HideBusySpinner() {
811#if defined(__ANDROID__)
812wxSize BasePlatform::getDisplaySize() {
return getAndroidDisplayDimensions(); }
815wxSize BasePlatform::getDisplaySize() {
return wxSize(0, 0); }
819double BasePlatform::GetDisplaySizeMM() {
820 if (m_displaySizeMMOverride.size() > 0 && m_displaySizeMMOverride[0] > 0) {
821 return m_displaySizeMMOverride[0];
826 ret = GetAndroidDisplaySize();
829 wxLogDebug(
"Detected display size (horizontal): %d mm", (
int)ret);
833#if defined(__ANDROID__)
834double BasePlatform::GetDisplayDPmm() {
return getAndroidDPmm(); }
837double BasePlatform::GetDisplayDPmm() {
838 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
839 double r = getDisplaySize().x;
840 return r / GetDisplaySizeMM();
851 if (win) rv = (double)(win->ToDIP(100)) / 100.;
856unsigned int AbstractPlatform::GetSelectRadiusPix() {
857 return GetDisplayDPmm() *
858 (g_btouch ? g_selection_radius_touch_mm : g_selection_radius_mm);
865const GUID GUID_CLASS_MONITOR = {0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08,
866 0x00, 0x2b, 0xe1, 0x03, 0x18};
869bool GetMonitorSizeFromEDID(
const HKEY hDevRegKey,
int* WidthMm,
871 DWORD dwType, AcutalValueNameLength = NAME_SIZE;
872 TCHAR valueName[NAME_SIZE];
875 DWORD edidsize =
sizeof(EDIDdata);
877 for (LONG i = 0, retValue = ERROR_SUCCESS; retValue != ERROR_NO_MORE_ITEMS;
879 retValue = RegEnumValue(hDevRegKey, i, &valueName[0],
880 &AcutalValueNameLength, NULL, &dwType,
884 if (retValue != ERROR_SUCCESS || 0 != _tcscmp(valueName, L
"EDID"))
continue;
886 *WidthMm = ((EDIDdata[68] & 0xF0) << 4) + EDIDdata[66];
887 *HeightMm = ((EDIDdata[68] & 0x0F) << 8) + EDIDdata[67];
895bool GetSizeForDevID(wxString& TargetDevID,
int* WidthMm,
int* HeightMm) {
897 SetupDiGetClassDevsEx(&GUID_CLASS_MONITOR,
905 if (NULL == devInfo)
return false;
909 for (ULONG i = 0; ERROR_NO_MORE_ITEMS != GetLastError(); ++i) {
910 SP_DEVINFO_DATA devInfoData;
911 memset(&devInfoData, 0,
sizeof(devInfoData));
912 devInfoData.cbSize =
sizeof(devInfoData);
914 if (SetupDiEnumDeviceInfo(devInfo, i, &devInfoData)) {
915 wchar_t Instance[80];
916 SetupDiGetDeviceInstanceId(devInfo, &devInfoData, Instance, MAX_PATH,
918 wxString instance(Instance);
919 if (instance.Upper().Find(TargetDevID.Upper()) == wxNOT_FOUND)
continue;
921 HKEY hDevRegKey = SetupDiOpenDevRegKey(
922 devInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
924 if (!hDevRegKey || (hDevRegKey == INVALID_HANDLE_VALUE))
continue;
926 bRes = GetMonitorSizeFromEDID(hDevRegKey, WidthMm, HeightMm);
928 RegCloseKey(hDevRegKey);
931 SetupDiDestroyDeviceInfoList(devInfo);
935bool AbstractPlatform::GetWindowsMonitorSize(
int* width,
int* height) {
936 bool bFoundDevice =
true;
938 if (m_monitorWidth < 10) {
948 bFoundDevice =
false;
949 while (EnumDisplayDevices(0, dev, &dd, 0) && !bFoundDevice) {
950 DISPLAY_DEVICE ddMon;
951 ZeroMemory(&ddMon,
sizeof(ddMon));
952 ddMon.cb =
sizeof(ddMon);
955 while (EnumDisplayDevices(dd.DeviceName, devMon, &ddMon, 0) &&
957 if (ddMon.StateFlags & DISPLAY_DEVICE_ACTIVE &&
958 !(ddMon.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)) {
959 DeviceID = wxString(ddMon.DeviceID, wxConvUTF8);
960 DeviceID = DeviceID.Mid(8);
961 DeviceID = DeviceID.Mid(0, DeviceID.Find(
'\\'));
963 bFoundDevice = GetSizeForDevID(DeviceID, &WidthMm, &HeightMm);
967 ZeroMemory(&ddMon,
sizeof(ddMon));
968 ddMon.cb =
sizeof(ddMon);
971 ZeroMemory(&dd,
sizeof(dd));
975 m_monitorWidth = WidthMm;
976 m_monitorHeight = HeightMm;
979 if (width) *width = m_monitorWidth;
980 if (height) *height = m_monitorHeight;
988 double size = w->GetCharHeight() * (IsWindows() ? 1.3 : 1.0);
989#if wxCHECK_VERSION(3, 1, 2)
992 size *=
static_cast<double>(w->ToDIP(100)) / 100.;
996 double pixel_per_mm = wxGetDisplaySize().x / GetDisplaySizeMM();
997 size = std::max(size, 7.0 * pixel_per_mm);
999 return std::round(size);
1004 return androidGetMemoryStatus(mem_total, mem_used);
1007#if defined(__linux__)
1011 struct sysinfo sys_info;
1012 if (sysinfo(&sys_info) != -1)
1013 *mem_total = ((uint64_t)sys_info.totalram * sys_info.mem_unit) / 1024;
1027 FILE* file = fopen(
"/proc/self/statm",
"r");
1029 if (fscanf(file,
"%d", mem_used) != 1) {
1030 wxLogWarning(
"Cannot parse /proc/self/statm (!)");
1043 PROCESS_MEMORY_COUNTERS pmc;
1045 unsigned long processID = wxGetProcessId();
1048 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
1051 if (hProcess && GetProcessMemoryInfo(hProcess, &pmc,
sizeof(pmc))) {
1069 *mem_used = pmc.WorkingSetSize / 1024;
1072 CloseHandle(hProcess);
1076 MEMORYSTATUSEX statex;
1078 statex.dwLength =
sizeof(statex);
1080 GlobalMemoryStatusEx(&statex);
1098 *mem_total = statex.ullTotalPhys / 1024;
1105 if (g_tick != g_lastMemTick) {
1106 malloc_zone_pressure_relief(NULL, 0);
1109 int blocksInUse = 0;
1110 int sizeAllocated = 0;
1112 malloc_statistics_t stats;
1113 stats.blocks_in_use = 0;
1114 stats.size_in_use = 0;
1115 stats.max_size_in_use = 0;
1116 stats.size_allocated = 0;
1117 malloc_zone_statistics(NULL, &stats);
1118 bytesInUse += stats.size_in_use;
1119 blocksInUse += stats.blocks_in_use;
1120 sizeAllocated += stats.size_allocated;
1122 g_memUsed = sizeAllocated >> 10;
1125 g_lastMemTick = g_tick;
1128 if (mem_used) *mem_used = g_memUsed;
1131 FILE* fpIn = popen(
"sysctl -n hw.memsize",
"r");
1133 double pagesUsed = 0.0, totalPages = 0.0;
1135 if (fgets(buf,
sizeof(buf), fpIn) != NULL) {
1136 *mem_total = atol(buf) >> 10;
1144 if (mem_used) *mem_used = 0;
1145 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.