| 1 | // tMachine.cpp  |
| 2 | //  |
| 3 | // Hardware ans OS access functions like querying supported instruction sets, number or cores, computer name/ip.  |
| 4 | // Includes parsing environment variables from the XDG Base Directory Specification (Linux-only).  |
| 5 | //  |
| 6 | // Copyright (c) 2004-2006, 2017, 2019-2022, 2024 Tristan Grimmer.  |
| 7 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
| 8 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
| 9 | //  |
| 10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
| 11 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
| 12 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
| 13 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
| 14 | // PERFORMANCE OF THIS SOFTWARE.  |
| 15 |   |
| 16 | #ifdef PLATFORM_WINDOWS  |
| 17 | #include <Windows.h>  |
| 18 | #include <intrin.h>  |
| 19 | #else  |
| 20 | #include <unistd.h>  |
| 21 | #include <limits.h>  |
| 22 | #include <sys/sysinfo.h>  |
| 23 | #endif  |
| 24 | #include <cstdlib>  |
| 25 | #include "Foundation/tStandard.h"  |
| 26 | #include "System/tFile.h"  |
| 27 | #include "System/tMachine.h"  |
| 28 |   |
| 29 |   |
| 30 | #ifdef PLATFORM_LINUX  |
| 31 | namespace tSystem  |
| 32 | {  |
| 33 | bool tGetXDGSingleEnvVar(tString& xdgEnvVar, const tString& xdgEnvVarName, const tString& xdgEnvVarDefault);  |
| 34 | bool tGetXDGMultipleEnvVar(tList<tStringItem>& xdgEnvVars, const tString& xdgEnvVarName, const tString& xdgEnvVarDefaults);  |
| 35 | };  |
| 36 | #endif  |
| 37 |   |
| 38 |   |
| 39 | bool tSystem::tSupportsSSE()  |
| 40 | {  |
| 41 | #ifdef PLATFORM_WINDOWS  |
| 42 | int cpuInfo[4];  |
| 43 | int infoType = 1;  |
| 44 | __cpuid(cpuInfo, infoType);  |
| 45 |   |
| 46 | int features = cpuInfo[3];  |
| 47 |   |
| 48 | // SSE feature bit is 25.  |
| 49 | if (features & (1 << 25))  |
| 50 | return true;  |
| 51 | else  |
| 52 | return false;  |
| 53 | #elif defined(PLATFORM_LINUX)  |
| 54 | // @todo Implement  |
| 55 | return true;  |
| 56 | #endif  |
| 57 | }  |
| 58 |   |
| 59 |   |
| 60 | bool tSystem::tSupportsSSE2()  |
| 61 | {  |
| 62 | #ifdef PLATFORM_WINDOWS  |
| 63 | int cpuInfo[4];  |
| 64 | int infoType = 1;  |
| 65 | __cpuid(cpuInfo, infoType);  |
| 66 |   |
| 67 | int features = cpuInfo[3];  |
| 68 |   |
| 69 | // SSE2 feature bit is 26.  |
| 70 | if (features & (1 << 26))  |
| 71 | return true;  |
| 72 | else  |
| 73 | return false;  |
| 74 |   |
| 75 | #elif defined(PLATFORM_LINUX)  |
| 76 | // @todo Implement  |
| 77 | return true;  |
| 78 | #endif  |
| 79 | }  |
| 80 |   |
| 81 |   |
| 82 | tString tSystem::tGetComputerName()  |
| 83 | {  |
| 84 | #ifdef PLATFORM_WINDOWS  |
| 85 |   |
| 86 | ulong nameSize = 128;  |
| 87 | #ifdef TACENT_UTF16_API_CALLS  |
| 88 | char16_t name[128];  |
| 89 | WinBool success = GetComputerName(LPWSTR(name), &nameSize);  |
| 90 | if (success)  |
| 91 | {  |
| 92 | tString nameUTF8;  |
| 93 | nameUTF8.SetUTF16(name);  |
| 94 | return nameUTF8;  |
| 95 | }  |
| 96 |   |
| 97 | #else  |
| 98 | char name[128];  |
| 99 | WinBool success = GetComputerName(name, &nameSize);  |
| 100 | if (success)  |
| 101 | return name;  |
| 102 | #endif  |
| 103 |   |
| 104 | #else  |
| 105 | char hostname[HOST_NAME_MAX];  |
| 106 | int err = gethostname(name: hostname, HOST_NAME_MAX);  |
| 107 | if (!err)  |
| 108 | return hostname;  |
| 109 |   |
| 110 | #endif  |
| 111 | return tString();  |
| 112 | }  |
| 113 |   |
| 114 |   |
| 115 | tString tSystem::tGetEnvVar(const tString& envVarName)  |
| 116 | {  |
| 117 | if (envVarName.IsEmpty())  |
| 118 | return tString();  |
| 119 |   |
| 120 | // The tString constructor handles possible nullptr input from getenv.  |
| 121 | return tString(std::getenv(name: envVarName.Chr()));  |
| 122 | }  |
| 123 |   |
| 124 |   |
| 125 | int tSystem::tGetNumCores()  |
| 126 | {  |
| 127 | // Lets cache this value as it never changes.  |
| 128 | static int numCores = 0;  |
| 129 | if (numCores > 0)  |
| 130 | return numCores;  |
| 131 |   |
| 132 | #ifdef PLATFORM_WINDOWS  |
| 133 | SYSTEM_INFO sysinfo;  |
| 134 | tStd::tMemset(&sysinfo, 0, sizeof(sysinfo));  |
| 135 | GetSystemInfo(&sysinfo);  |
| 136 |   |
| 137 | // dwNumberOfProcessors is unsigned, so can't say just > 0.  |
| 138 | if ((sysinfo.dwNumberOfProcessors == 0) || (sysinfo.dwNumberOfProcessors == -1))  |
| 139 | numCores = 1;  |
| 140 | else  |
| 141 | numCores = sysinfo.dwNumberOfProcessors;  |
| 142 |   |
| 143 | #else  |
| 144 | numCores = get_nprocs_conf();  |
| 145 | if (numCores < 1)  |
| 146 | numCores = 1;  |
| 147 |   |
| 148 | #endif  |
| 149 | return numCores;  |
| 150 | }  |
| 151 |   |
| 152 |   |
| 153 | bool tSystem::tOpenSystemFileExplorer(const tString& dir, const tString& file)  |
| 154 | {  |
| 155 | #ifdef PLATFORM_WINDOWS  |
| 156 | tString fullName = dir + file;  |
| 157 | HWND hWnd = ::GetActiveWindow();  |
| 158 |   |
| 159 | // Just open an explorer window if the dir is invalid.  |
| 160 | if (!tSystem::tDirExists(dir))  |
| 161 | {  |
| 162 | // 20D04FE0-3AEA-1069-A2D8-08002B30309D is the CLSID of "This PC" on Windows.  |
| 163 | #ifdef TACENT_UTF16_API_CALLS  |
| 164 | ShellExecute(hWnd, LPWSTR(u"open" ), LPWSTR(u"explorer" ), LPWSTR(u"/n,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" ), 0, SW_SHOWNORMAL);  |
| 165 | #else  |
| 166 | ShellExecute(hWnd, "open" , "explorer" , "/n,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" , 0, SW_SHOWNORMAL);  |
| 167 | #endif  |
| 168 | return false;  |
| 169 | }  |
| 170 |   |
| 171 | if (tSystem::tFileExists(fullName))  |
| 172 | {  |
| 173 | fullName.Replace('/', '\\');  |
| 174 | tString options;  |
| 175 | tsPrintf(options, "/select,\"%s\"" , fullName.Chr());  |
| 176 | #ifdef TACENT_UTF16_API_CALLS  |
| 177 | tStringUTF16 optionsUTF16(options);  |
| 178 | ShellExecute(hWnd, LPWSTR(u"open" ), LPWSTR(u"explorer" ), optionsUTF16.GetLPWSTR(), 0, SW_SHOWNORMAL);  |
| 179 | #else  |
| 180 | ShellExecute(hWnd, "open" , "explorer" , options.Chr(), 0, SW_SHOWNORMAL);  |
| 181 | #endif  |
| 182 | }  |
| 183 | else  |
| 184 | {  |
| 185 | #ifdef TACENT_UTF16_API_CALLS  |
| 186 | tStringUTF16 dirUTF16(dir);  |
| 187 | ShellExecute(hWnd, LPWSTR(u"open" ), dirUTF16.GetLPWSTR(), 0, dirUTF16.GetLPWSTR(), SW_SHOWNORMAL);  |
| 188 | #else  |
| 189 | ShellExecute(hWnd, "open" , dir.Chr(), 0, dir.Chr(), SW_SHOWNORMAL);  |
| 190 | #endif  |
| 191 | }  |
| 192 | return true;  |
| 193 |   |
| 194 | #elif defined(PLATFORM_LINUX)  |
| 195 | tString nautilus = "/usr/bin/nautilus" ;  |
| 196 | tString dolphin = "/usr/bin/dolphin" ;  |
| 197 | tString browser;  |
| 198 |   |
| 199 | if (tFileExists(file: nautilus))  |
| 200 | browser = nautilus;  |
| 201 | else if (tFileExists(file: dolphin))  |
| 202 | browser = dolphin;  |
| 203 | if (browser.IsEmpty())  |
| 204 | return false;  |
| 205 |   |
| 206 | tString sysStr;  |
| 207 | if (browser == nautilus)  |
| 208 | tsPrintf(dest&: sysStr, format: "%s %s%s &" , browser.Chr(), dir.Chr(), file.Chr());  |
| 209 | else if (browser == dolphin)  |
| 210 | tsPrintf(dest&: sysStr, format: "%s --new-window --select %s%s &" , browser.Chr(), dir.Chr(), file.Chr());  |
| 211 |   |
| 212 | system(command: sysStr.Chr());  |
| 213 | return true;  |
| 214 |   |
| 215 | #else  |
| 216 | return false;  |
| 217 |   |
| 218 | #endif  |
| 219 | }  |
| 220 |   |
| 221 |   |
| 222 | bool tSystem::tOpenSystemFileExplorer(const tString& fullFilename)  |
| 223 | {  |
| 224 | return tOpenSystemFileExplorer(dir: tSystem::tGetDir(path: fullFilename), file: tSystem::tGetFileName(file: fullFilename));  |
| 225 | }  |
| 226 |   |
| 227 |   |
| 228 | #if defined(PLATFORM_LINUX)  |
| 229 | bool tSystem::tGetXDGSingleEnvVar(tString& xdgEnvVar, const tString& xdgEnvVarName, const tString& xdgEnvVarDefault)  |
| 230 | {  |
| 231 | if (xdgEnvVarName.IsEmpty())  |
| 232 | {  |
| 233 | xdgEnvVar.Clear();  |
| 234 | return false;  |
| 235 | }  |
| 236 | xdgEnvVar = tGetEnvVar(envVarName: xdgEnvVarName);  |
| 237 | bool envVarSet = xdgEnvVar.IsValid();  |
| 238 | if (envVarSet)  |
| 239 | {  |
| 240 | tPathStdDir(path&: xdgEnvVar);  |
| 241 |   |
| 242 | // According to the spec xdgEnvVar should be an absolute path and ignored if relative.  |
| 243 | if (tIsRelativePath(path: xdgEnvVar))  |
| 244 | xdgEnvVar = xdgEnvVarDefault;  |
| 245 | }  |
| 246 | else  |
| 247 | {  |
| 248 | xdgEnvVar = xdgEnvVarDefault;  |
| 249 | }  |
| 250 |   |
| 251 | return envVarSet;  |
| 252 | }  |
| 253 |   |
| 254 |   |
| 255 | bool tSystem::tGetXDGMultipleEnvVar(tList<tStringItem>& xdgEnvVars, const tString& xdgEnvVarName, const tString& xdgEnvVarDefaults)  |
| 256 | {  |
| 257 | xdgEnvVars.Empty();  |
| 258 | if (xdgEnvVarName.IsEmpty())  |
| 259 | return false;  |
| 260 |   |
| 261 | tString xdgEnvVar = tGetEnvVar(envVarName: xdgEnvVarName);  |
| 262 | bool envVarSet = xdgEnvVar.IsValid();  |
| 263 | tList<tStringItem> paths;  |
| 264 | tStd::tExplode(components&: paths, src: xdgEnvVar, divider: ':');  |
| 265 |   |
| 266 | while (paths.Head())  |
| 267 | {  |
| 268 | tStringItem* path = paths.Remove();  |
| 269 | tPathStdDir(path&: *path);  |
| 270 |   |
| 271 | // According to the spec 'path' should be an absolute path and ignored if relative.  |
| 272 | if (tIsRelativePath(path: *path))  |
| 273 | delete path;  |
| 274 | else  |
| 275 | xdgEnvVars.Append(item: path);  |
| 276 | }  |
| 277 |   |
| 278 | if (xdgEnvVars.IsEmpty())  |
| 279 | {  |
| 280 | tList<tStringItem> defaultPaths;  |
| 281 | tStd::tExplode(components&: defaultPaths, src: xdgEnvVarDefaults, divider: ':');  |
| 282 | while (defaultPaths.Head())  |
| 283 | xdgEnvVars.Append(item: defaultPaths.Remove());  |
| 284 | }  |
| 285 |   |
| 286 | return envVarSet;  |
| 287 | }  |
| 288 |   |
| 289 |   |
| 290 | bool tSystem::tGetXDGDataHome(tString& xdgDataHome)  |
| 291 | {  |
| 292 | // From https://specifications.freedesktop.org/basedir-spec/latest/  |
| 293 | // There is a single base directory relative to which user-specific data files should be written.  |
| 294 | // This directory is defined by the environment variable $XDG_DATA_HOME.  |
| 295 | tString defaultPath = tGetHomeDir() + ".local/share/" ;  |
| 296 | return tGetXDGSingleEnvVar(xdgEnvVar&: xdgDataHome, xdgEnvVarName: "XDG_DATA_HOME" , xdgEnvVarDefault: defaultPath);  |
| 297 | }  |
| 298 |   |
| 299 |   |
| 300 | bool tSystem::tGetXDGConfigHome(tString& xdgConfigHome)  |
| 301 | {  |
| 302 | // From https://specifications.freedesktop.org/basedir-spec/latest/  |
| 303 | // There is a single base directory relative to which user-specific configuration files should be written.  |
| 304 | // This directory is defined by the environment variable $XDG_CONFIG_HOME.  |
| 305 | tString defaultPath = tGetHomeDir() + ".config/" ;  |
| 306 | return tGetXDGSingleEnvVar(xdgEnvVar&: xdgConfigHome, xdgEnvVarName: "XDG_CONFIG_HOME" , xdgEnvVarDefault: defaultPath);  |
| 307 | }  |
| 308 |   |
| 309 |   |
| 310 | bool tSystem::tGetXDGStateHome(tString& xdgStateHome)  |
| 311 | {  |
| 312 | // From https://specifications.freedesktop.org/basedir-spec/latest/  |
| 313 | // There is a single base directory relative to which user-specific state data should be written.  |
| 314 | // This directory is defined by the environment variable $XDG_STATE_HOME.  |
| 315 | tString defaultPath = tGetHomeDir() + ".local/state/" ;  |
| 316 | return tGetXDGSingleEnvVar(xdgEnvVar&: xdgStateHome, xdgEnvVarName: "XDG_STATE_HOME" , xdgEnvVarDefault: defaultPath);  |
| 317 | }  |
| 318 |   |
| 319 |   |
| 320 | void tSystem::tGetXDGExeHome(tString& xdgExeHome)  |
| 321 | {  |
| 322 | // From https://specifications.freedesktop.org/basedir-spec/latest/  |
| 323 | // There is a single base directory relative to which user-specific executable files may be written.  |
| 324 | xdgExeHome = tGetHomeDir() + ".local/bin/" ;  |
| 325 | }  |
| 326 |   |
| 327 |   |
| 328 | bool tSystem::tGetXDGDataDirs(tList<tStringItem>& xdgDataDirs)  |
| 329 | {  |
| 330 | // From https://specifications.freedesktop.org/basedir-spec/latest/  |
| 331 | // There is a set of preference ordered base directories relative to which data files should be searched.  |
| 332 | // This set of directories is defined by the environment variable $XDG_DATA_DIRS.  |
| 333 | tString defaultPaths = "/usr/local/share/:/usr/share/" ;  |
| 334 | return tGetXDGMultipleEnvVar(xdgEnvVars&: xdgDataDirs, xdgEnvVarName: "XDG_DATA_DIRS" , xdgEnvVarDefaults: defaultPaths);  |
| 335 | }  |
| 336 |   |
| 337 |   |
| 338 | bool tSystem::tGetXDGConfigDirs(tList<tStringItem>& xdgConfigDirs)  |
| 339 | {  |
| 340 | // From https://specifications.freedesktop.org/basedir-spec/latest/  |
| 341 | // There is a set of preference ordered base directories relative to which configuration files should be searched.  |
| 342 | // This set of directories is defined by the environment variable $XDG_CONFIG_DIRS.  |
| 343 | tString defaultPaths = "/etc/xdg/" ;  |
| 344 | return tGetXDGMultipleEnvVar(xdgEnvVars&: xdgConfigDirs, xdgEnvVarName: "XDG_CONFIG_DIRS" , xdgEnvVarDefaults: defaultPaths);   |
| 345 | }  |
| 346 |   |
| 347 |   |
| 348 | bool tSystem::tGetXDGCacheHome(tString& xdgCacheHome)  |
| 349 | {  |
| 350 | // From https://specifications.freedesktop.org/basedir-spec/latest/  |
| 351 | // There is a single base directory relative to which user-specific non-essential (cached) data should be written.  |
| 352 | // This directory is defined by the environment variable $XDG_CACHE_HOME.  |
| 353 | tString defaultPath = tGetHomeDir() + ".cache/" ;  |
| 354 | return tGetXDGSingleEnvVar(xdgEnvVar&: xdgCacheHome, xdgEnvVarName: "XDG_CACHE_HOME" , xdgEnvVarDefault: defaultPath);  |
| 355 | }  |
| 356 |   |
| 357 |   |
| 358 | bool tSystem::tGetXDGRuntimeDir(tString& xdgRuntimeDir)  |
| 359 | {  |
| 360 | // From https://specifications.freedesktop.org/basedir-spec/latest/  |
| 361 | // There is a single base directory relative to which user-specific runtime files and other file objects should be placed.  |
| 362 | // This directory is defined by the environment variable $XDG_RUNTIME_DIR.  |
| 363 | //  |
| 364 | // The defalut is empty on purpose for this one.  |
| 365 | tString defaultPath;  |
| 366 | return tGetXDGSingleEnvVar(xdgEnvVar&: xdgRuntimeDir, xdgEnvVarName: "XDG_RUNTIME_DIR" , xdgEnvVarDefault: defaultPath);  |
| 367 | }  |
| 368 | #endif  |
| 369 | |