fix GPU getNvidiaSmi directory tracing

This commit is contained in:
Sebastian Hildebrandt 2025-12-23 07:09:28 +01:00
parent 2677324976
commit 1a9f434335

View File

@ -21,13 +21,13 @@ const util = require('./util');
let _platform = process.platform;
let _nvidiaSmiPath = '';
const _linux = (_platform === 'linux' || _platform === 'android');
const _darwin = (_platform === 'darwin');
const _windows = (_platform === 'win32');
const _freebsd = (_platform === 'freebsd');
const _openbsd = (_platform === 'openbsd');
const _netbsd = (_platform === 'netbsd');
const _sunos = (_platform === 'sunos');
const _linux = _platform === 'linux' || _platform === 'android';
const _darwin = _platform === 'darwin';
const _windows = _platform === 'win32';
const _freebsd = _platform === 'freebsd';
const _openbsd = _platform === 'openbsd';
const _netbsd = _platform === 'netbsd';
const _sunos = _platform === 'sunos';
let _resolutionX = 0;
let _resolutionY = 0;
@ -37,22 +37,22 @@ let _refreshRate = 0;
const videoTypes = {
'-2': 'UNINITIALIZED',
'-1': 'OTHER',
'0': 'HD15',
'1': 'SVIDEO',
'2': 'Composite video',
'3': 'Component video',
'4': 'DVI',
'5': 'HDMI',
'6': 'LVDS',
'8': 'D_JPN',
'9': 'SDI',
'10': 'DP',
'11': 'DP embedded',
'12': 'UDI',
'13': 'UDI embedded',
'14': 'SDTVDONGLE',
'15': 'MIRACAST',
'2147483648': 'INTERNAL'
0: 'HD15',
1: 'SVIDEO',
2: 'Composite video',
3: 'Component video',
4: 'DVI',
5: 'HDMI',
6: 'LVDS',
8: 'D_JPN',
9: 'SDI',
10: 'DP',
11: 'DP embedded',
12: 'UDI',
13: 'UDI embedded',
14: 'SDTVDONGLE',
15: 'MIRACAST',
2147483648: 'INTERNAL'
};
function getVendorFromModel(model) {
@ -77,7 +77,7 @@ function getVendorFromModel(model) {
{ pattern: 'APPLE.?', manufacturer: 'Apple' },
{ pattern: 'INTEL.?', manufacturer: 'Intel' },
{ pattern: 'AMD.?', manufacturer: 'AMD' },
{ pattern: 'NVIDIA.?', manufacturer: 'NVDIA' },
{ pattern: 'NVIDIA.?', manufacturer: 'NVDIA' }
];
let result = '';
@ -85,7 +85,9 @@ function getVendorFromModel(model) {
model = model.toUpperCase();
manufacturers.forEach((manufacturer) => {
const re = RegExp(manufacturer.pattern);
if (re.test(model)) { result = manufacturer.manufacturer; }
if (re.test(model)) {
result = manufacturer.manufacturer;
}
});
}
return result;
@ -93,11 +95,11 @@ function getVendorFromModel(model) {
function getVendorFromId(id) {
const vendors = {
'610': 'Apple',
610: 'Apple',
'1e6d': 'LG',
'10ac': 'DELL',
'4dd9': 'Sony',
'38a3': 'NEC',
'38a3': 'NEC'
};
return vendors[id] || '';
}
@ -105,47 +107,51 @@ function getVendorFromId(id) {
function vendorToId(str) {
let result = '';
str = (str || '').toLowerCase();
if (str.indexOf('apple') >= 0) { result = '0x05ac'; }
else if (str.indexOf('nvidia') >= 0) { result = '0x10de'; }
else if (str.indexOf('intel') >= 0) { result = '0x8086'; }
else if (str.indexOf('ati') >= 0 || str.indexOf('amd') >= 0) { result = '0x1002'; }
if (str.indexOf('apple') >= 0) {
result = '0x05ac';
} else if (str.indexOf('nvidia') >= 0) {
result = '0x10de';
} else if (str.indexOf('intel') >= 0) {
result = '0x8086';
} else if (str.indexOf('ati') >= 0 || str.indexOf('amd') >= 0) {
result = '0x1002';
}
return result;
}
function getMetalVersion(id) {
const families = {
'spdisplays_mtlgpufamilymac1': 'mac1',
'spdisplays_mtlgpufamilymac2': 'mac2',
'spdisplays_mtlgpufamilyapple1': 'apple1',
'spdisplays_mtlgpufamilyapple2': 'apple2',
'spdisplays_mtlgpufamilyapple3': 'apple3',
'spdisplays_mtlgpufamilyapple4': 'apple4',
'spdisplays_mtlgpufamilyapple5': 'apple5',
'spdisplays_mtlgpufamilyapple6': 'apple6',
'spdisplays_mtlgpufamilyapple7': 'apple7',
'spdisplays_metalfeaturesetfamily11': 'family1_v1',
'spdisplays_metalfeaturesetfamily12': 'family1_v2',
'spdisplays_metalfeaturesetfamily13': 'family1_v3',
'spdisplays_metalfeaturesetfamily14': 'family1_v4',
'spdisplays_metalfeaturesetfamily21': 'family2_v1'
spdisplays_mtlgpufamilymac1: 'mac1',
spdisplays_mtlgpufamilymac2: 'mac2',
spdisplays_mtlgpufamilyapple1: 'apple1',
spdisplays_mtlgpufamilyapple2: 'apple2',
spdisplays_mtlgpufamilyapple3: 'apple3',
spdisplays_mtlgpufamilyapple4: 'apple4',
spdisplays_mtlgpufamilyapple5: 'apple5',
spdisplays_mtlgpufamilyapple6: 'apple6',
spdisplays_mtlgpufamilyapple7: 'apple7',
spdisplays_metalfeaturesetfamily11: 'family1_v1',
spdisplays_metalfeaturesetfamily12: 'family1_v2',
spdisplays_metalfeaturesetfamily13: 'family1_v3',
spdisplays_metalfeaturesetfamily14: 'family1_v4',
spdisplays_metalfeaturesetfamily21: 'family2_v1'
};
return families[id] || '';
}
function graphics(callback) {
function parseLinesDarwin(graphicsArr) {
const res = {
controllers: [],
displays: []
};
try {
graphicsArr.forEach(function (item) {
graphicsArr.forEach( (item) => {
// controllers
const bus = ((item.sppci_bus || '').indexOf('builtin') > -1 ? 'Built-In' : ((item.sppci_bus || '').indexOf('pcie') > -1 ? 'PCIe' : ''));
const vram = (parseInt((item.spdisplays_vram || ''), 10) || 0) * (((item.spdisplays_vram || '').indexOf('GB') > -1) ? 1024 : 1);
const vramDyn = (parseInt((item.spdisplays_vram_shared || ''), 10) || 0) * (((item.spdisplays_vram_shared || '').indexOf('GB') > -1) ? 1024 : 1);
const bus = (item.sppci_bus || '').indexOf('builtin') > -1 ? 'Built-In' : (item.sppci_bus || '').indexOf('pcie') > -1 ? 'PCIe' : '';
const vram = (parseInt(item.spdisplays_vram || '', 10) || 0) * ((item.spdisplays_vram || '').indexOf('GB') > -1 ? 1024 : 1);
const vramDyn = (parseInt(item.spdisplays_vram_shared || '', 10) || 0) * ((item.spdisplays_vram_shared || '').indexOf('GB') > -1 ? 1024 : 1);
let metalVersion = getMetalVersion(item.spdisplays_metal || item.spdisplays_metalfamily || '');
res.controllers.push({
vendor: getVendorFromModel(item.spdisplays_vendor || '') || item.spdisplays_vendor || '',
@ -155,14 +161,14 @@ function graphics(callback) {
vram: vram || vramDyn || null,
deviceId: item['spdisplays_device-id'] || '',
vendorId: item['spdisplays_vendor-id'] || vendorToId((item['spdisplays_vendor'] || '') + (item.sppci_model || '')),
external: (item.sppci_device_type === 'spdisplays_egpu'),
external: item.sppci_device_type === 'spdisplays_egpu',
cores: item['sppci_cores'] || null,
metalVersion
});
// displays
if (item.spdisplays_ndrvs && item.spdisplays_ndrvs.length) {
item.spdisplays_ndrvs.forEach(function (displayItem) {
item.spdisplays_ndrvs.forEach( (displayItem) => {
const connectionType = displayItem['spdisplays_connection_type'] || '';
const currentResolutionParts = (displayItem['_spdisplays_resolution'] || '').split('@');
const currentResolution = currentResolutionParts[0].split('x');
@ -178,18 +184,17 @@ function graphics(callback) {
displayId: displayItem['_spdisplays_displayID'] || null,
main: displayItem['spdisplays_main'] ? displayItem['spdisplays_main'] === 'spdisplays_yes' : false,
builtin: (displayItem['spdisplays_display_type'] || '').indexOf('built-in') > -1,
connection: ((connectionType.indexOf('_internal') > -1) ? 'Internal' : ((connectionType.indexOf('_displayport') > -1) ? 'Display Port' : ((connectionType.indexOf('_hdmi') > -1) ? 'HDMI' : null))),
connection: connectionType.indexOf('_internal') > -1 ? 'Internal' : connectionType.indexOf('_displayport') > -1 ? 'Display Port' : connectionType.indexOf('_hdmi') > -1 ? 'HDMI' : null,
sizeX: null,
sizeY: null,
pixelDepth: (pixelDepthString === 'CGSThirtyBitColor' ? 30 : (pixelDepthString === 'CGSThirtytwoBitColor' ? 32 : (pixelDepthString === 'CGSTwentyfourBitColor' ? 24 : null))),
pixelDepth: pixelDepthString === 'CGSThirtyBitColor' ? 30 : pixelDepthString === 'CGSThirtytwoBitColor' ? 32 : pixelDepthString === 'CGSTwentyfourBitColor' ? 24 : null,
resolutionX: pixelParts.length > 1 ? parseInt(pixelParts[0], 10) : null,
resolutionY: pixelParts.length > 1 ? parseInt(pixelParts[1], 10) : null,
currentResX: currentResolution.length > 1 ? parseInt(currentResolution[0], 10) : null,
currentResY: currentResolution.length > 1 ? parseInt(currentResolution[1], 10) : null,
positionX: 0,
positionY: 0,
currentRefreshRate: currentResolutionParts.length > 1 ? parseInt(currentResolutionParts[1], 10) : null,
currentRefreshRate: currentResolutionParts.length > 1 ? parseInt(currentResolutionParts[1], 10) : null
});
});
}
@ -229,22 +234,26 @@ function graphics(callback) {
let i = 1;
lines.forEach((line) => {
let subsystem = '';
if (i < lines.length && lines[i]) { // get next line;
if (i < lines.length && lines[i]) {
// get next line;
subsystem = lines[i];
if (subsystem.indexOf(':') > 0) {
subsystem = subsystem.split(':')[1];
}
}
if ('' !== line.trim()) {
if (' ' !== line[0] && '\t' !== line[0]) { // first line of new entry
let isExternal = (pciIDs.indexOf(line.split(' ')[0]) >= 0);
if (' ' !== line[0] && '\t' !== line[0]) {
// first line of new entry
let isExternal = pciIDs.indexOf(line.split(' ')[0]) >= 0;
let vgapos = line.toLowerCase().indexOf(' vga ');
let _3dcontrollerpos = line.toLowerCase().indexOf('3d controller');
if (vgapos !== -1 || _3dcontrollerpos !== -1) { // VGA
if (vgapos !== -1 || _3dcontrollerpos !== -1) {
// VGA
if (_3dcontrollerpos !== -1 && vgapos === -1) {
vgapos = _3dcontrollerpos;
}
if (currentController.vendor || currentController.model || currentController.bus || currentController.vram !== null || currentController.vramDynamic) { // already a controller found
if (currentController.vendor || currentController.model || currentController.bus || currentController.vram !== null || currentController.vramDynamic) {
// already a controller found
controllers.push(currentController);
currentController = {
vendor: '',
@ -252,7 +261,7 @@ function graphics(callback) {
bus: '',
busAddress: '',
vram: null,
vramDynamic: false,
vramDynamic: false
};
}
@ -268,28 +277,47 @@ function graphics(callback) {
parts[1] = parts[1].trim();
if (parts[1].toLowerCase().indexOf('corporation') >= 0) {
currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf('corporation') + 11).trim();
currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf('corporation') + 11, 200).split('(')[0].trim();
currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard';
currentController.model = parts[1]
.substr(parts[1].toLowerCase().indexOf('corporation') + 11, 200)
.split('(')[0]
.trim();
currentController.bus = pciIDs.length > 0 && isExternal ? 'PCIe' : 'Onboard';
currentController.vram = null;
currentController.vramDynamic = false;
} else if (parts[1].toLowerCase().indexOf(' inc.') >= 0) {
if ((parts[1].match(/]/g) || []).length > 1) {
currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim();
currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim();
currentController.model = parts[1]
.substr(parts[1].toLowerCase().indexOf(']') + 1, 200)
.trim()
.split('(')[0]
.trim();
} else {
currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' inc.') + 5).trim();
currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' inc.') + 5, 200).trim().split('(')[0].trim();
currentController.model = parts[1]
.substr(parts[1].toLowerCase().indexOf(' inc.') + 5, 200)
.trim()
.split('(')[0]
.trim();
}
currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard';
currentController.bus = pciIDs.length > 0 && isExternal ? 'PCIe' : 'Onboard';
currentController.vram = null;
currentController.vramDynamic = false;
} else if (parts[1].toLowerCase().indexOf(' ltd.') >= 0) {
if ((parts[1].match(/]/g) || []).length > 1) {
currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim();
currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim();
currentController.model = parts[1]
.substr(parts[1].toLowerCase().indexOf(']') + 1, 200)
.trim()
.split('(')[0]
.trim();
} else {
currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' ltd.') + 5).trim();
currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' ltd.') + 5, 200).trim().split('(')[0].trim();
currentController.model = parts[1]
.substr(parts[1].toLowerCase().indexOf(' ltd.') + 5, 200)
.trim()
.split('(')[0]
.trim();
}
}
if (currentController.model && subsystem.indexOf(currentController.model) !== -1) {
@ -299,14 +327,16 @@ function graphics(callback) {
}
}
}
} else {
isGraphicsController = false;
}
}
if (isGraphicsController) { // within VGA details
if (isGraphicsController) {
// within VGA details
let parts = line.split(':');
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('devicename') !== -1 && parts[1].toLowerCase().indexOf('onboard') !== -1) { currentController.bus = 'Onboard'; }
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('devicename') !== -1 && parts[1].toLowerCase().indexOf('onboard') !== -1) {
currentController.bus = 'Onboard';
}
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('region') !== -1 && parts[1].toLowerCase().indexOf('memory') !== -1) {
let memparts = parts[1].split('=');
if (memparts.length > 1) {
@ -318,10 +348,11 @@ function graphics(callback) {
i++;
});
if (currentController.vendor || currentController.model || currentController.bus || currentController.busAddress || currentController.vram !== null || currentController.vramDynamic) { // already a controller found
if (currentController.vendor || currentController.model || currentController.bus || currentController.busAddress || currentController.vram !== null || currentController.vramDynamic) {
// already a controller found
controllers.push(currentController);
}
return (controllers);
return controllers;
}
function parseLinesLinuxClinfo(controllers, lines) {
@ -356,7 +387,7 @@ function graphics(callback) {
}
}
if (busAddress) {
let controller = controllers.find(controller => controller.busAddress === busAddress);
let controller = controllers.find((controller) => controller.busAddress === busAddress);
if (!controller) {
controller = {
vendor: '',
@ -391,22 +422,27 @@ function graphics(callback) {
if (_windows) {
try {
const basePath = util.WINDIR + '\\System32\\DriverStore\\FileRepository';
const basePath = util.WINDIR + String.raw`\System32\DriverStore\FileRepository`;
// find all directories that have an nvidia-smi.exe file
const candidateDirs = fs.readdirSync(basePath).filter(dir => {
return fs.readdirSync([basePath, dir].join('/')).includes('nvidia-smi.exe');
const candidateDirs = fs.readdirSync(basePath).filter((dir) => {
if (fs.statSync([basePath, dir].join('/')).isDirectory()) {
return fs.readdirSync([basePath, dir].join('/')).includes('nvidia-smi.exe');
} else {
return false;
}
});
// use the directory with the most recently created nvidia-smi.exe file
const targetDir = candidateDirs.reduce((prevDir, currentDir) => {
const previousNvidiaSmi = fs.statSync([basePath, prevDir, 'nvidia-smi.exe'].join('/'));
const currentNvidiaSmi = fs.statSync([basePath, currentDir, 'nvidia-smi.exe'].join('/'));
return (previousNvidiaSmi.ctimeMs > currentNvidiaSmi.ctimeMs) ? prevDir : currentDir;
return previousNvidiaSmi.ctimeMs > currentNvidiaSmi.ctimeMs ? prevDir : currentDir;
});
if (targetDir) {
_nvidiaSmiPath = [basePath, targetDir, 'nvidia-smi.exe'].join('/');
}
} catch (e) {
} catch {
util.noop();
}
} else if (_linux) {
@ -419,7 +455,8 @@ function graphics(callback) {
const nvidiaSmiExe = getNvidiaSmi();
options = options || util.execOptsWin;
if (nvidiaSmiExe) {
const nvidiaSmiOpts = '--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';
const nvidiaSmiOpts =
'--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';
const cmd = nvidiaSmiExe + ' ' + nvidiaSmiOpts + (_linux ? ' 2>/dev/null' : '');
if (_linux) {
options.stdio = ['pipe', 'pipe', 'ignore'];
@ -428,7 +465,7 @@ function graphics(callback) {
const sanitized = util.sanitizeShellString(cmd);
const res = execSync(sanitized, options).toString();
return res;
} catch (e) {
} catch {
util.noop();
}
}
@ -436,7 +473,6 @@ function graphics(callback) {
}
function nvidiaDevices() {
function safeParseNumber(value) {
if ([null, undefined].includes(value)) {
return value;
@ -450,8 +486,8 @@ function graphics(callback) {
}
const gpus = stdout.split('\n').filter(Boolean);
let results = gpus.map(gpu => {
const splittedData = gpu.split(', ').map(value => value.includes('N/A') ? undefined : value);
let results = gpus.map((gpu) => {
const splittedData = gpu.split(', ').map((value) => (value.includes('N/A') ? undefined : value));
if (splittedData.length === 16) {
return {
driverVersion: splittedData[0],
@ -469,39 +505,69 @@ function graphics(callback) {
powerDraw: safeParseNumber(splittedData[12]),
powerLimit: safeParseNumber(splittedData[13]),
clockCore: safeParseNumber(splittedData[14]),
clockMemory: safeParseNumber(splittedData[15]),
clockMemory: safeParseNumber(splittedData[15])
};
} else {
return {};
}
});
results = results.filter((item) => {
return ('pciBus' in item);
return 'pciBus' in item;
});
return results;
}
function mergeControllerNvidia(controller, nvidia) {
if (nvidia.driverVersion) { controller.driverVersion = nvidia.driverVersion; }
if (nvidia.subDeviceId) { controller.subDeviceId = nvidia.subDeviceId; }
if (nvidia.name) { controller.name = nvidia.name; }
if (nvidia.pciBus) { controller.pciBus = nvidia.pciBus; }
if (nvidia.fanSpeed) { controller.fanSpeed = nvidia.fanSpeed; }
if (nvidia.driverVersion) {
controller.driverVersion = nvidia.driverVersion;
}
if (nvidia.subDeviceId) {
controller.subDeviceId = nvidia.subDeviceId;
}
if (nvidia.name) {
controller.name = nvidia.name;
}
if (nvidia.pciBus) {
controller.pciBus = nvidia.pciBus;
}
if (nvidia.fanSpeed) {
controller.fanSpeed = nvidia.fanSpeed;
}
if (nvidia.memoryTotal) {
controller.memoryTotal = nvidia.memoryTotal;
controller.vram = nvidia.memoryTotal;
controller.vramDynamic = false;
}
if (nvidia.memoryUsed) { controller.memoryUsed = nvidia.memoryUsed; }
if (nvidia.memoryFree) { controller.memoryFree = nvidia.memoryFree; }
if (nvidia.utilizationGpu) { controller.utilizationGpu = nvidia.utilizationGpu; }
if (nvidia.utilizationMemory) { controller.utilizationMemory = nvidia.utilizationMemory; }
if (nvidia.temperatureGpu) { controller.temperatureGpu = nvidia.temperatureGpu; }
if (nvidia.temperatureMemory) { controller.temperatureMemory = nvidia.temperatureMemory; }
if (nvidia.powerDraw) { controller.powerDraw = nvidia.powerDraw; }
if (nvidia.powerLimit) { controller.powerLimit = nvidia.powerLimit; }
if (nvidia.clockCore) { controller.clockCore = nvidia.clockCore; }
if (nvidia.clockMemory) { controller.clockMemory = nvidia.clockMemory; }
if (nvidia.memoryUsed) {
controller.memoryUsed = nvidia.memoryUsed;
}
if (nvidia.memoryFree) {
controller.memoryFree = nvidia.memoryFree;
}
if (nvidia.utilizationGpu) {
controller.utilizationGpu = nvidia.utilizationGpu;
}
if (nvidia.utilizationMemory) {
controller.utilizationMemory = nvidia.utilizationMemory;
}
if (nvidia.temperatureGpu) {
controller.temperatureGpu = nvidia.temperatureGpu;
}
if (nvidia.temperatureMemory) {
controller.temperatureMemory = nvidia.temperatureMemory;
}
if (nvidia.powerDraw) {
controller.powerDraw = nvidia.powerDraw;
}
if (nvidia.powerLimit) {
controller.powerLimit = nvidia.powerLimit;
}
if (nvidia.clockCore) {
controller.clockCore = nvidia.clockCore;
}
if (nvidia.clockMemory) {
controller.clockMemory = nvidia.clockMemory;
}
return controller;
}
@ -514,7 +580,7 @@ function graphics(callback) {
// --> pixeldepth (?)
// --> sizex
// --> sizey
let result = {
const result = {
vendor: '',
model: '',
deviceName: '',
@ -559,11 +625,12 @@ function graphics(callback) {
}
try {
if (model_raw.length > 2) {
result.model = model_raw.match(/.{1,2}/g).map(function (v) {
return String.fromCharCode(parseInt(v, 16));
}).join('');
result.model = model_raw
.match(/.{1,2}/g)
.map((v) => String.fromCharCode(parseInt(v, 16)))
.join('');
}
} catch (e) {
} catch {
util.noop();
}
} else {
@ -573,7 +640,7 @@ function graphics(callback) {
}
function parseLinesLinuxDisplays(lines, depth) {
let displays = [];
const displays = [];
let currentDisplay = {
vendor: '',
model: '',
@ -596,10 +663,21 @@ function graphics(callback) {
let is_current = false;
let edid_raw = '';
let start = 0;
for (let i = 1; i < lines.length; i++) { // start with second line
for (let i = 1; i < lines.length; i++) {
// start with second line
if ('' !== lines[i].trim()) {
if (' ' !== lines[i][0] && '\t' !== lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') !== -1) { // first line of new entry
if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // push last display to array
if (' ' !== lines[i][0] && '\t' !== lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') !== -1) {
// first line of new entry
if (
currentDisplay.model ||
currentDisplay.main ||
currentDisplay.builtin ||
currentDisplay.connection ||
currentDisplay.sizeX !== null ||
currentDisplay.pixelDepth !== null ||
currentDisplay.resolutionX !== null
) {
// push last display to array
displays.push(currentDisplay);
currentDisplay = {
vendor: '',
@ -622,7 +700,7 @@ function graphics(callback) {
let parts = lines[i].split(' ');
currentDisplay.connection = parts[0];
currentDisplay.main = lines[i].toLowerCase().indexOf(' primary ') >= 0;
currentDisplay.builtin = (parts[0].toLowerCase().indexOf('edp') >= 0);
currentDisplay.builtin = parts[0].toLowerCase().indexOf('edp') >= 0;
}
// try to read EDID information
@ -666,7 +744,16 @@ function graphics(callback) {
}
// pushen displays
if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // still information there
if (
currentDisplay.model ||
currentDisplay.main ||
currentDisplay.builtin ||
currentDisplay.connection ||
currentDisplay.sizeX !== null ||
currentDisplay.pixelDepth !== null ||
currentDisplay.resolutionX !== null
) {
// still information there
displays.push(currentDisplay);
}
return displays;
@ -680,8 +767,8 @@ function graphics(callback) {
displays: []
};
if (_darwin) {
let cmd = 'system_profiler -xml -detailLevel full SPDisplaysDataType';
exec(cmd, function (error, stdout) {
const cmd = 'system_profiler -xml -detailLevel full SPDisplaysDataType';
exec(cmd, (error, stdout) => {
if (!error) {
try {
const output = stdout.toString();
@ -690,7 +777,10 @@ function graphics(callback) {
util.noop();
}
try {
stdout = execSync('defaults read /Library/Preferences/com.apple.windowserver.plist 2>/dev/null;defaults read /Library/Preferences/com.apple.windowserver.displays.plist 2>/dev/null; echo ""', { maxBuffer: 1024 * 20000 });
stdout = execSync(
'defaults read /Library/Preferences/com.apple.windowserver.plist 2>/dev/null;defaults read /Library/Preferences/com.apple.windowserver.displays.plist 2>/dev/null; echo ""',
{ maxBuffer: 1024 * 20000 }
);
const output = (stdout || '').toString();
const obj = util.plistReader(output);
if (obj['DisplayAnyUserSets'] && obj['DisplayAnyUserSets']['Configs'] && obj['DisplayAnyUserSets']['Configs'][0] && obj['DisplayAnyUserSets']['Configs'][0]['DisplayConfig']) {
@ -722,7 +812,7 @@ function graphics(callback) {
i++;
});
}
} catch (e) {
} catch {
util.noop();
}
}
@ -735,9 +825,9 @@ function graphics(callback) {
if (_linux) {
// Raspberry: https://elinux.org/RPI_vcgencmd_usage
if (util.isRaspberry()) {
let 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;';
exec(cmd, function (error, stdout) {
let lines = stdout.toString().split('\n');
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;";
exec(cmd, (error, stdout) => {
const lines = stdout.toString().split('\n');
if (lines.length > 3 && lines[0].indexOf('mode "') >= -1 && lines[2].indexOf('0x12000a') > -1) {
const parts = lines[0].replace('mode', '').replace(/"/g, '').trim().split('x');
if (parts.length === 2) {
@ -769,44 +859,41 @@ function graphics(callback) {
vramDynamic: true
});
}
// if (callback) {
// callback(result);
// }
// resolve(result);
});
}
// } else {
let cmd = 'lspci -vvv 2>/dev/null';
exec(cmd, function (error, stdout) {
const cmd = 'lspci -vvv 2>/dev/null';
exec(cmd, (error, stdout) => {
if (!error) {
let lines = stdout.toString().split('\n');
const lines = stdout.toString().split('\n');
if (result.controllers.length === 0) {
result.controllers = parseLinesLinuxControllers(lines);
const nvidiaData = nvidiaDevices();
// needs to be rewritten ... using no spread operators
result.controllers = result.controllers.map((controller) => { // match by busAddress
result.controllers = result.controllers.map((controller) => {
// match by busAddress
return mergeControllerNvidia(controller, nvidiaData.find((contr) => contr.pciBus.toLowerCase().endsWith(controller.busAddress.toLowerCase())) || {});
});
}
}
let cmd = 'clinfo --raw';
exec(cmd, function (error, stdout) {
const cmd = 'clinfo --raw';
exec(cmd, (error, stdout) => {
if (!error) {
let lines = stdout.toString().split('\n');
const lines = stdout.toString().split('\n');
result.controllers = parseLinesLinuxClinfo(result.controllers, lines);
}
let cmd = 'xdpyinfo 2>/dev/null | grep \'depth of root window\' | awk \'{ print $5 }\'';
exec(cmd, function (error, stdout) {
const cmd = "xdpyinfo 2>/dev/null | grep 'depth of root window' | awk '{ print $5 }'";
exec(cmd, (error, stdout) => {
let depth = 0;
if (!error) {
let lines = stdout.toString().split('\n');
const lines = stdout.toString().split('\n');
depth = parseInt(lines[0]) || 0;
}
let cmd = 'xrandr --verbose 2>/dev/null';
exec(cmd, function (error, stdout) {
const cmd = 'xrandr --verbose 2>/dev/null';
exec(cmd, (error, stdout) => {
if (!error) {
let lines = stdout.toString().split('\n');
const lines = stdout.toString().split('\n');
result.displays = parseLinesLinuxDisplays(lines, depth);
}
if (callback) {
@ -820,124 +907,142 @@ function graphics(callback) {
// }
}
if (_freebsd || _openbsd || _netbsd) {
if (callback) { callback(null); }
if (callback) {
callback(null);
}
resolve(null);
}
if (_sunos) {
if (callback) { callback(null); }
if (callback) {
callback(null);
}
resolve(null);
}
if (_windows) {
// https://blogs.technet.microsoft.com/heyscriptingguy/2013/10/03/use-powershell-to-discover-multi-monitor-information/
// https://devblogs.microsoft.com/scripting/use-powershell-to-discover-multi-monitor-information/
try {
const workload = [];
workload.push(util.powerShell('Get-CimInstance win32_VideoController | fl *'));
workload.push(util.powerShell('gp "HKLM:\\SYSTEM\\ControlSet001\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\*" -ErrorAction SilentlyContinue | where MatchingDeviceId $null -NE | select MatchingDeviceId,HardwareInformation.qwMemorySize | fl'));
workload.push(
util.powerShell(
'gp "HKLM:\\SYSTEM\\ControlSet001\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\*" -ErrorAction SilentlyContinue | where MatchingDeviceId $null -NE | select MatchingDeviceId,HardwareInformation.qwMemorySize | fl'
)
);
workload.push(util.powerShell('Get-CimInstance win32_desktopmonitor | fl *'));
workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorBasicDisplayParams | fl'));
workload.push(util.powerShell('Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::AllScreens'));
workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorConnectionParams | fl'));
workload.push(util.powerShell('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}'));
workload.push(
util.powerShell(
'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}'
)
);
const nvidiaData = nvidiaDevices();
Promise.all(
workload
).then((data) => {
// controller + vram
let csections = data[0].replace(/\r/g, '').split(/\n\s*\n/);
let vsections = data[1].replace(/\r/g, '').split(/\n\s*\n/);
result.controllers = parseLinesWindowsControllers(csections, vsections);
result.controllers = result.controllers.map((controller) => { // match by subDeviceId
if (controller.vendor.toLowerCase() === 'nvidia') {
return mergeControllerNvidia(controller, nvidiaData.find(device => {
let windowsSubDeviceId = (controller.subDeviceId || '').toLowerCase();
const nvidiaSubDeviceIdParts = device.subDeviceId.split('x');
let nvidiaSubDeviceId = nvidiaSubDeviceIdParts.length > 1 ? nvidiaSubDeviceIdParts[1].toLowerCase() : nvidiaSubDeviceIdParts[0].toLowerCase();
const lengthDifference = Math.abs(windowsSubDeviceId.length - nvidiaSubDeviceId.length);
if (windowsSubDeviceId.length > nvidiaSubDeviceId.length) {
for (let i = 0; i < lengthDifference; i++) {
nvidiaSubDeviceId = '0' + nvidiaSubDeviceId;
}
} else if (windowsSubDeviceId.length < nvidiaSubDeviceId.length) {
for (let i = 0; i < lengthDifference; i++) {
windowsSubDeviceId = '0' + windowsSubDeviceId;
}
Promise.all(workload)
.then((data) => {
// controller + vram
const csections = data[0].replace(/\r/g, '').split(/\n\s*\n/);
const vsections = data[1].replace(/\r/g, '').split(/\n\s*\n/);
result.controllers = parseLinesWindowsControllers(csections, vsections);
result.controllers = result.controllers.map((controller) => {
// match by subDeviceId
if (controller.vendor.toLowerCase() === 'nvidia') {
return mergeControllerNvidia(
controller,
nvidiaData.find((device) => {
let windowsSubDeviceId = (controller.subDeviceId || '').toLowerCase();
const nvidiaSubDeviceIdParts = device.subDeviceId.split('x');
let nvidiaSubDeviceId = nvidiaSubDeviceIdParts.length > 1 ? nvidiaSubDeviceIdParts[1].toLowerCase() : nvidiaSubDeviceIdParts[0].toLowerCase();
const lengthDifference = Math.abs(windowsSubDeviceId.length - nvidiaSubDeviceId.length);
if (windowsSubDeviceId.length > nvidiaSubDeviceId.length) {
for (let i = 0; i < lengthDifference; i++) {
nvidiaSubDeviceId = '0' + nvidiaSubDeviceId;
}
} else if (windowsSubDeviceId.length < nvidiaSubDeviceId.length) {
for (let i = 0; i < lengthDifference; i++) {
windowsSubDeviceId = '0' + windowsSubDeviceId;
}
}
return windowsSubDeviceId === nvidiaSubDeviceId;
}) || {}
);
} else {
return controller;
}
});
// displays
const dsections = data[2].replace(/\r/g, '').split(/\n\s*\n/);
// result.displays = parseLinesWindowsDisplays(dsections);
if (dsections[0].trim() === '') {
dsections.shift();
}
if (dsections.length && dsections[dsections.length - 1].trim() === '') {
dsections.pop();
}
// monitor (powershell)
const msections = data[3].replace(/\r/g, '').split('Active ');
msections.shift();
// forms.screens (powershell)
const ssections = data[4].replace(/\r/g, '').split('BitsPerPixel ');
ssections.shift();
// connection params (powershell) - video type
const tsections = data[5].replace(/\r/g, '').split(/\n\s*\n/);
tsections.shift();
// monitor ID (powershell) - model / vendor
const res = data[6].replace(/\r/g, '').split(/\n/);
const isections = [];
res.forEach((element) => {
const parts = element.split('|');
if (parts.length === 5) {
isections.push({
vendor: parts[0],
code: parts[1],
model: parts[2],
serial: parts[3],
instanceId: parts[4]
});
}
});
result.displays = parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections);
if (result.displays.length === 1) {
if (_resolutionX) {
result.displays[0].resolutionX = _resolutionX;
if (!result.displays[0].currentResX) {
result.displays[0].currentResX = _resolutionX;
}
return windowsSubDeviceId === nvidiaSubDeviceId;
}) || {});
} else {
return controller;
}
});
// displays
let dsections = data[2].replace(/\r/g, '').split(/\n\s*\n/);
// result.displays = parseLinesWindowsDisplays(dsections);
if (dsections[0].trim() === '') { dsections.shift(); }
if (dsections.length && dsections[dsections.length - 1].trim() === '') { dsections.pop(); }
// monitor (powershell)
let msections = data[3].replace(/\r/g, '').split('Active ');
msections.shift();
// forms.screens (powershell)
let ssections = data[4].replace(/\r/g, '').split('BitsPerPixel ');
ssections.shift();
// connection params (powershell) - video type
let tsections = data[5].replace(/\r/g, '').split(/\n\s*\n/);
tsections.shift();
// monitor ID (powershell) - model / vendor
const res = data[6].replace(/\r/g, '').split(/\n/);
let isections = [];
res.forEach(element => {
const parts = element.split('|');
if (parts.length === 5) {
isections.push({
vendor: parts[0],
code: parts[1],
model: parts[2],
serial: parts[3],
instanceId: parts[4]
});
}
});
result.displays = parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections);
if (result.displays.length === 1) {
if (_resolutionX) {
result.displays[0].resolutionX = _resolutionX;
if (!result.displays[0].currentResX) {
result.displays[0].currentResX = _resolutionX;
}
if (_resolutionY) {
result.displays[0].resolutionY = _resolutionY;
if (result.displays[0].currentResY === 0) {
result.displays[0].currentResY = _resolutionY;
}
}
if (_pixelDepth) {
result.displays[0].pixelDepth = _pixelDepth;
}
}
if (_resolutionY) {
result.displays[0].resolutionY = _resolutionY;
if (result.displays[0].currentResY === 0) {
result.displays[0].currentResY = _resolutionY;
result.displays = result.displays.map((element) => {
if (_refreshRate && !element.currentRefreshRate) {
element.currentRefreshRate = _refreshRate;
}
}
if (_pixelDepth) {
result.displays[0].pixelDepth = _pixelDepth;
}
}
result.displays = result.displays.map(element => {
if (_refreshRate && !element.currentRefreshRate) {
element.currentRefreshRate = _refreshRate;
}
return element;
});
return element;
});
if (callback) {
callback(result);
}
resolve(result);
})
if (callback) {
callback(result);
}
resolve(result);
})
.catch(() => {
if (callback) {
callback(result);
@ -945,7 +1050,9 @@ function graphics(callback) {
resolve(result);
});
} catch (e) {
if (callback) { callback(result); }
if (callback) {
callback(result);
}
resolve(result);
}
}
@ -976,12 +1083,12 @@ function graphics(callback) {
}
}
let controllers = [];
for (let i in sections) {
const controllers = [];
for (const i in sections) {
if ({}.hasOwnProperty.call(sections, i)) {
if (sections[i].trim() !== '') {
let lines = sections[i].trim().split('\n');
let pnpDeviceId = util.getValue(lines, 'PNPDeviceID', ':').match(/PCI\\(VEN_[0-9A-F]{4})&(DEV_[0-9A-F]{4})(?:&(SUBSYS_[0-9A-F]{8}))?(?:&(REV_[0-9A-F]{2}))?/i);
const lines = sections[i].trim().split('\n');
const pnpDeviceId = util.getValue(lines, 'PNPDeviceID', ':').match(/PCI\\(VEN_[0-9A-F]{4})&(DEV_[0-9A-F]{4})(?:&(SUBSYS_[0-9A-F]{8}))?(?:&(REV_[0-9A-F]{2}))?/i);
let subDeviceId = null;
let memorySize = null;
if (pnpDeviceId) {
@ -1031,7 +1138,7 @@ function graphics(callback) {
model: util.getValue(lines, 'name', ':'),
bus: util.getValue(lines, 'PNPDeviceID', ':').startsWith('PCI') ? 'PCI' : '',
vram: (memorySize == null ? util.toInt(util.getValue(lines, 'AdapterRAM', ':')) : memorySize) / 1024 / 1024,
vramDynamic: (util.getValue(lines, 'VideoMemoryType', ':') === '2'),
vramDynamic: util.getValue(lines, 'VideoMemoryType', ':') === '2',
subDeviceId
});
_resolutionX = util.toInt(util.getValue(lines, 'CurrentHorizontalResolution', ':')) || _resolutionX;
@ -1045,14 +1152,14 @@ function graphics(callback) {
}
function parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections) {
let displays = [];
const displays = [];
let vendor = '';
let model = '';
let deviceID = '';
let resolutionX = 0;
let resolutionY = 0;
if (dsections && dsections.length) {
let linesDisplay = dsections[0].split('\n');
const linesDisplay = dsections[0].split('\n');
vendor = util.getValue(linesDisplay, 'MonitorManufacturer', ':');
model = util.getValue(linesDisplay, 'Name', ':');
deviceID = util.getValue(linesDisplay, 'PNPDeviceID', ':').replace(/&amp;/g, '&').toLowerCase();
@ -1068,10 +1175,10 @@ function graphics(callback) {
if (tsections.length === 0 || tsections[i] === undefined) {
tsections[i] = 'Unknown';
}
let linesScreen = ssections[i].split('\n');
let linesMonitor = msections[i].split('\n');
const linesScreen = ssections[i].split('\n');
const linesMonitor = msections[i].split('\n');
let linesConnection = tsections[i].split('\n');
const linesConnection = tsections[i].split('\n');
const bitsPerPixel = util.getValue(linesScreen, 'BitsPerPixel');
const bounds = util.getValue(linesScreen, 'Bounds').replace('{', '').replace('}', '').replace(/=/g, ':').split(',');
const primary = util.getValue(linesScreen, 'Primary');
@ -1082,7 +1189,7 @@ function graphics(callback) {
const deviceName = util.getValue(linesScreen, 'DeviceName');
let displayVendor = '';
let displayModel = '';
isections.forEach(element => {
isections.forEach((element) => {
if (element.instanceId.toLowerCase().startsWith(instanceName) && vendor.startsWith('(') && model.startsWith('PnP')) {
displayVendor = element.vendor;
displayModel = element.model;
@ -1103,7 +1210,7 @@ function graphics(callback) {
currentResX: util.toInt(util.getValue(bounds, 'Width', ':')),
currentResY: util.toInt(util.getValue(bounds, 'Height', ':')),
positionX: util.toInt(util.getValue(bounds, 'X', ':')),
positionY: util.toInt(util.getValue(bounds, 'Y', ':')),
positionY: util.toInt(util.getValue(bounds, 'Y', ':'))
});
}
}