@depup/systeminformation 5.31.4-depup.0 → 5.33.1-depup.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,14 +341,26 @@ 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
  }
339
348
  if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('region') !== -1 && parts[1].toLowerCase().indexOf('memory') !== -1) {
340
- let memparts = parts[1].split('=');
341
- if (memparts.length > 1) {
342
- currentController.vram = parseInt(memparts[1]);
349
+ const sizeMatch = parts[1].match(/size=(\d+)([KMG])?/i);
350
+ if (sizeMatch) {
351
+ let vram = parseInt(sizeMatch[1], 10);
352
+ const unit = (sizeMatch[2] || '').toUpperCase();
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
360
+ // keep the largest memory region (the actual framebuffer aperture)
361
+ if (currentController.vram === null || vram > currentController.vram) {
362
+ currentController.vram = vram;
363
+ }
343
364
  }
344
365
  }
345
366
  }
@@ -366,7 +387,7 @@ function graphics(callback) {
366
387
  }
367
388
  return devices;
368
389
  }, {});
369
- for (let deviceId in devices) {
390
+ for (const deviceId in devices) {
370
391
  const device = devices[deviceId];
371
392
  if (device['CL_DEVICE_TYPE'] === 'CL_DEVICE_TYPE_GPU') {
372
393
  let busAddress;
@@ -421,24 +442,29 @@ function graphics(callback) {
421
442
 
422
443
  if (_windows) {
423
444
  try {
424
- const basePath = path.join(util.WINDIR, 'System32', 'DriverStore', 'FileRepository');
425
- // find all directories that have an nvidia-smi.exe file with date
426
- const candidates = fs
427
- .readdirSync(basePath, { withFileTypes: true })
428
- .filter((dir) => dir.isDirectory())
429
- .map((dir) => {
430
- const nvidiaSmiPath = path.join(basePath, dir.name, 'nvidia-smi.exe');
431
- try {
432
- const stats = fs.statSync(nvidiaSmiPath);
433
- return { path: nvidiaSmiPath, ctime: stats.ctimeMs };
434
- } catch {
435
- return null;
436
- }
437
- })
438
- .filter(Boolean);
439
- if (candidates.length > 0) {
440
- // take the most recent
441
- _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
+ }
442
468
  }
443
469
  } catch {
444
470
  util.noop();
@@ -451,7 +477,7 @@ function graphics(callback) {
451
477
 
452
478
  function nvidiaSmi(options) {
453
479
  const nvidiaSmiExe = getNvidiaSmi();
454
- options = options || util.execOptsWin;
480
+ options = Object.assign({}, options || util.execOptsWin);
455
481
  if (nvidiaSmiExe) {
456
482
  const nvidiaSmiOpts =
457
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';
@@ -661,11 +687,26 @@ function graphics(callback) {
661
687
  let is_current = false;
662
688
  let edid_raw = '';
663
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
+ };
664
701
  for (let i = 1; i < lines.length; i++) {
665
702
  // start with second line
666
703
  if ('' !== lines[i].trim()) {
667
704
  if (' ' !== lines[i][0] && '\t' !== lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') !== -1) {
668
705
  // first line of new entry
706
+ if (is_edid && edid_raw) {
707
+ // pending EDID belongs to the previous display
708
+ applyEdid();
709
+ }
669
710
  if (
670
711
  currentDisplay.model ||
671
712
  currentDisplay.main ||
@@ -695,10 +736,15 @@ function graphics(callback) {
695
736
  currentRefreshRate: null
696
737
  };
697
738
  }
698
- let parts = lines[i].split(' ');
739
+ const parts = lines[i].split(' ');
699
740
  currentDisplay.connection = parts[0];
700
741
  currentDisplay.main = lines[i].toLowerCase().indexOf(' primary ') >= 0;
701
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
+ }
702
748
  }
703
749
 
704
750
  // try to read EDID information
@@ -707,19 +753,12 @@ function graphics(callback) {
707
753
  edid_raw += lines[i].toLowerCase().trim();
708
754
  } else {
709
755
  // parsen EDID
710
- let edid_decoded = parseLinesLinuxEdid(edid_raw);
711
- currentDisplay.vendor = edid_decoded.vendor;
712
- currentDisplay.model = edid_decoded.model;
713
- currentDisplay.resolutionX = edid_decoded.resolutionX;
714
- currentDisplay.resolutionY = edid_decoded.resolutionY;
715
- currentDisplay.sizeX = edid_decoded.sizeX;
716
- currentDisplay.sizeY = edid_decoded.sizeY;
717
- currentDisplay.pixelDepth = depth;
718
- is_edid = false;
756
+ applyEdid();
719
757
  }
720
758
  }
721
759
  if (lines[i].toLowerCase().indexOf('edid:') >= 0) {
722
760
  is_edid = true;
761
+ edid_raw = '';
723
762
  start = lines[i].search(/\S|$/);
724
763
  }
725
764
  if (lines[i].toLowerCase().indexOf('*current') >= 0) {
@@ -742,6 +781,10 @@ function graphics(callback) {
742
781
  }
743
782
 
744
783
  // pushen displays
784
+ if (is_edid && edid_raw) {
785
+ // EDID was the last block in the output
786
+ applyEdid();
787
+ }
745
788
  if (
746
789
  currentDisplay.model ||
747
790
  currentDisplay.main ||
@@ -774,6 +817,20 @@ function graphics(callback) {
774
817
  } catch (e) {
775
818
  util.noop();
776
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
+ }
777
834
  try {
778
835
  stdout = execSync(
779
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 ""',
@@ -826,7 +883,7 @@ function graphics(callback) {
826
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;";
827
884
  exec(cmd, (error, stdout) => {
828
885
  const lines = stdout.toString().split('\n');
829
- 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) {
830
887
  const parts = lines[0].replace('mode', '').replace(/"/g, '').trim().split('x');
831
888
  if (parts.length === 2) {
832
889
  result.displays.push({
@@ -848,12 +905,12 @@ function graphics(callback) {
848
905
  });
849
906
  }
850
907
  }
851
- if (lines.length >= 1 && stdout.toString().indexOf('gpu=') >= -1) {
908
+ if (lines.length >= 1 && stdout.toString().indexOf('gpu=') >= 0) {
852
909
  result.controllers.push({
853
910
  vendor: 'Broadcom',
854
911
  model: util.getRpiGpu(),
855
912
  bus: '',
856
- vram: util.getValue(lines, 'gpu', '=').replace('M', ''),
913
+ vram: parseInt(util.getValue(lines, 'gpu', '=').replace('M', ''), 10) || null,
857
914
  vramDynamic: true
858
915
  });
859
916
  }
@@ -871,7 +928,7 @@ function graphics(callback) {
871
928
  // needs to be rewritten ... using no spread operators
872
929
  result.controllers = result.controllers.map((controller) => {
873
930
  // match by busAddress
874
- 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())) || {});
875
932
  });
876
933
  }
877
934
  }
@@ -933,9 +990,10 @@ function graphics(callback) {
933
990
  workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorConnectionParams | fl'));
934
991
  workload.push(
935
992
  util.powerShell(
936
- '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}'
937
994
  )
938
995
  );
996
+ workload.push(util.powerShell(psCurrentModes));
939
997
 
940
998
  const nvidiaData = nvidiaDevices();
941
999
 
@@ -952,7 +1010,7 @@ function graphics(callback) {
952
1010
  controller,
953
1011
  nvidiaData.find((device) => {
954
1012
  let windowsSubDeviceId = (controller.subDeviceId || '').toLowerCase();
955
- const nvidiaSubDeviceIdParts = device.subDeviceId.split('x');
1013
+ const nvidiaSubDeviceIdParts = (device.subDeviceId || '').split('x');
956
1014
  let nvidiaSubDeviceId = nvidiaSubDeviceIdParts.length > 1 ? nvidiaSubDeviceIdParts[1].toLowerCase() : nvidiaSubDeviceIdParts[0].toLowerCase();
957
1015
  const lengthDifference = Math.abs(windowsSubDeviceId.length - nvidiaSubDeviceId.length);
958
1016
  if (windowsSubDeviceId.length > nvidiaSubDeviceId.length) {
@@ -982,35 +1040,65 @@ function graphics(callback) {
982
1040
  dsections.pop();
983
1041
  }
984
1042
 
985
- // monitor (powershell)
986
- const msections = data[3].replace(/\r/g, '').split('Active ');
987
- 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
+ });
988
1058
 
989
1059
  // forms.screens (powershell)
990
1060
  const ssections = data[4].replace(/\r/g, '').split('BitsPerPixel ');
991
1061
  ssections.shift();
992
1062
 
993
- // connection params (powershell) - video type
994
- const tsections = data[5].replace(/\r/g, '').split(/\n\s*\n/);
995
- 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
+ });
996
1072
 
997
1073
  // monitor ID (powershell) - model / vendor
998
1074
  const res = data[6].replace(/\r/g, '').split(/\n/);
999
1075
  const isections = [];
1000
1076
  res.forEach((element) => {
1001
1077
  const parts = element.split('|');
1002
- if (parts.length === 5) {
1078
+ if (parts.length === 6) {
1003
1079
  isections.push({
1004
1080
  vendor: parts[0],
1005
1081
  code: parts[1],
1006
1082
  model: parts[2],
1007
1083
  serial: parts[3],
1008
- instanceId: parts[4]
1084
+ productionYear: util.toInt(parts[4]) || null,
1085
+ instanceId: parts[5]
1009
1086
  });
1010
1087
  }
1011
1088
  });
1012
1089
 
1013
- 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);
1014
1102
 
1015
1103
  if (result.displays.length === 1) {
1016
1104
  if (_resolutionX) {
@@ -1149,7 +1237,7 @@ function graphics(callback) {
1149
1237
  return controllers;
1150
1238
  }
1151
1239
 
1152
- function parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections) {
1240
+ function parseLinesWindowsDisplaysPowershell(ssections, monitors, dsections, connections, isections, currentModes) {
1153
1241
  const displays = [];
1154
1242
  let vendor = '';
1155
1243
  let model = '';
@@ -1164,40 +1252,34 @@ function graphics(callback) {
1164
1252
  resolutionX = util.toInt(util.getValue(linesDisplay, 'ScreenWidth', ':'));
1165
1253
  resolutionY = util.toInt(util.getValue(linesDisplay, 'ScreenHeight', ':'));
1166
1254
  }
1167
- for (let i = 0; i < ssections.length; i++) {
1168
- if (ssections[i].trim() !== '') {
1169
- ssections[i] = 'BitsPerPixel ' + ssections[i];
1170
- msections[i] = 'Active ' + msections[i];
1171
- // tsections can be empty OR undefined on earlier versions of powershell (<=2.0)
1172
- // Tag connection type as UNKNOWN by default if this information is missing
1173
- if (tsections.length === 0 || tsections[i] === undefined) {
1174
- tsections[i] = 'Unknown';
1175
- }
1176
- const linesScreen = ssections[i].split('\n');
1177
- const linesMonitor = msections[i].split('\n');
1178
-
1179
- const linesConnection = tsections[i].split('\n');
1180
- 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;
1181
1267
  const bounds = util.getValue(linesScreen, 'Bounds').replace('{', '').replace('}', '').replace(/=/g, ':').split(',');
1182
1268
  const primary = util.getValue(linesScreen, 'Primary');
1183
- const sizeX = util.getValue(linesMonitor, 'MaxHorizontalImageSize');
1184
- const sizeY = util.getValue(linesMonitor, 'MaxVerticalImageSize');
1185
- const instanceName = util.getValue(linesMonitor, 'InstanceName').toLowerCase();
1186
- 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] : '';
1187
1272
  const deviceName = util.getValue(linesScreen, 'DeviceName');
1188
- let displayVendor = '';
1189
- let displayModel = '';
1190
- isections.forEach((element) => {
1191
- if (element.instanceId.toLowerCase().startsWith(instanceName) && vendor.startsWith('(') && model.startsWith('PnP')) {
1192
- displayVendor = element.vendor;
1193
- displayModel = element.model;
1194
- }
1195
- });
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;
1196
1275
  displays.push({
1197
- vendor: instanceName.startsWith(deviceID) && displayVendor === '' ? vendor : displayVendor,
1198
- 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,
1199
1281
  deviceName,
1200
- main: primary.toLowerCase() === 'true',
1282
+ main: hasOwnScreen ? primary.toLowerCase() === 'true' : false,
1201
1283
  builtin: videoOutputTechnology === '2147483648',
1202
1284
  connection: videoOutputTechnology && videoTypes[videoOutputTechnology] ? videoTypes[videoOutputTechnology] : '',
1203
1285
  resolutionX: util.toInt(util.getValue(bounds, 'Width', ':')),
@@ -1208,7 +1290,8 @@ function graphics(callback) {
1208
1290
  currentResX: util.toInt(util.getValue(bounds, 'Width', ':')),
1209
1291
  currentResY: util.toInt(util.getValue(bounds, 'Height', ':')),
1210
1292
  positionX: util.toInt(util.getValue(bounds, 'X', ':')),
1211
- positionY: util.toInt(util.getValue(bounds, 'Y', ':'))
1293
+ positionY: util.toInt(util.getValue(bounds, 'Y', ':')),
1294
+ currentRefreshRate: currentModes[deviceName.toLowerCase()] || null
1212
1295
  });
1213
1296
  }
1214
1297
  }
@@ -1216,6 +1299,9 @@ function graphics(callback) {
1216
1299
  displays.push({
1217
1300
  vendor,
1218
1301
  model,
1302
+ serial: null,
1303
+ productionYear: null,
1304
+ displayId: null,
1219
1305
  main: true,
1220
1306
  sizeX: null,
1221
1307
  sizeY: null,
package/lib/index.d.ts CHANGED
@@ -5,7 +5,6 @@
5
5
  /// <reference types="node" />
6
6
 
7
7
  export namespace Systeminformation {
8
-
9
8
  // 1. General
10
9
 
11
10
  interface TimeData {
@@ -225,7 +224,7 @@ export namespace Systeminformation {
225
224
  nvme_pci_vendor?: {
226
225
  id: number;
227
226
  subsystem_id: number;
228
- },
227
+ };
229
228
  nvme_smart_health_information_log?: {
230
229
  critical_warning?: number;
231
230
  temperature?: number;
@@ -245,11 +244,11 @@ export namespace Systeminformation {
245
244
  warning_temp_time?: number;
246
245
  critical_comp_time?: number;
247
246
  temperature_sensors?: number[];
248
- },
247
+ };
249
248
  user_capacity?: {
250
249
  blocks: number;
251
250
  bytes: number;
252
- },
251
+ };
253
252
  logical_block_size?: number;
254
253
  temperature: {
255
254
  current: number;
@@ -758,10 +757,12 @@ export namespace Systeminformation {
758
757
  createdAt: string;
759
758
  startedAt: string;
760
759
  finishedAt: string;
760
+ status: string;
761
761
  state: string;
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
  }
@@ -996,10 +997,7 @@ export function networkInterfaces(): Promise<Systeminformation.NetworkInterfaces
996
997
  export function networkInterfaces(defaultString: 'default'): Promise<Systeminformation.NetworkInterfacesData>;
997
998
  export function networkInterfaces(rescan: boolean): Promise<Systeminformation.NetworkInterfacesData[]>;
998
999
  export function networkInterfaces(
999
- cb:
1000
- | ((data: Systeminformation.NetworkInterfacesData[] | Systeminformation.NetworkInterfacesData) => any)
1001
- | boolean
1002
- | string,
1000
+ cb: ((data: Systeminformation.NetworkInterfacesData[] | Systeminformation.NetworkInterfacesData) => any) | boolean | string,
1003
1001
  rescan?: boolean,
1004
1002
  defaultString?: string
1005
1003
  ): Promise<Systeminformation.NetworkInterfacesData[] | Systeminformation.NetworkInterfacesData>;
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
@@ -258,13 +258,19 @@ function mem(callback) {
258
258
  util.noop();
259
259
  }
260
260
  try {
261
- exec('vm_stat 2>/dev/null | egrep "Pages active|Pages inactive"', (error, stdout) => {
261
+ exec('vm_stat 2>/dev/null | egrep "Pages active|Pages inactive|Pages speculative|Pages wired down|Pages occupied by compressor|Pages purgeable|File-backed pages|Anonymous pages"', (error, stdout) => {
262
262
  if (!error) {
263
263
  let lines = stdout.toString().split('\n');
264
- result.active = (parseInt(util.getValue(lines, 'Pages active'), 10) || 0) * pageSize;
264
+ const wired = (parseInt(util.getValue(lines, 'Pages wired down'), 10) || 0) * pageSize;
265
+ const compressed = (parseInt(util.getValue(lines, 'Pages occupied by compressor'), 10) || 0) * pageSize;
266
+ const purgeable = (parseInt(util.getValue(lines, 'Pages purgeable'), 10) || 0) * pageSize;
267
+ const anonymous = (parseInt(util.getValue(lines, 'Anonymous pages'), 10) || 0) * pageSize;
268
+
269
+ result.active = anonymous - purgeable + wired + compressed;
265
270
  result.reclaimable = (parseInt(util.getValue(lines, 'Pages inactive'), 10) || 0) * pageSize;
266
271
  result.buffcache = result.used - result.active;
267
272
  result.available = result.free + result.buffcache;
273
+
268
274
  }
269
275
  exec('sysctl -n vm.swapusage 2>/dev/null', (error, stdout) => {
270
276
  if (!error) {
@@ -395,6 +401,7 @@ function memLayout(callback) {
395
401
  ecc: null,
396
402
  clockSpeed: 0,
397
403
  formFactor: util.getValue(lines, 'Form Factor:'),
404
+ manufacturer: '',
398
405
  partNum: '',
399
406
  serialNum: '',
400
407
  voltageConfigured: null,
@@ -484,10 +491,12 @@ function memLayout(callback) {
484
491
  devices.forEach((device) => {
485
492
  const lines = device.split('\n');
486
493
  const bank = (hasBank ? 'BANK ' : 'DIMM') + lines[0].trim().split('/')[0];
487
- 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;
488
497
  if (size) {
489
498
  result.push({
490
- size: size * 1024 * 1024 * 1024,
499
+ size: size * sizeUnit,
491
500
  bank: bank,
492
501
  type: util.getValue(lines, ' Type:'),
493
502
  ecc: eccStatus ? eccStatus === 'enabled' : null,
@@ -580,7 +589,7 @@ function memLayout(callback) {
580
589
  result.push({
581
590
  size,
582
591
  bank: util.getValue(lines, 'BankLabel', ':') + (tagInt[1] ? '/' + tagInt[1] : ''), // BankLabel
583
- 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],
584
593
  ecc: dataWidth && totalWidth ? totalWidth > dataWidth : false,
585
594
  clockSpeed: parseInt(util.getValue(lines, 'ConfiguredClockSpeed', ':'), 10) || parseInt(util.getValue(lines, 'Speed', ':'), 10) || 0,
586
595
  formFactor: FormFactors[parseInt(util.getValue(lines, 'FormFactor', ':'), 10) || 0],