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 
31namespace 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 
39bool 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 
60bool 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 
82tString 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 
115tString 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 
125int 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 
153bool 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 
222bool 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) 
229bool 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 
255bool 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 
290bool 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 
300bool 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 
310bool 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 
320void 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 
328bool 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 
338bool 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 
348bool 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 
358bool 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