graphics() macOS new XML parser

This commit is contained in:
Sebastian Hildebrandt 2021-09-14 17:50:05 +02:00
parent cd46226ad5
commit 09775cd5bd
3 changed files with 186 additions and 159 deletions

View File

@ -989,7 +989,7 @@ function diskLayout(callback) {
if (sizeStr) {
let sizeValue = 0;
if (sizeStr.indexOf('(') >= 0) {
sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, ''));
sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/ /g, ''));
}
if (!sizeValue) {
sizeValue = parseInt(sizeStr);
@ -1037,7 +1037,7 @@ function diskLayout(callback) {
if (sizeStr) {
let sizeValue = 0;
if (sizeStr.indexOf('(') >= 0) {
sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, ''));
sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/ /g, ''));
}
if (!sizeValue) {
sizeValue = parseInt(sizeStr);
@ -1082,7 +1082,7 @@ function diskLayout(callback) {
if (sizeStr) {
let sizeValue = 0;
if (sizeStr.indexOf('(') >= 0) {
sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, ''));
sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/ /g, ''));
}
if (!sizeValue) {
sizeValue = parseInt(sizeStr);

View File

@ -57,7 +57,7 @@ const videoTypes = {
};
function getVendorFromModel(model) {
const diskManufacturers = [
const manufacturers = [
{ pattern: '^LG.+', manufacturer: 'LG' },
{ pattern: '^BENQ.+', manufacturer: 'BenQ' },
{ pattern: '^ASUS.+', manufacturer: 'Asus' },
@ -76,12 +76,15 @@ function getVendorFromModel(model) {
{ pattern: '^LENOVO.+', manufacturer: 'Lenovo' },
{ pattern: 'COMPAQ.+', manufacturer: 'Compaq' },
{ pattern: 'APPLE.+', manufacturer: 'Apple' },
{ pattern: 'INTEL.+', manufacturer: 'Intel' },
{ pattern: 'AMD.+', manufacturer: 'AMD' },
{ pattern: 'NVIDIA.+', manufacturer: 'NVDIA' },
];
let result = '';
if (model) {
model = model.toUpperCase();
diskManufacturers.forEach((manufacturer) => {
manufacturers.forEach((manufacturer) => {
const re = RegExp(manufacturer.pattern);
if (re.test(model)) { result = manufacturer.manufacturer; }
});
@ -89,166 +92,97 @@ function getVendorFromModel(model) {
return result;
}
function getVendorFromId(id) {
const vendors = {
'610': 'Apple',
'1e6d': 'LG',
'10ac': 'DELL',
'4dd9': 'Sony',
'38a3': 'NEC',
};
return vendors[id] || '';
}
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',
};
return families[id] || '';
}
function graphics(callback) {
function parseLinesDarwin(lines) {
let starts = [];
let level = -1;
let lastlevel = -1;
let controllers = [];
let displays = [];
let currentController = {
vendor: '',
model: '',
bus: '',
vram: null,
vramDynamic: false
function parseLinesDarwin(graphicsArr) {
const res = {
controllers: [],
displays: []
};
let currentDisplay = {
vendor: '',
model: '',
deviceName: '',
main: false,
builtin: false,
connection: '',
sizeX: null,
sizeY: null,
pixelDepth: null,
resolutionX: null,
resolutionY: null,
currentResX: null,
currentResY: null,
positionX: 0,
positionY: 0,
currentRefreshRate: null
};
for (let i = 0; i < lines.length; i++) {
if ('' !== lines[i].trim()) {
let start = lines[i].search(/\S|$/);
if (-1 === starts.indexOf(start)) {
starts.push(start);
}
level = starts.indexOf(start);
if (level < lastlevel) {
if (Object.keys(currentController).length > 0 && (currentController.vendor || currentController.model)) {// just changed to Displays
controllers.push(currentController);
currentController = {
vendor: '',
model: '',
bus: '',
vram: null,
vramDynamic: false
};
}
if (Object.keys(currentDisplay).length > 0) {// just changed to Displays
if (currentDisplay.resolutionX && currentDisplay.resolutionY) { displays.push(currentDisplay); }
currentDisplay = {
vendor: '',
model: '',
deviceName: '',
main: false,
builtin: false,
connection: '',
try {
graphicsArr.forEach(function (item) {
// controllers
const bus = ((item.sppci_bus || '').indexOf('builtin') ? 'Built-In' : ((item.sppci_bus || '').indexOf('pcie') ? 'PCIE' : ''));
const vram = (parseInt((item._spdisplays_vram || ''), 10) || 0) * (((item._spdisplays_vram || '').indexOf('GB') > -1) ? 1024 : 1);
const vramDyn = (parseInt((item.spdisplays_vram_shares || ''), 10) || 0) * (((item.spdisplays_vram_shares || '').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 || '',
model: item.sppci_model || '',
bus,
vramDynamic: bus === 'Built-In',
vram: vram || vramDyn || 0,
deviceId: item['spdisplays_device-id'] || '',
vendorId: item['spdisplays_vendor-id'] || '',
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) {
const connectionType = displayItem['spdisplays_connection_type'];
const currentResolutionParts = (displayItem['_spdisplays_resolution'] || '').split('@');
const currentResolution = currentResolutionParts[0].split('x');
const pixelParts = (displayItem['_spdisplays_pixels'] || '').split('x');
const pixelDepthString = displayItem['spdisplays_depth'] || '';
const serial = displayItem['_spdisplays_display-serial-number'] || displayItem['_spdisplays_display-serial-number2'] || null;
res.displays.push({
vendor: getVendorFromId(displayItem['_spdisplays_display-vendor-id'] || '') || getVendorFromModel(displayItem['_name'] || ''),
vendorId: displayItem['_spdisplays_display-vendor-id'] || '',
model: displayItem['_name'] || '',
productionYear: displayItem['_spdisplays_display-year'] || null,
serial: serial !== '0' ? serial : null,
displayId: displayItem['_spdisplays_displayID'] || null,
main: displayItem['spdisplays_main'] ? displayItem['spdisplays_main'] === 'spdisplays_yes' : true,
builtin: (displayItem['spdisplays_display_type'] || '').indexOf('built-in') >= 0,
connection: ((connectionType.indexOf('_internal')) ? 'Internal' : ((connectionType.indexOf('_displayport')) ? 'Display Port' : ((connectionType.indexOf('_hdmi')) ? 'HDMI' : ''))),
sizeX: null,
sizeY: null,
pixelDepth: null,
resolutionX: null,
resolutionY: null,
currentResX: null,
currentResY: null,
pixelDepth: (pixelDepthString === 'CGSThirtyBitColor' ? 30 : (pixelDepthString === 'CGSThirtytwoBitColor' ? 32 : (pixelDepthString === 'CGSTwentyfourBitColor' ? 24 : ''))),
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: null
};
}
currentRefreshRate: currentResolutionParts.length > 1 ? parseInt(currentResolutionParts[1], 10) : null,
});
});
}
lastlevel = level;
let parts = lines[i].split(':');
if (2 === level) { // grafics controller level
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('chipsetmodel') !== -1) { currentController.model = parts[1].trim(); }
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('vendor') !== -1) { currentController.vendor = parts[1].split('(')[0].trim(); }
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('vram(total)') !== -1) {
currentController.vram = parseInt(parts[1]); // in MB
if (parts[1].toLowerCase().indexOf('gb') !== -1) {
currentController.vram = currentController.vram * 1024;
}
currentController.vramDynamic = false;
}
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('vram(dynamic,max)') !== -1) {
currentController.vram = parseInt(parts[1]); // in MB
if (parts[1].toLowerCase().indexOf('gb') !== -1) {
currentController.vram = currentController.vram * 1024;
}
currentController.vramDynamic = true;
}
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('bus') !== -1) {
currentController.bus = parts[1].trim();
if (currentController.bus.toLowerCase() === 'built-in') {
currentController.vramDynamic = true;
}
}
}
if (3 === level) { // display controller level
if (parts.length > 1 && '' === parts[1]) {
currentDisplay.vendor = getVendorFromModel(parts[0].trim());
currentDisplay.model = parts[0].trim();
currentDisplay.main = false;
currentDisplay.builtin = false;
currentDisplay.connection = '';
currentDisplay.sizeX = null;
currentDisplay.sizeY = null;
currentDisplay.positionX = 0;
currentDisplay.positionY = 0;
currentDisplay.pixelDepth = null;
}
}
if (4 === level) { // display controller details level
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('resolution') !== -1) {
let resolution = parts[1].split('x');
if (resolution.length > 1) {
let xpart = resolution[0];
if (xpart.indexOf('(') !== -1) {
xpart = xpart.split('(').slice(-1)[0];
}
let ypart = resolution[1];
if (ypart.indexOf(')') !== -1) {
ypart = ypart.split(')')[0];
}
currentDisplay.resolutionX = parseInt(xpart) || 0;
currentDisplay.resolutionY = parseInt(ypart) || 0;
currentDisplay.currentResX = currentDisplay.resolutionX;
currentDisplay.currentResY = currentDisplay.resolutionY;
}
}
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('pixeldepth') !== -1) { currentDisplay.pixelDepth = parseInt(parts[1]); } // in BIT
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('framebufferdepth') !== -1) { currentDisplay.pixelDepth = parseInt(parts[1]); } // in BIT
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('maindisplay') !== -1 && parts[1].replace(/ +/g, '').toLowerCase() === 'yes') { currentDisplay.main = true; }
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('built-in') !== -1 && parts[1].replace(/ +/g, '').toLowerCase() === 'yes') {
currentDisplay.vendor = 'Apple';
currentDisplay.builtin = true;
currentDisplay.connection = '';
}
if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('connectiontype') !== -1) {
currentDisplay.builtin = false;
currentDisplay.connection = parts[1].trim();
if (currentDisplay.connection === 'Internal') {
currentDisplay.vendor = 'Apple';
currentDisplay.builtin = true;
}
}
}
}
});
return res;
} catch (e) {
return res;
}
if (Object.keys(currentController).length > 0 && (currentController.vendor || currentController.model)) {// just changed to Displays
controllers.push(currentController);
}
if (Object.keys(currentDisplay).length > 0) {// just changed to Displays
if (currentDisplay.resolutionX && currentDisplay.resolutionY) { displays.push(currentDisplay); }
}
return ({
controllers: controllers,
displays: displays
});
}
function parseLinesLinuxControllers(lines) {
@ -706,11 +640,15 @@ function graphics(callback) {
displays: []
};
if (_darwin) {
let cmd = 'system_profiler SPDisplaysDataType';
let cmd = 'system_profiler -xml -detailLevel full SPDisplaysDataType';
exec(cmd, function (error, stdout) {
if (!error) {
let lines = stdout.toString().split('\n');
result = parseLinesDarwin(lines);
try {
let output = stdout.toString();
result = parseLinesDarwin(util.plistParser(output)[0]._items);
} catch (e) {
util.noop();
}
}
if (callback) {
callback(result);

View File

@ -985,6 +985,94 @@ function linuxVersion() {
return result;
}
function plistParser(xmlStr) {
const tags = ['array', 'dict', 'key', 'string', 'integer', 'date', 'real', 'data'];
function getNextTagPos() {
let result = {
pos: 999999,
tag: ''
};
tags.forEach((tag) => {
const ii = xmlStr.indexOf(`<${tag}>`);
if (ii !== -1 && ii < result.pos) {
result = {
pos: ii,
tag
};
}
});
return result;
}
function getNextClosingTagPos(tag) {
return xmlStr.indexOf(`</${tag}>`);
}
function parseXmlTree(isArray, closingTag) {
// start parsing
let obj = {};
let arr = [];
let cpos = getNextTagPos();
let key = '';
let valueStr = null;
while (cpos.pos >= 0 && cpos.tag) {
let nextTagPos = getNextTagPos();
// let nextClosePos = getNextClosingTagPos(cpos.tag);
let nextClosePosBlock = closingTag ? getNextClosingTagPos(closingTag) : 999999;
if (nextClosePosBlock <= nextTagPos.pos) {
return (isArray ? arr : obj);
}
xmlStr = xmlStr.substring((cpos.pos + cpos.tag.length + 2));
if (cpos.tag === 'array') {
const res = parseXmlTree(true, cpos.tag);
if (key) {
obj[key] = res;
} else {
obj = res;
}
} else if (cpos.tag === 'dict') {
const res = parseXmlTree(false, cpos.tag);
if (!isArray) {
obj[key] = res;
} else {
arr.push(res);
}
xmlStr = xmlStr.substring((cpos.pos + cpos.tag.length + 3));
} else {
let nextTagPos = getNextTagPos();
let nextClosePos = getNextClosingTagPos(cpos.tag);
nextClosePosBlock = closingTag ? getNextClosingTagPos(closingTag) : 999999;
if (nextClosePos < nextTagPos.pos) {
if (cpos.tag === 'key') {
key = xmlStr.substring(0, nextClosePos);
xmlStr = xmlStr.substring(key.length + cpos.tag.length + 3); // key done
} else {
valueStr = xmlStr.substring(0, nextClosePos).replace(/\t/g, '');
if (cpos.tag === 'string') { if (!isArray) { obj[key] = valueStr; } else { arr.push(valueStr); } }
if (cpos.tag === 'integer') { if (!isArray) { obj[key] = parseInt(valueStr); } else { arr.push(parseInt(valueStr)); } }
if (cpos.tag === 'date') { if (!isArray) { obj[key] = valueStr; } else { arr.push(valueStr); } }
if (cpos.tag === 'data') { if (!isArray) { obj[key] = valueStr; } else { arr.push(valueStr); } }
if (cpos.tag === 'real') { if (!isArray) { obj[key] = parseFloat(valueStr); } else { arr.push(parseFloat(valueStr)); } }
key = '';
xmlStr = xmlStr.substring(valueStr.length + cpos.tag.length + 3); // property done
}
}
}
cpos = getNextTagPos();
}
return (isArray ? arr : obj);
}
try {
const result = parseXmlTree(false, '');
return result;
} catch (e) {
return {};
}
}
function noop() { }
exports.toInt = toInt;
@ -1020,6 +1108,7 @@ exports.promisify = promisify;
exports.promisifySave = promisifySave;
exports.smartMonToolsInstalled = smartMonToolsInstalled;
exports.linuxVersion = linuxVersion;
exports.plistParser = plistParser;
exports.stringReplace = stringReplace;
exports.stringToLower = stringToLower;
exports.stringToString = stringToString;