Skip to content

Commit

Permalink
Merge pull request #1 from shoustech/bugfix/sebhildebrandt#851
Browse files Browse the repository at this point in the history
  • Loading branch information
shoustech committed Oct 4, 2023
2 parents d763149 + c424106 commit 04c3134
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 154 deletions.
280 changes: 128 additions & 152 deletions lib/graphics.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const exec = require('child_process').exec;
const execSync = require('child_process').execSync;
const util = require('./util');

let _platform = process.platform;
const _platform = process.platform;
let _nvidiaSmiPath = '';

const _linux = (_platform === 'linux' || _platform === 'android');
Expand Down Expand Up @@ -52,7 +52,8 @@ const videoTypes = {
'13': 'UDI embedded',
'14': 'SDTVDONGLE',
'15': 'MIRACAST',
'2147483648': 'INTERNAL'
'2147483648': 'INTERNAL',
'4294967295': 'RDP'
};

function getVendorFromModel(model) {
Expand Down Expand Up @@ -827,20 +828,20 @@ function graphics(callback) {
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('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 win32_desktopmonitor | select * | fl *'));
workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorBasicDisplayParams | fl *'));
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:" +( $_.ManufacturerName -notmatch 0 | foreach {[char]$_}) -join "") + "|ProductCodeID:" + (($_.ProductCodeID -notmatch 0 | foreach {[char]$_}) -join "") + "|UserFriendlyName:" + (($_.UserFriendlyName -notmatch 0 | foreach {[char]$_}) -join "") + "|SerialNumberID:" + (($_.SerialNumberID -notmatch 0 | foreach {[char]$_}) -join "") + "|InstanceName:" + $_.InstanceName + "|YearOfManufacture:" + $_.YearOfManufacture}'))
workload.push(util.powerShell('Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::AllScreens'));

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/);
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') {
Expand All @@ -866,65 +867,23 @@ function graphics(callback) {
});

// 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;
const dsections = giveMeJson(data[2])
const msections = giveMeJson(data[3])
const tsections = giveMeJson(data[4])
// const ssections = giveMeJson(data[6]) I cannot find a reliable method to correlate this with the displays from the other output
const isections = []
data[5].split('\r\n').forEach((display) => {
const newDisplay = {}
display.split('|').forEach((chunk) => {
const entries = chunk.split(':')
if (entries.length === 2) {
newDisplay[entries[0].trim()] = entries[1].trim()
}
}
}
if (_pixelDepth) {
result.displays[0].pixelDepth = _pixelDepth;
}
}
result.displays = result.displays.map(element => {
if (_refreshRate && !element.currentRefreshRate) {
element.currentRefreshRate = _refreshRate;
}
return element;
});

)
isections.push(newDisplay)
})
result.displays = parseWindowsDisplays(dsections, msections, tsections, isections)
if (callback) {
callback(result);
}
Expand All @@ -944,6 +903,105 @@ function graphics(callback) {
});
});

function giveMeJson(windowsOutput) {
const newResult = []
windowsOutput.trim()
.replace(/\r/g, '')
.split(/\n\s*\n/)
.map((display, i) => {
const array = display.split('\n')
array.map(line => {
const entries = line.split(':')
if (entries.length === 2) {
if (!newResult[i]) {
newResult[i] = {};
}
newResult[i][entries[0].trim()] = entries[1].trim()
return entries
}
})
})
return newResult;
}


function parseWindowsDisplays(dsections, msections, tsections, isections) {
/**
* To tie everything together tsections.InstanceName is what is being used as the key.
* TSections is the object that will list every monitor when a RDP connection is being used.
*/
const displays = []
tsections.forEach((tsection) => {
const display = {
vendor: '',
deviceName: '',
model: '',
productionYear: '',
serial: '',
displayId: tsection.InstanceName,
main: '',
builtin: ['2147483648', '4294967295'].includes(tsection.VideoOutputTechnology), //*
connection: videoTypes[tsection.VideoOutputTechnology], //*
sizeX: '',
sizeY: '',
pixelDepth: 0,
resolutionX: '',
resolutionY: '',
currentResX: '',
currentResY: '',
positionX: '',
positionY: '',
currentRefreshRate: 0,
}
const dsection = dsections.find((dsection) => {
return `${dsection.PNPDeviceID}_0` === tsection.InstanceName.toUpperCase()
})
const msection = msections.find((msection) => {
return msection.InstanceName === tsection.InstanceName
})
const isection = isections.find((isection) => {
return `${isection.InstanceName}` === tsection.InstanceName
})
if (dsection) {
display.vendor = dsection.MonitorManufacturer
display.deviceName = dsection.Name
display.displayId = tsection.InstanceName
display.resolutionX = util.toInt(dsection.ScreenWidth)
display.resolutionY = util.toInt(dsection.ScreenHeight)
display.model = dsection.Name.split('(', 1)[0].trim()
display.productionYear = dsection.ManufactureYearWeek
display.serial = dsection.SerialNumberID
} else {
display.resolutionX = 0;
display.resolutionY = 0;
}
if (msection) {
display.sizeX = util.toInt(msection.MaxHorizontalImageSize);
display.sizeY = util.toInt(msection.MaxVerticalImageSize);
} else {
display.sizeX = 0
display.sizeY = 0
}
if (isection) {
display.productionYear = isection.YearOfManufacture
if (display.vendor === '') {
display.vendor = isection.ManufacturerName
}
display.serial = isection.SerialNumberID
if (display.deviceName === '') {
display.deviceName = isection.UserFriendlyName
display.model = isection.UserFriendlyName
}
}
display.positionX = 0;
display.positionY = 0;
display.currentResX = display.resolutionX;
display.currentResY = display.resolutionY;
displays.push(display)
})
return displays
}

function parseLinesWindowsControllers(sections, vections) {
const memorySizes = {};
for (const i in vections) {
Expand All @@ -968,12 +1026,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) {
Expand Down Expand Up @@ -1035,88 +1093,6 @@ function graphics(callback) {
}
return controllers;
}

function parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections) {
let displays = [];
let vendor = '';
let model = '';
let deviceID = '';
let resolutionX = 0;
let resolutionY = 0;
if (dsections && dsections.length) {
let linesDisplay = dsections[0].split('\n');
vendor = util.getValue(linesDisplay, 'MonitorManufacturer', ':');
model = util.getValue(linesDisplay, 'Name', ':');
deviceID = util.getValue(linesDisplay, 'PNPDeviceID', ':').replace(/&/g, '&').toLowerCase();
resolutionX = util.toInt(util.getValue(linesDisplay, 'ScreenWidth', ':'));
resolutionY = util.toInt(util.getValue(linesDisplay, 'ScreenHeight', ':'));
}
for (let i = 0; i < ssections.length; i++) {
if (ssections[i].trim() !== '') {
ssections[i] = 'BitsPerPixel ' + ssections[i];
msections[i] = 'Active ' + msections[i];
// tsections can be empty OR undefined on earlier versions of powershell (<=2.0)
// Tag connection type as UNKNOWN by default if this information is missing
if (tsections.length === 0 || tsections[i] === undefined) {
tsections[i] = 'Unknown';
}
let linesScreen = ssections[i].split('\n');
let linesMonitor = msections[i].split('\n');

let 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');
const sizeX = util.getValue(linesMonitor, 'MaxHorizontalImageSize');
const sizeY = util.getValue(linesMonitor, 'MaxVerticalImageSize');
const instanceName = util.getValue(linesMonitor, 'InstanceName').toLowerCase();
const videoOutputTechnology = util.getValue(linesConnection, 'VideoOutputTechnology');
const deviceName = util.getValue(linesScreen, 'DeviceName');
let displayVendor = '';
let displayModel = '';
isections.forEach(element => {
if (element.instanceId.toLowerCase().startsWith(instanceName) && vendor.startsWith('(') && model.startsWith('PnP')) {
displayVendor = element.vendor;
displayModel = element.model;
}
});
displays.push({
vendor: instanceName.startsWith(deviceID) && displayVendor === '' ? vendor : displayVendor,
model: instanceName.startsWith(deviceID) && displayModel === '' ? model : displayModel,
deviceName,
main: primary.toLowerCase() === 'true',
builtin: videoOutputTechnology === '2147483648',
connection: videoOutputTechnology && videoTypes[videoOutputTechnology] ? videoTypes[videoOutputTechnology] : '',
resolutionX: util.toInt(util.getValue(bounds, 'Width', ':')),
resolutionY: util.toInt(util.getValue(bounds, 'Height', ':')),
sizeX: sizeX ? parseInt(sizeX, 10) : null,
sizeY: sizeY ? parseInt(sizeY, 10) : null,
pixelDepth: bitsPerPixel,
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', ':')),
});
}
}
if (ssections.length === 0) {
displays.push({
vendor,
model,
main: true,
sizeX: null,
sizeY: null,
resolutionX,
resolutionY,
pixelDepth: null,
currentResX: resolutionX,
currentResY: resolutionY,
positionX: 0,
positionY: 0
});
}
return displays;
}
}

exports.graphics = graphics;
6 changes: 4 additions & 2 deletions lib/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ exports.system = system;
function cleanDefaults(s) {
if (s === 'Default string') { s = ''; }
if (s.toLowerCase().indexOf('o.e.m.') !== -1) { s = ''; }

return s
}
function bios(callback) {
Expand Down Expand Up @@ -561,8 +561,10 @@ function baseboard(callback) {
const workload = [];
const win10plus = parseInt(os.release()) >= 10;
const maxCapacityAttribute = win10plus ? 'MaxCapacityEx' : 'MaxCapacity';
workload.push(util.powerShell('Get-CimInstance Win32_baseboard | select Model,Manufacturer,Product,Version,SerialNumber,PartNumber,SKU,SMBIOSAssetTag | fl'));
workload.push(util.powerShell('Get-CimInstance Win32_baseboard | select Model,Manufacturer,Product,Version,SerialNumber,PartNumber,SKU | fl'));
workload.push(util.powerShell(`Get-CimInstance Win32_physicalmemoryarray | select ${maxCapacityAttribute}, MemoryDevices | fl`));
workload.push(util.powerShell('Get-CimInstance Win32_SystemEnclosure | select SMBIOSAssetTag | fl'));

util.promiseAll(
workload
).then((data) => {
Expand Down

0 comments on commit 04c3134

Please sign in to comment.