systeminformation 5.31.16 → 5.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/graphics.js CHANGED
@@ -35,6 +35,10 @@ let _resolutionY = 0;
35
35
  let _pixelDepth = 0;
36
36
  let _refreshRate = 0;
37
37
 
38
+ // EnumDisplaySettings (current mode per \\.\DISPLAYn) - per-display refresh rate (issue #853)
39
+ const psCurrentModes =
40
+ "Add-Type -AssemblyName System.Windows.Forms; if (-not ('SiDevMode' -as [Type])) { Add-Type -TypeDefinition 'using System;using System.Runtime.InteropServices;[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]public struct SIDEVMODE{[MarshalAs(UnmanagedType.ByValTStr,SizeConst=32)]public string dmDeviceName;public short dmSpecVersion;public short dmDriverVersion;public short dmSize;public short dmDriverExtra;public int dmFields;public int dmPositionX;public int dmPositionY;public int dmDisplayOrientation;public int dmDisplayFixedOutput;public short dmColor;public short dmDuplex;public short dmYResolution;public short dmTTOption;public short dmCollate;[MarshalAs(UnmanagedType.ByValTStr,SizeConst=32)]public string dmFormName;public short dmLogPixels;public int dmBitsPerPel;public int dmPelsWidth;public int dmPelsHeight;public int dmDisplayFlags;public int dmDisplayFrequency;public int dmICMMethod;public int dmICMIntent;public int dmMediaType;public int dmDitherType;public int dmReserved1;public int dmReserved2;public int dmPanningWidth;public int dmPanningHeight;}public class SiDevMode{[DllImport(\"user32.dll\",CharSet=CharSet.Ansi)]public static extern bool EnumDisplaySettings(string lpszDeviceName,int iModeNum,ref SIDEVMODE lpDevMode);}' }; [System.Windows.Forms.Screen]::AllScreens | ForEach-Object { $dm = New-Object SIDEVMODE; $dm.dmSize = [System.Runtime.InteropServices.Marshal]::SizeOf($dm); if ([SiDevMode]::EnumDisplaySettings($_.DeviceName, -1, [ref]$dm)) { $_.DeviceName + '|' + $dm.dmDisplayFrequency + '|' + $dm.dmBitsPerPel + '|' + $dm.dmPelsWidth + '|' + $dm.dmPelsHeight } }";
41
+
38
42
  const videoTypes = {
39
43
  '-2': 'UNINITIALIZED',
40
44
  '-1': 'OTHER',
@@ -53,7 +57,8 @@ const videoTypes = {
53
57
  13: 'UDI embedded',
54
58
  14: 'SDTVDONGLE',
55
59
  15: 'MIRACAST',
56
- 2147483648: 'INTERNAL'
60
+ 2147483648: 'INTERNAL',
61
+ 4294967295: 'RDP'
57
62
  };
58
63
 
59
64
  function getVendorFromModel(model) {
@@ -78,7 +83,7 @@ function getVendorFromModel(model) {
78
83
  { pattern: 'APPLE.?', manufacturer: 'Apple' },
79
84
  { pattern: 'INTEL.?', manufacturer: 'Intel' },
80
85
  { pattern: 'AMD.?', manufacturer: 'AMD' },
81
- { pattern: 'NVIDIA.?', manufacturer: 'NVDIA' }
86
+ { pattern: 'NVIDIA.?', manufacturer: 'NVIDIA' }
82
87
  ];
83
88
 
84
89
  let result = '';
@@ -153,7 +158,7 @@ function graphics(callback) {
153
158
  const bus = (item.sppci_bus || '').indexOf('builtin') > -1 ? 'Built-In' : (item.sppci_bus || '').indexOf('pcie') > -1 ? 'PCIe' : '';
154
159
  const vram = (parseInt(item.spdisplays_vram || '', 10) || 0) * ((item.spdisplays_vram || '').indexOf('GB') > -1 ? 1024 : 1);
155
160
  const vramDyn = (parseInt(item.spdisplays_vram_shared || '', 10) || 0) * ((item.spdisplays_vram_shared || '').indexOf('GB') > -1 ? 1024 : 1);
156
- let metalVersion = getMetalVersion(item.spdisplays_metal || item.spdisplays_metalfamily || '');
161
+ const metalVersion = getMetalVersion(item.spdisplays_metal || item.spdisplays_metalfamily || '');
157
162
  res.controllers.push({
158
163
  vendor: getVendorFromModel(item.spdisplays_vendor || '') || item.spdisplays_vendor || '',
159
164
  model: item.sppci_model || '',
@@ -207,7 +212,7 @@ function graphics(callback) {
207
212
  }
208
213
 
209
214
  function parseLinesLinuxControllers(lines) {
210
- let controllers = [];
215
+ const controllers = [];
211
216
  let currentController = {
212
217
  vendor: '',
213
218
  subVendor: '',
@@ -243,14 +248,18 @@ function graphics(callback) {
243
248
  if ('' !== line.trim()) {
244
249
  if (' ' !== line[0] && '\t' !== line[0]) {
245
250
  // first line of new entry
246
- let isExternal = pciIDs.indexOf(line.split(' ')[0]) >= 0;
251
+ const isExternal = pciIDs.indexOf(line.split(' ')[0]) >= 0;
247
252
  let vgapos = line.toLowerCase().indexOf(' vga ');
248
- let _3dcontrollerpos = line.toLowerCase().indexOf('3d controller');
249
- if (vgapos !== -1 || _3dcontrollerpos !== -1) {
253
+ const _3dcontrollerpos = line.toLowerCase().indexOf('3d controller');
254
+ const _displaycontrollerpos = line.toLowerCase().indexOf('display controller');
255
+ if (vgapos !== -1 || _3dcontrollerpos !== -1 || _displaycontrollerpos !== -1) {
250
256
  // VGA
251
257
  if (_3dcontrollerpos !== -1 && vgapos === -1) {
252
258
  vgapos = _3dcontrollerpos;
253
259
  }
260
+ if (_displaycontrollerpos !== -1 && vgapos === -1) {
261
+ vgapos = _displaycontrollerpos;
262
+ }
254
263
  if (currentController.vendor || currentController.model || currentController.bus || currentController.vram !== null || currentController.vramDynamic) {
255
264
  // already a controller found
256
265
  controllers.push(currentController);
@@ -269,8 +278,8 @@ function graphics(callback) {
269
278
  currentController.busAddress = pciIDCandidate;
270
279
  }
271
280
  isGraphicsController = true;
272
- let endpos = line.search(/\[[0-9a-f]{4}:[0-9a-f]{4}]|$/);
273
- let parts = line.substr(vgapos, endpos - vgapos).split(':');
281
+ const endpos = line.search(/\[[0-9a-f]{4}:[0-9a-f]{4}]|$/);
282
+ const parts = line.substr(vgapos, endpos - vgapos).split(':');
274
283
  currentController.busAddress = line.substr(0, vgapos).trim();
275
284
  if (parts.length > 1) {
276
285
  parts[1] = parts[1].trim();
@@ -332,7 +341,7 @@ function graphics(callback) {
332
341
  }
333
342
  if (isGraphicsController) {
334
343
  // within VGA details
335
- let parts = line.split(':');
344
+ const parts = line.split(':');
336
345
  if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('devicename') !== -1 && parts[1].toLowerCase().indexOf('onboard') !== -1) {
337
346
  currentController.bus = 'Onboard';
338
347
  }
@@ -341,9 +350,13 @@ function graphics(callback) {
341
350
  if (sizeMatch) {
342
351
  let vram = parseInt(sizeMatch[1], 10);
343
352
  const unit = (sizeMatch[2] || '').toUpperCase();
344
- if (unit === 'G') { vram *= 1024; }
345
- else if (unit === 'K') { vram = Math.round(vram / 1024); }
346
- else if (unit === '') { vram = Math.round(vram / 1024 / 1024); } // bytes
353
+ if (unit === 'G') {
354
+ vram *= 1024;
355
+ } else if (unit === 'K') {
356
+ vram = Math.round(vram / 1024);
357
+ } else if (unit === '') {
358
+ vram = Math.round(vram / 1024 / 1024);
359
+ } // bytes
347
360
  // keep the largest memory region (the actual framebuffer aperture)
348
361
  if (currentController.vram === null || vram > currentController.vram) {
349
362
  currentController.vram = vram;
@@ -374,7 +387,7 @@ function graphics(callback) {
374
387
  }
375
388
  return devices;
376
389
  }, {});
377
- for (let deviceId in devices) {
390
+ for (const deviceId in devices) {
378
391
  const device = devices[deviceId];
379
392
  if (device['CL_DEVICE_TYPE'] === 'CL_DEVICE_TYPE_GPU') {
380
393
  let busAddress;
@@ -429,24 +442,29 @@ function graphics(callback) {
429
442
 
430
443
  if (_windows) {
431
444
  try {
432
- const basePath = path.join(util.WINDIR, 'System32', 'DriverStore', 'FileRepository');
433
- // find all directories that have an nvidia-smi.exe file with date
434
- const candidates = fs
435
- .readdirSync(basePath, { withFileTypes: true })
436
- .filter((dir) => dir.isDirectory())
437
- .map((dir) => {
438
- const nvidiaSmiPath = path.join(basePath, dir.name, 'nvidia-smi.exe');
439
- try {
440
- const stats = fs.statSync(nvidiaSmiPath);
441
- return { path: nvidiaSmiPath, ctime: stats.ctimeMs };
442
- } catch {
443
- return null;
444
- }
445
- })
446
- .filter(Boolean);
447
- if (candidates.length > 0) {
448
- // take the most recent
449
- _nvidiaSmiPath = candidates.reduce((prev, curr) => (curr.ctime > prev.ctime ? curr : prev)).path;
445
+ const systemSmiPath = path.join(util.WINDIR, 'System32', 'nvidia-smi.exe');
446
+ if (fs.existsSync(systemSmiPath)) {
447
+ _nvidiaSmiPath = systemSmiPath;
448
+ } else {
449
+ const basePath = path.join(util.WINDIR, 'System32', 'DriverStore', 'FileRepository');
450
+ // find all directories that have an nvidia-smi.exe file with date
451
+ const candidates = fs
452
+ .readdirSync(basePath, { withFileTypes: true })
453
+ .filter((dir) => dir.isDirectory())
454
+ .map((dir) => {
455
+ const nvidiaSmiPath = path.join(basePath, dir.name, 'nvidia-smi.exe');
456
+ try {
457
+ const stats = fs.statSync(nvidiaSmiPath);
458
+ return { path: nvidiaSmiPath, ctime: stats.ctimeMs };
459
+ } catch {
460
+ return null;
461
+ }
462
+ })
463
+ .filter(Boolean);
464
+ if (candidates.length > 0) {
465
+ // take the most recent
466
+ _nvidiaSmiPath = candidates.reduce((prev, curr) => (curr.ctime > prev.ctime ? curr : prev)).path;
467
+ }
450
468
  }
451
469
  } catch {
452
470
  util.noop();
@@ -459,7 +477,7 @@ function graphics(callback) {
459
477
 
460
478
  function nvidiaSmi(options) {
461
479
  const nvidiaSmiExe = getNvidiaSmi();
462
- options = options || util.execOptsWin;
480
+ options = Object.assign({}, options || util.execOptsWin);
463
481
  if (nvidiaSmiExe) {
464
482
  const nvidiaSmiOpts =
465
483
  '--query-gpu=driver_version,pci.sub_device_id,name,pci.bus_id,fan.speed,memory.total,memory.used,memory.free,utilization.gpu,utilization.memory,temperature.gpu,temperature.memory,power.draw,power.limit,clocks.gr,clocks.mem --format=csv,noheader,nounits';
@@ -669,11 +687,26 @@ function graphics(callback) {
669
687
  let is_current = false;
670
688
  let edid_raw = '';
671
689
  let start = 0;
690
+ const applyEdid = () => {
691
+ const edid_decoded = parseLinesLinuxEdid(edid_raw);
692
+ currentDisplay.vendor = edid_decoded.vendor;
693
+ currentDisplay.model = edid_decoded.model;
694
+ currentDisplay.resolutionX = edid_decoded.resolutionX;
695
+ currentDisplay.resolutionY = edid_decoded.resolutionY;
696
+ currentDisplay.sizeX = edid_decoded.sizeX;
697
+ currentDisplay.sizeY = edid_decoded.sizeY;
698
+ currentDisplay.pixelDepth = depth;
699
+ is_edid = false;
700
+ };
672
701
  for (let i = 1; i < lines.length; i++) {
673
702
  // start with second line
674
703
  if ('' !== lines[i].trim()) {
675
704
  if (' ' !== lines[i][0] && '\t' !== lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') !== -1) {
676
705
  // first line of new entry
706
+ if (is_edid && edid_raw) {
707
+ // pending EDID belongs to the previous display
708
+ applyEdid();
709
+ }
677
710
  if (
678
711
  currentDisplay.model ||
679
712
  currentDisplay.main ||
@@ -703,10 +736,15 @@ function graphics(callback) {
703
736
  currentRefreshRate: null
704
737
  };
705
738
  }
706
- let parts = lines[i].split(' ');
739
+ const parts = lines[i].split(' ');
707
740
  currentDisplay.connection = parts[0];
708
741
  currentDisplay.main = lines[i].toLowerCase().indexOf(' primary ') >= 0;
709
742
  currentDisplay.builtin = parts[0].toLowerCase().indexOf('edp') >= 0;
743
+ const geometry = lines[i].match(/\d+x\d+\+(-?\d+)\+(-?\d+)/);
744
+ if (geometry) {
745
+ currentDisplay.positionX = util.toInt(geometry[1]);
746
+ currentDisplay.positionY = util.toInt(geometry[2]);
747
+ }
710
748
  }
711
749
 
712
750
  // try to read EDID information
@@ -715,19 +753,12 @@ function graphics(callback) {
715
753
  edid_raw += lines[i].toLowerCase().trim();
716
754
  } else {
717
755
  // parsen EDID
718
- let edid_decoded = parseLinesLinuxEdid(edid_raw);
719
- currentDisplay.vendor = edid_decoded.vendor;
720
- currentDisplay.model = edid_decoded.model;
721
- currentDisplay.resolutionX = edid_decoded.resolutionX;
722
- currentDisplay.resolutionY = edid_decoded.resolutionY;
723
- currentDisplay.sizeX = edid_decoded.sizeX;
724
- currentDisplay.sizeY = edid_decoded.sizeY;
725
- currentDisplay.pixelDepth = depth;
726
- is_edid = false;
756
+ applyEdid();
727
757
  }
728
758
  }
729
759
  if (lines[i].toLowerCase().indexOf('edid:') >= 0) {
730
760
  is_edid = true;
761
+ edid_raw = '';
731
762
  start = lines[i].search(/\S|$/);
732
763
  }
733
764
  if (lines[i].toLowerCase().indexOf('*current') >= 0) {
@@ -750,6 +781,10 @@ function graphics(callback) {
750
781
  }
751
782
 
752
783
  // pushen displays
784
+ if (is_edid && edid_raw) {
785
+ // EDID was the last block in the output
786
+ applyEdid();
787
+ }
753
788
  if (
754
789
  currentDisplay.model ||
755
790
  currentDisplay.main ||
@@ -782,6 +817,20 @@ function graphics(callback) {
782
817
  } catch (e) {
783
818
  util.noop();
784
819
  }
820
+ try {
821
+ // GPU temperature (Apple Silicon) - optional dependency
822
+ const macosTemp = require('macos-temperature-sensor');
823
+ const temps = macosTemp.temperature();
824
+ if (temps && temps.gpu) {
825
+ result.controllers.forEach((controller) => {
826
+ if (controller.bus === 'Built-In') {
827
+ controller.temperatureGpu = Math.round(temps.gpu * 100) / 100;
828
+ }
829
+ });
830
+ }
831
+ } catch {
832
+ util.noop();
833
+ }
785
834
  try {
786
835
  stdout = execSync(
787
836
  'defaults read /Library/Preferences/com.apple.windowserver.plist 2>/dev/null;defaults read /Library/Preferences/com.apple.windowserver.displays.plist 2>/dev/null; echo ""',
@@ -834,7 +883,7 @@ function graphics(callback) {
834
883
  const cmd = "fbset -s 2> /dev/null | grep 'mode \"' ; vcgencmd get_mem gpu 2> /dev/null; tvservice -s 2> /dev/null; tvservice -n 2> /dev/null;";
835
884
  exec(cmd, (error, stdout) => {
836
885
  const lines = stdout.toString().split('\n');
837
- if (lines.length > 3 && lines[0].indexOf('mode "') >= -1 && lines[2].indexOf('0x12000a') > -1) {
886
+ if (lines.length > 3 && lines[0].indexOf('mode "') >= 0 && lines[2].indexOf('0x12000a') > -1) {
838
887
  const parts = lines[0].replace('mode', '').replace(/"/g, '').trim().split('x');
839
888
  if (parts.length === 2) {
840
889
  result.displays.push({
@@ -856,12 +905,12 @@ function graphics(callback) {
856
905
  });
857
906
  }
858
907
  }
859
- if (lines.length >= 1 && stdout.toString().indexOf('gpu=') >= -1) {
908
+ if (lines.length >= 1 && stdout.toString().indexOf('gpu=') >= 0) {
860
909
  result.controllers.push({
861
910
  vendor: 'Broadcom',
862
911
  model: util.getRpiGpu(),
863
912
  bus: '',
864
- vram: util.getValue(lines, 'gpu', '=').replace('M', ''),
913
+ vram: parseInt(util.getValue(lines, 'gpu', '=').replace('M', ''), 10) || null,
865
914
  vramDynamic: true
866
915
  });
867
916
  }
@@ -879,7 +928,7 @@ function graphics(callback) {
879
928
  // needs to be rewritten ... using no spread operators
880
929
  result.controllers = result.controllers.map((controller) => {
881
930
  // match by busAddress
882
- return mergeControllerNvidia(controller, nvidiaData.find((contr) => contr.pciBus.toLowerCase().endsWith(controller.busAddress.toLowerCase())) || {});
931
+ return mergeControllerNvidia(controller, nvidiaData.find((contr) => contr.pciBus && controller.busAddress && contr.pciBus.toLowerCase().endsWith(controller.busAddress.toLowerCase())) || {});
883
932
  });
884
933
  }
885
934
  }
@@ -941,9 +990,10 @@ function graphics(callback) {
941
990
  workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorConnectionParams | fl'));
942
991
  workload.push(
943
992
  util.powerShell(
944
- 'gwmi WmiMonitorID -Namespace root\\wmi | ForEach-Object {(($_.ManufacturerName -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.ProductCodeID -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.UserFriendlyName -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.SerialNumberID -notmatch 0 | foreach {[char]$_}) -join "") + "|" + $_.InstanceName}'
993
+ 'gwmi WmiMonitorID -Namespace root\\wmi | ForEach-Object {(($_.ManufacturerName -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.ProductCodeID -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.UserFriendlyName -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.SerialNumberID -notmatch 0 | foreach {[char]$_}) -join "") + "|" + $_.YearOfManufacture + "|" + $_.InstanceName}'
945
994
  )
946
995
  );
996
+ workload.push(util.powerShell(psCurrentModes));
947
997
 
948
998
  const nvidiaData = nvidiaDevices();
949
999
 
@@ -960,7 +1010,7 @@ function graphics(callback) {
960
1010
  controller,
961
1011
  nvidiaData.find((device) => {
962
1012
  let windowsSubDeviceId = (controller.subDeviceId || '').toLowerCase();
963
- const nvidiaSubDeviceIdParts = device.subDeviceId.split('x');
1013
+ const nvidiaSubDeviceIdParts = (device.subDeviceId || '').split('x');
964
1014
  let nvidiaSubDeviceId = nvidiaSubDeviceIdParts.length > 1 ? nvidiaSubDeviceIdParts[1].toLowerCase() : nvidiaSubDeviceIdParts[0].toLowerCase();
965
1015
  const lengthDifference = Math.abs(windowsSubDeviceId.length - nvidiaSubDeviceId.length);
966
1016
  if (windowsSubDeviceId.length > nvidiaSubDeviceId.length) {
@@ -990,35 +1040,65 @@ function graphics(callback) {
990
1040
  dsections.pop();
991
1041
  }
992
1042
 
993
- // monitor (powershell)
994
- const msections = data[3].replace(/\r/g, '').split('Active ');
995
- msections.shift();
1043
+ // physical monitors (powershell) - keyed by InstanceName (issue #764)
1044
+ // inactive monitors (attached but not part of the desktop, e.g. "PC screen only") are skipped
1045
+ const monitors = [];
1046
+ data[3].replace(/\r/g, '').split(/\n\s*\n/).forEach((section) => {
1047
+ const lines = section.split('\n');
1048
+ const monitorInstanceName = util.getValue(lines, 'InstanceName').toLowerCase();
1049
+ const monitorActive = util.getValue(lines, 'Active').toLowerCase() !== 'false';
1050
+ if (monitorInstanceName && monitorActive) {
1051
+ monitors.push({
1052
+ instanceName: monitorInstanceName,
1053
+ sizeX: util.getValue(lines, 'MaxHorizontalImageSize'),
1054
+ sizeY: util.getValue(lines, 'MaxVerticalImageSize')
1055
+ });
1056
+ }
1057
+ });
996
1058
 
997
1059
  // forms.screens (powershell)
998
1060
  const ssections = data[4].replace(/\r/g, '').split('BitsPerPixel ');
999
1061
  ssections.shift();
1000
1062
 
1001
- // connection params (powershell) - video type
1002
- const tsections = data[5].replace(/\r/g, '').split(/\n\s*\n/);
1003
- tsections.shift();
1063
+ // connection params (powershell) - video type per InstanceName (issue #764)
1064
+ const connections = Object.create(null);
1065
+ data[5].replace(/\r/g, '').split(/\n\s*\n/).forEach((section) => {
1066
+ const lines = section.split('\n');
1067
+ const connectionInstanceName = util.getValue(lines, 'InstanceName').toLowerCase();
1068
+ if (connectionInstanceName) {
1069
+ connections[connectionInstanceName] = util.getValue(lines, 'VideoOutputTechnology');
1070
+ }
1071
+ });
1004
1072
 
1005
1073
  // monitor ID (powershell) - model / vendor
1006
1074
  const res = data[6].replace(/\r/g, '').split(/\n/);
1007
1075
  const isections = [];
1008
1076
  res.forEach((element) => {
1009
1077
  const parts = element.split('|');
1010
- if (parts.length === 5) {
1078
+ if (parts.length === 6) {
1011
1079
  isections.push({
1012
1080
  vendor: parts[0],
1013
1081
  code: parts[1],
1014
1082
  model: parts[2],
1015
1083
  serial: parts[3],
1016
- instanceId: parts[4]
1084
+ productionYear: util.toInt(parts[4]) || null,
1085
+ instanceId: parts[5]
1017
1086
  });
1018
1087
  }
1019
1088
  });
1020
1089
 
1021
- result.displays = parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections);
1090
+ // current display mode per device name (issue #853)
1091
+ const currentModes = Object.create(null);
1092
+ (data[7] || '').replace(/\r/g, '').split(/\n/).forEach((element) => {
1093
+ const parts = element.split('|');
1094
+ const frequency = parts.length === 5 ? util.toInt(parts[1]) : 0;
1095
+ // dmDisplayFrequency 0/1 means hardware default
1096
+ if (frequency > 1 && parts[0]) {
1097
+ currentModes[parts[0].trim().toLowerCase()] = frequency;
1098
+ }
1099
+ });
1100
+
1101
+ result.displays = parseLinesWindowsDisplaysPowershell(ssections, monitors, dsections, connections, isections, currentModes);
1022
1102
 
1023
1103
  if (result.displays.length === 1) {
1024
1104
  if (_resolutionX) {
@@ -1157,7 +1237,7 @@ function graphics(callback) {
1157
1237
  return controllers;
1158
1238
  }
1159
1239
 
1160
- function parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections) {
1240
+ function parseLinesWindowsDisplaysPowershell(ssections, monitors, dsections, connections, isections, currentModes) {
1161
1241
  const displays = [];
1162
1242
  let vendor = '';
1163
1243
  let model = '';
@@ -1172,40 +1252,34 @@ function graphics(callback) {
1172
1252
  resolutionX = util.toInt(util.getValue(linesDisplay, 'ScreenWidth', ':'));
1173
1253
  resolutionY = util.toInt(util.getValue(linesDisplay, 'ScreenHeight', ':'));
1174
1254
  }
1175
- for (let i = 0; i < ssections.length; i++) {
1176
- if (ssections[i].trim() !== '') {
1177
- ssections[i] = 'BitsPerPixel ' + ssections[i];
1178
- msections[i] = 'Active ' + msections[i];
1179
- // tsections can be empty OR undefined on earlier versions of powershell (<=2.0)
1180
- // Tag connection type as UNKNOWN by default if this information is missing
1181
- if (tsections.length === 0 || tsections[i] === undefined) {
1182
- tsections[i] = 'Unknown';
1183
- }
1184
- const linesScreen = ssections[i].split('\n');
1185
- const linesMonitor = msections[i].split('\n');
1186
-
1187
- const linesConnection = tsections[i].split('\n');
1188
- const bitsPerPixel = util.getValue(linesScreen, 'BitsPerPixel');
1255
+ // iterate over physical monitors too - mirrored monitors have no own logical screen (issue #940)
1256
+ const count = Math.max(ssections.length, monitors.length);
1257
+ for (let i = 0; i < count; i++) {
1258
+ const hasOwnScreen = i < ssections.length;
1259
+ // mirrored monitors show the same image as the first screen
1260
+ const ssection = hasOwnScreen ? ssections[i] : ssections[0];
1261
+ if (ssection !== undefined && ssection.trim() !== '') {
1262
+ const linesScreen = ('BitsPerPixel ' + ssection).split('\n');
1263
+ // screen <-> monitor correlation stays index based - Forms.Screen exposes no shared key with root\wmi
1264
+ const monitor = monitors[i];
1265
+ const instanceName = monitor ? monitor.instanceName : '';
1266
+ const bitsPerPixel = util.toInt(util.getValue(linesScreen, 'BitsPerPixel')) || null;
1189
1267
  const bounds = util.getValue(linesScreen, 'Bounds').replace('{', '').replace('}', '').replace(/=/g, ':').split(',');
1190
1268
  const primary = util.getValue(linesScreen, 'Primary');
1191
- const sizeX = util.getValue(linesMonitor, 'MaxHorizontalImageSize');
1192
- const sizeY = util.getValue(linesMonitor, 'MaxVerticalImageSize');
1193
- const instanceName = util.getValue(linesMonitor, 'InstanceName').toLowerCase();
1194
- const videoOutputTechnology = util.getValue(linesConnection, 'VideoOutputTechnology');
1269
+ const sizeX = monitor ? monitor.sizeX : '';
1270
+ const sizeY = monitor ? monitor.sizeY : '';
1271
+ const videoOutputTechnology = instanceName && connections[instanceName] !== undefined ? connections[instanceName] : '';
1195
1272
  const deviceName = util.getValue(linesScreen, 'DeviceName');
1196
- let displayVendor = '';
1197
- let displayModel = '';
1198
- isections.forEach((element) => {
1199
- if (element.instanceId.toLowerCase().startsWith(instanceName) && vendor.startsWith('(') && model.startsWith('PnP')) {
1200
- displayVendor = element.vendor;
1201
- displayModel = element.model;
1202
- }
1203
- });
1273
+ // WmiMonitorID data matches per InstanceName - prefer it over the locale-dependent Win32_DesktopMonitor values
1274
+ const isection = instanceName ? isections.find((element) => element.instanceId.toLowerCase().startsWith(instanceName)) : undefined;
1204
1275
  displays.push({
1205
- vendor: instanceName.startsWith(deviceID) && displayVendor === '' ? vendor : displayVendor,
1206
- model: instanceName.startsWith(deviceID) && displayModel === '' ? model : displayModel,
1276
+ vendor: (isection && isection.vendor) || (instanceName.startsWith(deviceID) ? vendor : ''),
1277
+ model: (isection && isection.model) || (instanceName.startsWith(deviceID) ? model : ''),
1278
+ serial: (isection && isection.serial) || null,
1279
+ productionYear: isection ? isection.productionYear : null,
1280
+ displayId: instanceName || null,
1207
1281
  deviceName,
1208
- main: primary.toLowerCase() === 'true',
1282
+ main: hasOwnScreen ? primary.toLowerCase() === 'true' : false,
1209
1283
  builtin: videoOutputTechnology === '2147483648',
1210
1284
  connection: videoOutputTechnology && videoTypes[videoOutputTechnology] ? videoTypes[videoOutputTechnology] : '',
1211
1285
  resolutionX: util.toInt(util.getValue(bounds, 'Width', ':')),
@@ -1216,7 +1290,8 @@ function graphics(callback) {
1216
1290
  currentResX: util.toInt(util.getValue(bounds, 'Width', ':')),
1217
1291
  currentResY: util.toInt(util.getValue(bounds, 'Height', ':')),
1218
1292
  positionX: util.toInt(util.getValue(bounds, 'X', ':')),
1219
- positionY: util.toInt(util.getValue(bounds, 'Y', ':'))
1293
+ positionY: util.toInt(util.getValue(bounds, 'Y', ':')),
1294
+ currentRefreshRate: currentModes[deviceName.toLowerCase()] || null
1220
1295
  });
1221
1296
  }
1222
1297
  }
@@ -1224,6 +1299,9 @@ function graphics(callback) {
1224
1299
  displays.push({
1225
1300
  vendor,
1226
1301
  model,
1302
+ serial: null,
1303
+ productionYear: null,
1304
+ displayId: null,
1227
1305
  main: true,
1228
1306
  sizeX: null,
1229
1307
  sizeY: null,
package/lib/index.d.ts CHANGED
@@ -762,6 +762,7 @@ export namespace Systeminformation {
762
762
  restartCount: number;
763
763
  platform: string;
764
764
  driver: string;
765
+ labels: { [key: string]: string };
765
766
  ports: number[];
766
767
  mounts: DockerContainerMountData[];
767
768
  }
package/lib/index.js CHANGED
@@ -325,8 +325,10 @@ function getAllData(srv, iface, callback) {
325
325
  function get(valueObject, callback) {
326
326
  return new Promise((resolve) => {
327
327
  process.nextTick(() => {
328
+ const blocked = ['get', 'getStaticData', 'getDynamicData', 'getAllData', 'observe', 'powerShellStart', 'powerShellRelease'];
329
+ const isGettable = (key) => ({}).hasOwnProperty.call(exports, key) && typeof exports[key] === 'function' && typeof valueObject[key] === 'string' && blocked.indexOf(key) < 0;
328
330
  const allPromises = Object.keys(valueObject)
329
- .filter((func) => ({}).hasOwnProperty.call(exports, func))
331
+ .filter((func) => isGettable(func))
330
332
  .map((func) => {
331
333
  const params = valueObject[func].substring(valueObject[func].lastIndexOf('(') + 1, valueObject[func].lastIndexOf(')'));
332
334
  let funcWithoutParams = func.indexOf(')') >= 0 ? func.split(')')[1].trim() : func;
@@ -342,7 +344,7 @@ function get(valueObject, callback) {
342
344
  const result = {};
343
345
  let i = 0;
344
346
  for (let key in valueObject) {
345
- if ({}.hasOwnProperty.call(valueObject, key) && {}.hasOwnProperty.call(exports, key) && data.length > i) {
347
+ if ({}.hasOwnProperty.call(valueObject, key) && isGettable(key) && data.length > i) {
346
348
  if (valueObject[key] === '*' || valueObject[key] === 'all') {
347
349
  result[key] = data[i];
348
350
  } else {
package/lib/memory.js CHANGED
@@ -401,6 +401,7 @@ function memLayout(callback) {
401
401
  ecc: null,
402
402
  clockSpeed: 0,
403
403
  formFactor: util.getValue(lines, 'Form Factor:'),
404
+ manufacturer: '',
404
405
  partNum: '',
405
406
  serialNum: '',
406
407
  voltageConfigured: null,
@@ -490,10 +491,12 @@ function memLayout(callback) {
490
491
  devices.forEach((device) => {
491
492
  const lines = device.split('\n');
492
493
  const bank = (hasBank ? 'BANK ' : 'DIMM') + lines[0].trim().split('/')[0];
493
- const size = parseInt(util.getValue(lines, ' Size'));
494
+ const sizeString = util.getValue(lines, ' Size');
495
+ const size = parseInt(sizeString);
496
+ const sizeUnit = sizeString.toLowerCase().indexOf('mb') >= 0 ? 1024 * 1024 : sizeString.toLowerCase().indexOf('tb') >= 0 ? 1024 * 1024 * 1024 * 1024 : 1024 * 1024 * 1024;
494
497
  if (size) {
495
498
  result.push({
496
- size: size * 1024 * 1024 * 1024,
499
+ size: size * sizeUnit,
497
500
  bank: bank,
498
501
  type: util.getValue(lines, ' Type:'),
499
502
  ecc: eccStatus ? eccStatus === 'enabled' : null,
@@ -586,7 +589,7 @@ function memLayout(callback) {
586
589
  result.push({
587
590
  size,
588
591
  bank: util.getValue(lines, 'BankLabel', ':') + (tagInt[1] ? '/' + tagInt[1] : ''), // BankLabel
589
- type: memoryTypes[parseInt(util.getValue(lines, 'MemoryType', ':'), 10) || parseInt(util.getValue(lines, 'SMBIOSMemoryType', ':'), 10)],
592
+ type: memoryTypes[parseInt(util.getValue(lines, 'MemoryType', ':'), 10) || parseInt(util.getValue(lines, 'SMBIOSMemoryType', ':'), 10) || 0],
590
593
  ecc: dataWidth && totalWidth ? totalWidth > dataWidth : false,
591
594
  clockSpeed: parseInt(util.getValue(lines, 'ConfiguredClockSpeed', ':'), 10) || parseInt(util.getValue(lines, 'Speed', ':'), 10) || 0,
592
595
  formFactor: FormFactors[parseInt(util.getValue(lines, 'FormFactor', ':'), 10) || 0],
package/lib/network.js CHANGED
@@ -546,8 +546,12 @@ function getLinuxIfaceConnectionName(interfaceName) {
546
546
  }
547
547
  }
548
548
 
549
- function checkLinuxDCHPInterfaces(file) {
549
+ function checkLinuxDCHPInterfaces(file, depth) {
550
550
  let result = [];
551
+ depth = depth || 0;
552
+ if (depth > 10) {
553
+ return result;
554
+ }
551
555
  try {
552
556
  const content = readFileSync(file, { encoding: 'utf8' });
553
557
  const lines = content.split('\n').filter((l) => /iface|source/.test(l));
@@ -561,7 +565,7 @@ function checkLinuxDCHPInterfaces(file) {
561
565
  }
562
566
  if (line.toLowerCase().includes('source')) {
563
567
  const file = line.split(' ')[1];
564
- result = result.concat(checkLinuxDCHPInterfaces(file));
568
+ result = result.concat(checkLinuxDCHPInterfaces(file, depth + 1));
565
569
  }
566
570
  });
567
571
  } catch {
@@ -1426,7 +1430,7 @@ function networkStatsSingle(iface) {
1426
1430
  rx_errors = rx_errors + parseInt(line[5]);
1427
1431
  }
1428
1432
  tx_bytes = tx_bytes + parseInt(line[10]);
1429
- if (line[12].trim() !== '-') {
1433
+ if (line[12] && line[12].trim() !== '-') {
1430
1434
  tx_dropped = tx_dropped + parseInt(line[12]);
1431
1435
  }
1432
1436
  if (line[9].trim() !== '-') {
@@ -1725,10 +1729,10 @@ function networkConnections(callback) {
1725
1729
  const hasState = states.indexOf(line[5]) >= 0;
1726
1730
  const connstate = hasState ? line[5] : 'UNKNOWN';
1727
1731
  let pidField = '';
1728
- if (line[line.length - 9].indexOf(':') >= 0) {
1732
+ if (line[line.length - 9] && line[line.length - 9].indexOf(':') >= 0) {
1729
1733
  pidField = line[line.length - 9].split(':')[1];
1730
1734
  } else {
1731
- pidField = line[pidPos + (hasState ? 0 : -1)];
1735
+ pidField = line[pidPos + (hasState ? 0 : -1)] || '';
1732
1736
 
1733
1737
  if (pidField.indexOf(':') >= 0) {
1734
1738
  pidField = pidField.split(':')[1];
@@ -1754,6 +1758,11 @@ function networkConnections(callback) {
1754
1758
  }
1755
1759
  resolve(result);
1756
1760
  });
1761
+ } else {
1762
+ if (callback) {
1763
+ callback(result);
1764
+ }
1765
+ resolve(result);
1757
1766
  }
1758
1767
  });
1759
1768
  }
@@ -1844,6 +1853,11 @@ function networkConnections(callback) {
1844
1853
  callback(result);
1845
1854
  }
1846
1855
  resolve(result);
1856
+ } else {
1857
+ if (callback) {
1858
+ callback(result);
1859
+ }
1860
+ resolve(result);
1847
1861
  }
1848
1862
  });
1849
1863
  } catch {