Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Completed Windows graphic changes
  • Loading branch information
shoustech committed Sep 19, 2023
commit 66a13ca4e59e16c7f7bb8b58dfc87ae305e822d0
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;