siddr5 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of siddr5 might be problematic. Click here for more details.

@@ -0,0 +1,1103 @@
1
+ 'use strict';
2
+ // @ts-check
3
+ // ==================================================================================
4
+ // graphics.js
5
+ // ----------------------------------------------------------------------------------
6
+ // Description: System Information - library
7
+ // for Node.js
8
+ // Copyright: (c) 2014 - 2023
9
+ // Author: Sebastian Hildebrandt
10
+ // ----------------------------------------------------------------------------------
11
+ // License: MIT
12
+ // ==================================================================================
13
+ // 7. Graphics (controller, display)
14
+ // ----------------------------------------------------------------------------------
15
+
16
+ const fs = require('fs');
17
+ const exec = require('child_process').exec;
18
+ const execSync = require('child_process').execSync;
19
+ const util = require('./util');
20
+
21
+ let _platform = process.platform;
22
+ let _nvidiaSmiPath = '';
23
+
24
+ const _linux = (_platform === 'linux' || _platform === 'android');
25
+ const _darwin = (_platform === 'darwin');
26
+ const _windows = (_platform === 'win32');
27
+ const _freebsd = (_platform === 'freebsd');
28
+ const _openbsd = (_platform === 'openbsd');
29
+ const _netbsd = (_platform === 'netbsd');
30
+ const _sunos = (_platform === 'sunos');
31
+
32
+ let _resolutionX = 0;
33
+ let _resolutionY = 0;
34
+ let _pixelDepth = 0;
35
+ let _refreshRate = 0;
36
+
37
+ const videoTypes = {
38
+ '-2': 'UNINITIALIZED',
39
+ '-1': 'OTHER',
40
+ '0': 'HD15',
41
+ '1': 'SVIDEO',
42
+ '2': 'Composite video',
43
+ '3': 'Component video',
44
+ '4': 'DVI',
45
+ '5': 'HDMI',
46
+ '6': 'LVDS',
47
+ '8': 'D_JPN',
48
+ '9': 'SDI',
49
+ '10': 'DP',
50
+ '11': 'DP embedded',
51
+ '12': 'UDI',
52
+ '13': 'UDI embedded',
53
+ '14': 'SDTVDONGLE',
54
+ '15': 'MIRACAST',
55
+ '2147483648': 'INTERNAL'
56
+ };
57
+
58
+ function getVendorFromModel(model) {
59
+ const manufacturers = [
60
+ { pattern: '^LG.+', manufacturer: 'LG' },
61
+ { pattern: '^BENQ.+', manufacturer: 'BenQ' },
62
+ { pattern: '^ASUS.+', manufacturer: 'Asus' },
63
+ { pattern: '^DELL.+', manufacturer: 'Dell' },
64
+ { pattern: '^SAMSUNG.+', manufacturer: 'Samsung' },
65
+ { pattern: '^VIEWSON.+', manufacturer: 'ViewSonic' },
66
+ { pattern: '^SONY.+', manufacturer: 'Sony' },
67
+ { pattern: '^ACER.+', manufacturer: 'Acer' },
68
+ { pattern: '^AOC.+', manufacturer: 'AOC Monitors' },
69
+ { pattern: '^HP.+', manufacturer: 'HP' },
70
+ { pattern: '^EIZO.?', manufacturer: 'Eizo' },
71
+ { pattern: '^PHILIPS.?', manufacturer: 'Philips' },
72
+ { pattern: '^IIYAMA.?', manufacturer: 'Iiyama' },
73
+ { pattern: '^SHARP.?', manufacturer: 'Sharp' },
74
+ { pattern: '^NEC.?', manufacturer: 'NEC' },
75
+ { pattern: '^LENOVO.?', manufacturer: 'Lenovo' },
76
+ { pattern: 'COMPAQ.?', manufacturer: 'Compaq' },
77
+ { pattern: 'APPLE.?', manufacturer: 'Apple' },
78
+ { pattern: 'INTEL.?', manufacturer: 'Intel' },
79
+ { pattern: 'AMD.?', manufacturer: 'AMD' },
80
+ { pattern: 'NVIDIA.?', manufacturer: 'NVDIA' },
81
+ ];
82
+
83
+ let result = '';
84
+ if (model) {
85
+ model = model.toUpperCase();
86
+ manufacturers.forEach((manufacturer) => {
87
+ const re = RegExp(manufacturer.pattern);
88
+ if (re.test(model)) { result = manufacturer.manufacturer; }
89
+ });
90
+ }
91
+ return result;
92
+ }
93
+
94
+ function getVendorFromId(id) {
95
+ const vendors = {
96
+ '610': 'Apple',
97
+ '1e6d': 'LG',
98
+ '10ac': 'DELL',
99
+ '4dd9': 'Sony',
100
+ '38a3': 'NEC',
101
+ };
102
+ return vendors[id] || '';
103
+ }
104
+
105
+ function vendorToId(str) {
106
+ let result = '';
107
+ str = (str || '').toLowerCase();
108
+ if (str.indexOf('apple') >= 0) { result = '0x05ac'; }
109
+ else if (str.indexOf('nvidia') >= 0) { result = '0x10de'; }
110
+ else if (str.indexOf('intel') >= 0) { result = '0x8086'; }
111
+ else if (str.indexOf('ati') >= 0 || str.indexOf('amd') >= 0) { result = '0x1002'; }
112
+
113
+ return result;
114
+ }
115
+
116
+ function getMetalVersion(id) {
117
+ const families = {
118
+ 'spdisplays_mtlgpufamilymac1': 'mac1',
119
+ 'spdisplays_mtlgpufamilymac2': 'mac2',
120
+ 'spdisplays_mtlgpufamilyapple1': 'apple1',
121
+ 'spdisplays_mtlgpufamilyapple2': 'apple2',
122
+ 'spdisplays_mtlgpufamilyapple3': 'apple3',
123
+ 'spdisplays_mtlgpufamilyapple4': 'apple4',
124
+ 'spdisplays_mtlgpufamilyapple5': 'apple5',
125
+ 'spdisplays_mtlgpufamilyapple6': 'apple6',
126
+ 'spdisplays_mtlgpufamilyapple7': 'apple7',
127
+ 'spdisplays_metalfeaturesetfamily11': 'family1_v1',
128
+ 'spdisplays_metalfeaturesetfamily12': 'family1_v2',
129
+ 'spdisplays_metalfeaturesetfamily13': 'family1_v3',
130
+ 'spdisplays_metalfeaturesetfamily14': 'family1_v4',
131
+ 'spdisplays_metalfeaturesetfamily21': 'family2_v1'
132
+ };
133
+ return families[id] || '';
134
+ }
135
+
136
+ function graphics(callback) {
137
+
138
+ function parseLinesDarwin(graphicsArr) {
139
+ const res = {
140
+ controllers: [],
141
+ displays: []
142
+ };
143
+ try {
144
+ graphicsArr.forEach(function (item) {
145
+ // controllers
146
+ const bus = ((item.sppci_bus || '').indexOf('builtin') > -1 ? 'Built-In' : ((item.sppci_bus || '').indexOf('pcie') > -1 ? 'PCIe' : ''));
147
+ const vram = (parseInt((item.spdisplays_vram || ''), 10) || 0) * (((item.spdisplays_vram || '').indexOf('GB') > -1) ? 1024 : 1);
148
+ const vramDyn = (parseInt((item.spdisplays_vram_shared || ''), 10) || 0) * (((item.spdisplays_vram_shared || '').indexOf('GB') > -1) ? 1024 : 1);
149
+ let metalVersion = getMetalVersion(item.spdisplays_metal || item.spdisplays_metalfamily || '');
150
+ res.controllers.push({
151
+ vendor: getVendorFromModel(item.spdisplays_vendor || '') || item.spdisplays_vendor || '',
152
+ model: item.sppci_model || '',
153
+ bus,
154
+ vramDynamic: bus === 'Built-In',
155
+ vram: vram || vramDyn || null,
156
+ deviceId: item['spdisplays_device-id'] || '',
157
+ vendorId: item['spdisplays_vendor-id'] || vendorToId((item['spdisplays_vendor'] || '') + (item.sppci_model || '')),
158
+ external: (item.sppci_device_type === 'spdisplays_egpu'),
159
+ cores: item['sppci_cores'] || null,
160
+ metalVersion
161
+ });
162
+
163
+ // displays
164
+ if (item.spdisplays_ndrvs && item.spdisplays_ndrvs.length) {
165
+ item.spdisplays_ndrvs.forEach(function (displayItem) {
166
+ const connectionType = displayItem['spdisplays_connection_type'] || '';
167
+ const currentResolutionParts = (displayItem['_spdisplays_resolution'] || '').split('@');
168
+ const currentResolution = currentResolutionParts[0].split('x');
169
+ const pixelParts = (displayItem['_spdisplays_pixels'] || '').split('x');
170
+ const pixelDepthString = displayItem['spdisplays_depth'] || '';
171
+ const serial = displayItem['_spdisplays_display-serial-number'] || displayItem['_spdisplays_display-serial-number2'] || null;
172
+ res.displays.push({
173
+ vendor: getVendorFromId(displayItem['_spdisplays_display-vendor-id'] || '') || getVendorFromModel(displayItem['_name'] || ''),
174
+ vendorId: displayItem['_spdisplays_display-vendor-id'] || '',
175
+ model: displayItem['_name'] || '',
176
+ productionYear: displayItem['_spdisplays_display-year'] || null,
177
+ serial: serial !== '0' ? serial : null,
178
+ displayId: displayItem['_spdisplays_displayID'] || null,
179
+ main: displayItem['spdisplays_main'] ? displayItem['spdisplays_main'] === 'spdisplays_yes' : false,
180
+ builtin: (displayItem['spdisplays_display_type'] || '').indexOf('built-in') > -1,
181
+ connection: ((connectionType.indexOf('_internal') > -1) ? 'Internal' : ((connectionType.indexOf('_displayport') > -1) ? 'Display Port' : ((connectionType.indexOf('_hdmi') > -1) ? 'HDMI' : null))),
182
+ sizeX: null,
183
+ sizeY: null,
184
+ pixelDepth: (pixelDepthString === 'CGSThirtyBitColor' ? 30 : (pixelDepthString === 'CGSThirtytwoBitColor' ? 32 : (pixelDepthString === 'CGSTwentyfourBitColor' ? 24 : null))),
185
+ resolutionX: pixelParts.length > 1 ? parseInt(pixelParts[0], 10) : null,
186
+ resolutionY: pixelParts.length > 1 ? parseInt(pixelParts[1], 10) : null,
187
+ currentResX: currentResolution.length > 1 ? parseInt(currentResolution[0], 10) : null,
188
+ currentResY: currentResolution.length > 1 ? parseInt(currentResolution[1], 10) : null,
189
+ positionX: 0,
190
+ positionY: 0,
191
+ currentRefreshRate: currentResolutionParts.length > 1 ? parseInt(currentResolutionParts[1], 10) : null,
192
+
193
+ });
194
+ });
195
+ }
196
+ });
197
+ return res;
198
+ } catch (e) {
199
+ return res;
200
+ }
201
+ }
202
+
203
+ function parseLinesLinuxControllers(lines) {
204
+ let controllers = [];
205
+ let currentController = {
206
+ vendor: '',
207
+ model: '',
208
+ bus: '',
209
+ busAddress: '',
210
+ vram: null,
211
+ vramDynamic: false,
212
+ pciID: ''
213
+ };
214
+ let isGraphicsController = false;
215
+ // PCI bus IDs
216
+ let pciIDs = [];
217
+ try {
218
+ pciIDs = execSync('export LC_ALL=C; dmidecode -t 9 2>/dev/null; unset LC_ALL | grep "Bus Address: "').toString().split('\n');
219
+ for (let i = 0; i < pciIDs.length; i++) {
220
+ pciIDs[i] = pciIDs[i].replace('Bus Address:', '').replace('0000:', '').trim();
221
+ }
222
+ pciIDs = pciIDs.filter(function (el) {
223
+ return el != null && el;
224
+ });
225
+ } catch (e) {
226
+ util.noop();
227
+ }
228
+ lines.forEach((line) => {
229
+ if ('' !== line.trim()) {
230
+ if (' ' !== line[0] && '\t' !== line[0]) { // first line of new entry
231
+ let isExternal = (pciIDs.indexOf(line.split(' ')[0]) >= 0);
232
+ let vgapos = line.toLowerCase().indexOf(' vga ');
233
+ let _3dcontrollerpos = line.toLowerCase().indexOf('3d controller');
234
+ if (vgapos !== -1 || _3dcontrollerpos !== -1) { // VGA
235
+ if (_3dcontrollerpos !== -1 && vgapos === -1) {
236
+ vgapos = _3dcontrollerpos;
237
+ }
238
+ if (currentController.vendor || currentController.model || currentController.bus || currentController.vram !== null || currentController.vramDynamic) { // already a controller found
239
+ controllers.push(currentController);
240
+ currentController = {
241
+ vendor: '',
242
+ model: '',
243
+ bus: '',
244
+ busAddress: '',
245
+ vram: null,
246
+ vramDynamic: false,
247
+ };
248
+ }
249
+
250
+ const pciIDCandidate = line.split(' ')[0];
251
+ if (/[\da-fA-F]{2}:[\da-fA-F]{2}\.[\da-fA-F]/.test(pciIDCandidate)) {
252
+ currentController.busAddress = pciIDCandidate;
253
+ }
254
+ isGraphicsController = true;
255
+ let endpos = line.search(/\[[0-9a-f]{4}:[0-9a-f]{4}]|$/);
256
+ let parts = line.substr(vgapos, endpos - vgapos).split(':');
257
+ currentController.busAddress = line.substr(0, vgapos).trim();
258
+ if (parts.length > 1) {
259
+ parts[1] = parts[1].trim();
260
+ if (parts[1].toLowerCase().indexOf('corporation') >= 0) {
261
+ currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf('corporation') + 11).trim();
262
+ currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf('corporation') + 11, 200).trim().split('(')[0];
263
+ currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard';
264
+ currentController.vram = null;
265
+ currentController.vramDynamic = false;
266
+ } else if (parts[1].toLowerCase().indexOf(' inc.') >= 0) {
267
+ if ((parts[1].match(/]/g) || []).length > 1) {
268
+ currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim();
269
+ currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim();
270
+ } else {
271
+ currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' inc.') + 5).trim();
272
+ currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' inc.') + 5, 200).trim().split('(')[0].trim();
273
+ }
274
+ currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard';
275
+ currentController.vram = null;
276
+ currentController.vramDynamic = false;
277
+ } else if (parts[1].toLowerCase().indexOf(' ltd.') >= 0) {
278
+ if ((parts[1].match(/]/g) || []).length > 1) {
279
+ currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim();
280
+ currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim();
281
+ } else {
282
+ currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' ltd.') + 5).trim();
283
+ currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' ltd.') + 5, 200).trim().split('(')[0].trim();
284
+ }
285
+ }
286
+ }
287
+
288
+ } else {
289
+ isGraphicsController = false;
290
+ }
291
+ }
292
+ if (isGraphicsController) { // within VGA details
293
+ let parts = line.split(':');
294
+ if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('devicename') !== -1 && parts[1].toLowerCase().indexOf('onboard') !== -1) { currentController.bus = 'Onboard'; }
295
+ if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('region') !== -1 && parts[1].toLowerCase().indexOf('memory') !== -1) {
296
+ let memparts = parts[1].split('=');
297
+ if (memparts.length > 1) {
298
+ currentController.vram = parseInt(memparts[1]);
299
+ }
300
+ }
301
+ }
302
+ }
303
+ });
304
+
305
+ if (currentController.vendor || currentController.model || currentController.bus || currentController.busAddress || currentController.vram !== null || currentController.vramDynamic) { // already a controller found
306
+ controllers.push(currentController);
307
+ }
308
+ return (controllers);
309
+ }
310
+
311
+ function parseLinesLinuxClinfo(controllers, lines) {
312
+ const fieldPattern = /\[([^\]]+)\]\s+(\w+)\s+(.*)/;
313
+ const devices = lines.reduce((devices, line) => {
314
+ const field = fieldPattern.exec(line.trim());
315
+ if (field) {
316
+ if (!devices[field[1]]) {
317
+ devices[field[1]] = {};
318
+ }
319
+ devices[field[1]][field[2]] = field[3];
320
+ }
321
+ return devices;
322
+ }, {});
323
+ for (let deviceId in devices) {
324
+ const device = devices[deviceId];
325
+ if (device['CL_DEVICE_TYPE'] === 'CL_DEVICE_TYPE_GPU') {
326
+ let busAddress;
327
+ if (device['CL_DEVICE_TOPOLOGY_AMD']) {
328
+ const bdf = device['CL_DEVICE_TOPOLOGY_AMD'].match(/[a-zA-Z0-9]+:\d+\.\d+/);
329
+ if (bdf) {
330
+ busAddress = bdf[0];
331
+ }
332
+ } else if (device['CL_DEVICE_PCI_BUS_ID_NV'] && device['CL_DEVICE_PCI_SLOT_ID_NV']) {
333
+ const bus = parseInt(device['CL_DEVICE_PCI_BUS_ID_NV']);
334
+ const slot = parseInt(device['CL_DEVICE_PCI_SLOT_ID_NV']);
335
+ if (!isNaN(bus) && !isNaN(slot)) {
336
+ const b = bus & 0xff;
337
+ const d = (slot >> 3) & 0xff;
338
+ const f = slot & 0x07;
339
+ busAddress = `${b.toString().padStart(2, '0')}:${d.toString().padStart(2, '0')}.${f}`;
340
+ }
341
+ }
342
+ if (busAddress) {
343
+ let controller = controllers.find(controller => controller.busAddress === busAddress);
344
+ if (!controller) {
345
+ controller = {
346
+ vendor: '',
347
+ model: '',
348
+ bus: '',
349
+ busAddress,
350
+ vram: null,
351
+ vramDynamic: false
352
+ };
353
+ controllers.push(controller);
354
+ }
355
+ controller.vendor = device['CL_DEVICE_VENDOR'];
356
+ if (device['CL_DEVICE_BOARD_NAME_AMD']) {
357
+ controller.model = device['CL_DEVICE_BOARD_NAME_AMD'];
358
+ } else {
359
+ controller.model = device['CL_DEVICE_NAME'];
360
+ }
361
+ const memory = parseInt(device['CL_DEVICE_GLOBAL_MEM_SIZE']);
362
+ if (!isNaN(memory)) {
363
+ controller.vram = Math.round(memory / 1024 / 1024);
364
+ }
365
+ }
366
+ }
367
+ }
368
+ return controllers;
369
+ }
370
+
371
+ function getNvidiaSmi() {
372
+ if (_nvidiaSmiPath) {
373
+ return _nvidiaSmiPath;
374
+ }
375
+
376
+ if (_windows) {
377
+ try {
378
+ const basePath = util.WINDIR + '\\System32\\DriverStore\\FileRepository';
379
+ // find all directories that have an nvidia-smi.exe file
380
+ const candidateDirs = fs.readdirSync(basePath).filter(dir => {
381
+ return fs.readdirSync([basePath, dir].join('/')).includes('nvidia-smi.exe');
382
+ });
383
+ // use the directory with the most recently created nvidia-smi.exe file
384
+ const targetDir = candidateDirs.reduce((prevDir, currentDir) => {
385
+ const previousNvidiaSmi = fs.statSync([basePath, prevDir, 'nvidia-smi.exe'].join('/'));
386
+ const currentNvidiaSmi = fs.statSync([basePath, currentDir, 'nvidia-smi.exe'].join('/'));
387
+ return (previousNvidiaSmi.ctimeMs > currentNvidiaSmi.ctimeMs) ? prevDir : currentDir;
388
+ });
389
+
390
+ if (targetDir) {
391
+ _nvidiaSmiPath = [basePath, targetDir, 'nvidia-smi.exe'].join('/');
392
+ }
393
+ } catch (e) {
394
+ util.noop();
395
+ }
396
+ } else if (_linux) {
397
+ _nvidiaSmiPath = 'nvidia-smi';
398
+ }
399
+ return _nvidiaSmiPath;
400
+ }
401
+
402
+ function nvidiaSmi(options) {
403
+ const nvidiaSmiExe = getNvidiaSmi();
404
+ options = options || util.execOptsWin;
405
+ if (nvidiaSmiExe) {
406
+ 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';
407
+ const cmd = nvidiaSmiExe + ' ' + nvidiaSmiOpts + (_linux ? ' 2>/dev/null' : '');
408
+ try {
409
+ const res = execSync(cmd, options).toString();
410
+ return res;
411
+ } catch (e) {
412
+ util.noop();
413
+ }
414
+ }
415
+ return '';
416
+ }
417
+
418
+ function nvidiaDevices() {
419
+
420
+ function safeParseNumber(value) {
421
+ if ([null, undefined].includes(value)) {
422
+ return value;
423
+ }
424
+ return parseFloat(value);
425
+ }
426
+
427
+ const stdout = nvidiaSmi();
428
+ if (!stdout) {
429
+ return [];
430
+ }
431
+
432
+ const gpus = stdout.split('\n').filter(Boolean);
433
+ let results = gpus.map(gpu => {
434
+ const splittedData = gpu.split(', ').map(value => value.includes('N/A') ? undefined : value);
435
+ if (splittedData.length === 16) {
436
+ return {
437
+ driverVersion: splittedData[0],
438
+ subDeviceId: splittedData[1],
439
+ name: splittedData[2],
440
+ pciBus: splittedData[3],
441
+ fanSpeed: safeParseNumber(splittedData[4]),
442
+ memoryTotal: safeParseNumber(splittedData[5]),
443
+ memoryUsed: safeParseNumber(splittedData[6]),
444
+ memoryFree: safeParseNumber(splittedData[7]),
445
+ utilizationGpu: safeParseNumber(splittedData[8]),
446
+ utilizationMemory: safeParseNumber(splittedData[9]),
447
+ temperatureGpu: safeParseNumber(splittedData[10]),
448
+ temperatureMemory: safeParseNumber(splittedData[11]),
449
+ powerDraw: safeParseNumber(splittedData[12]),
450
+ powerLimit: safeParseNumber(splittedData[13]),
451
+ clockCore: safeParseNumber(splittedData[14]),
452
+ clockMemory: safeParseNumber(splittedData[15]),
453
+ };
454
+ } else {
455
+ return {};
456
+ }
457
+ });
458
+ results = results.filter((item) => {
459
+ return ('pciBus' in item);
460
+ });
461
+ return results;
462
+ }
463
+
464
+ function mergeControllerNvidia(controller, nvidia) {
465
+ if (nvidia.driverVersion) { controller.driverVersion = nvidia.driverVersion; }
466
+ if (nvidia.subDeviceId) { controller.subDeviceId = nvidia.subDeviceId; }
467
+ if (nvidia.name) { controller.name = nvidia.name; }
468
+ if (nvidia.pciBus) { controller.pciBus = nvidia.pciBus; }
469
+ if (nvidia.fanSpeed) { controller.fanSpeed = nvidia.fanSpeed; }
470
+ if (nvidia.memoryTotal) {
471
+ controller.memoryTotal = nvidia.memoryTotal;
472
+ controller.vram = nvidia.memoryTotal;
473
+ controller.vramDynamic = false;
474
+ }
475
+ if (nvidia.memoryUsed) { controller.memoryUsed = nvidia.memoryUsed; }
476
+ if (nvidia.memoryFree) { controller.memoryFree = nvidia.memoryFree; }
477
+ if (nvidia.utilizationGpu) { controller.utilizationGpu = nvidia.utilizationGpu; }
478
+ if (nvidia.utilizationMemory) { controller.utilizationMemory = nvidia.utilizationMemory; }
479
+ if (nvidia.temperatureGpu) { controller.temperatureGpu = nvidia.temperatureGpu; }
480
+ if (nvidia.temperatureMemory) { controller.temperatureMemory = nvidia.temperatureMemory; }
481
+ if (nvidia.powerDraw) { controller.powerDraw = nvidia.powerDraw; }
482
+ if (nvidia.powerLimit) { controller.powerLimit = nvidia.powerLimit; }
483
+ if (nvidia.clockCore) { controller.clockCore = nvidia.clockCore; }
484
+ if (nvidia.clockMemory) { controller.clockMemory = nvidia.clockMemory; }
485
+ return controller;
486
+ }
487
+
488
+ function parseLinesLinuxEdid(edid) {
489
+ // parsen EDID
490
+ // --> model
491
+ // --> resolutionx
492
+ // --> resolutiony
493
+ // --> builtin = false
494
+ // --> pixeldepth (?)
495
+ // --> sizex
496
+ // --> sizey
497
+ let result = {
498
+ vendor: '',
499
+ model: '',
500
+ deviceName: '',
501
+ main: false,
502
+ builtin: false,
503
+ connection: '',
504
+ sizeX: null,
505
+ sizeY: null,
506
+ pixelDepth: null,
507
+ resolutionX: null,
508
+ resolutionY: null,
509
+ currentResX: null,
510
+ currentResY: null,
511
+ positionX: 0,
512
+ positionY: 0,
513
+ currentRefreshRate: null
514
+ };
515
+ // find first "Detailed Timing Description"
516
+ let start = 108;
517
+ if (edid.substr(start, 6) === '000000') {
518
+ start += 36;
519
+ }
520
+ if (edid.substr(start, 6) === '000000') {
521
+ start += 36;
522
+ }
523
+ if (edid.substr(start, 6) === '000000') {
524
+ start += 36;
525
+ }
526
+ if (edid.substr(start, 6) === '000000') {
527
+ start += 36;
528
+ }
529
+ result.resolutionX = parseInt('0x0' + edid.substr(start + 8, 1) + edid.substr(start + 4, 2));
530
+ result.resolutionY = parseInt('0x0' + edid.substr(start + 14, 1) + edid.substr(start + 10, 2));
531
+ result.sizeX = parseInt('0x0' + edid.substr(start + 28, 1) + edid.substr(start + 24, 2));
532
+ result.sizeY = parseInt('0x0' + edid.substr(start + 29, 1) + edid.substr(start + 26, 2));
533
+ // monitor name
534
+ start = edid.indexOf('000000fc00'); // find first "Monitor Description Data"
535
+ if (start >= 0) {
536
+ let model_raw = edid.substr(start + 10, 26);
537
+ if (model_raw.indexOf('0a') !== -1) {
538
+ model_raw = model_raw.substr(0, model_raw.indexOf('0a'));
539
+ }
540
+ try {
541
+ if (model_raw.length > 2) {
542
+ result.model = model_raw.match(/.{1,2}/g).map(function (v) {
543
+ return String.fromCharCode(parseInt(v, 16));
544
+ }).join('');
545
+ }
546
+ } catch (e) {
547
+ util.noop();
548
+ }
549
+ } else {
550
+ result.model = '';
551
+ }
552
+ return result;
553
+ }
554
+
555
+ function parseLinesLinuxDisplays(lines, depth) {
556
+ let displays = [];
557
+ let currentDisplay = {
558
+ vendor: '',
559
+ model: '',
560
+ deviceName: '',
561
+ main: false,
562
+ builtin: false,
563
+ connection: '',
564
+ sizeX: null,
565
+ sizeY: null,
566
+ pixelDepth: null,
567
+ resolutionX: null,
568
+ resolutionY: null,
569
+ currentResX: null,
570
+ currentResY: null,
571
+ positionX: 0,
572
+ positionY: 0,
573
+ currentRefreshRate: null
574
+ };
575
+ let is_edid = false;
576
+ let is_current = false;
577
+ let edid_raw = '';
578
+ let start = 0;
579
+ for (let i = 1; i < lines.length; i++) { // start with second line
580
+ if ('' !== lines[i].trim()) {
581
+ if (' ' !== lines[i][0] && '\t' !== lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') !== -1) { // first line of new entry
582
+ if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // push last display to array
583
+ displays.push(currentDisplay);
584
+ currentDisplay = {
585
+ vendor: '',
586
+ model: '',
587
+ main: false,
588
+ builtin: false,
589
+ connection: '',
590
+ sizeX: null,
591
+ sizeY: null,
592
+ pixelDepth: null,
593
+ resolutionX: null,
594
+ resolutionY: null,
595
+ currentResX: null,
596
+ currentResY: null,
597
+ positionX: 0,
598
+ positionY: 0,
599
+ currentRefreshRate: null
600
+ };
601
+ }
602
+ let parts = lines[i].split(' ');
603
+ currentDisplay.connection = parts[0];
604
+ currentDisplay.main = lines[i].toLowerCase().indexOf(' primary ') >= 0;
605
+ currentDisplay.builtin = (parts[0].toLowerCase().indexOf('edp') >= 0);
606
+ }
607
+
608
+ // try to read EDID information
609
+ if (is_edid) {
610
+ if (lines[i].search(/\S|$/) > start) {
611
+ edid_raw += lines[i].toLowerCase().trim();
612
+ } else {
613
+ // parsen EDID
614
+ let edid_decoded = parseLinesLinuxEdid(edid_raw);
615
+ currentDisplay.vendor = edid_decoded.vendor;
616
+ currentDisplay.model = edid_decoded.model;
617
+ currentDisplay.resolutionX = edid_decoded.resolutionX;
618
+ currentDisplay.resolutionY = edid_decoded.resolutionY;
619
+ currentDisplay.sizeX = edid_decoded.sizeX;
620
+ currentDisplay.sizeY = edid_decoded.sizeY;
621
+ currentDisplay.pixelDepth = depth;
622
+ is_edid = false;
623
+ }
624
+ }
625
+ if (lines[i].toLowerCase().indexOf('edid:') >= 0) {
626
+ is_edid = true;
627
+ start = lines[i].search(/\S|$/);
628
+ }
629
+ if (lines[i].toLowerCase().indexOf('*current') >= 0) {
630
+ const parts1 = lines[i].split('(');
631
+ if (parts1 && parts1.length > 1 && parts1[0].indexOf('x') >= 0) {
632
+ const resParts = parts1[0].trim().split('x');
633
+ currentDisplay.currentResX = util.toInt(resParts[0]);
634
+ currentDisplay.currentResY = util.toInt(resParts[1]);
635
+ }
636
+ is_current = true;
637
+ }
638
+ if (is_current && lines[i].toLowerCase().indexOf('clock') >= 0 && lines[i].toLowerCase().indexOf('hz') >= 0 && lines[i].toLowerCase().indexOf('v: height') >= 0) {
639
+ const parts1 = lines[i].split('clock');
640
+ if (parts1 && parts1.length > 1 && parts1[1].toLowerCase().indexOf('hz') >= 0) {
641
+ currentDisplay.currentRefreshRate = util.toInt(parts1[1]);
642
+ }
643
+ is_current = false;
644
+ }
645
+ }
646
+ }
647
+
648
+ // pushen displays
649
+ if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // still information there
650
+ displays.push(currentDisplay);
651
+ }
652
+ return displays;
653
+ }
654
+
655
+ // function starts here
656
+ return new Promise((resolve) => {
657
+ process.nextTick(() => {
658
+ let result = {
659
+ controllers: [],
660
+ displays: []
661
+ };
662
+ if (_darwin) {
663
+ let cmd = 'system_profiler -xml -detailLevel full SPDisplaysDataType';
664
+ exec(cmd, function (error, stdout) {
665
+ if (!error) {
666
+ try {
667
+ const output = stdout.toString();
668
+ result = parseLinesDarwin(util.plistParser(output)[0]._items);
669
+ } catch (e) {
670
+ util.noop();
671
+ }
672
+ 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 });
673
+ const output = (stdout || '').toString();
674
+ const obj = util.plistReader(output);
675
+ if (obj['DisplayAnyUserSets'] && obj['DisplayAnyUserSets']['Configs'] && obj['DisplayAnyUserSets']['Configs'][0] && obj['DisplayAnyUserSets']['Configs'][0]['DisplayConfig']) {
676
+ const current = obj['DisplayAnyUserSets']['Configs'][0]['DisplayConfig'];
677
+ let i = 0;
678
+ current.forEach((o) => {
679
+ if (o['CurrentInfo'] && o['CurrentInfo']['OriginX'] !== undefined && result.displays && result.displays[i]) {
680
+ result.displays[i].positionX = o['CurrentInfo']['OriginX'];
681
+ }
682
+ if (o['CurrentInfo'] && o['CurrentInfo']['OriginY'] !== undefined && result.displays && result.displays[i]) {
683
+ result.displays[i].positionY = o['CurrentInfo']['OriginY'];
684
+ }
685
+ i++;
686
+ });
687
+ }
688
+ if (obj['DisplayAnyUserSets'] && obj['DisplayAnyUserSets'].length > 0 && obj['DisplayAnyUserSets'][0].length > 0 && obj['DisplayAnyUserSets'][0][0]['DisplayID']) {
689
+ const current = obj['DisplayAnyUserSets'][0];
690
+ let i = 0;
691
+ current.forEach((o) => {
692
+ if ('OriginX' in o && result.displays && result.displays[i]) {
693
+ result.displays[i].positionX = o['OriginX'];
694
+ }
695
+ if ('OriginY' in o && result.displays && result.displays[i]) {
696
+ result.displays[i].positionY = o['OriginY'];
697
+ }
698
+ if (o['Mode'] && o['Mode']['BitsPerPixel'] !== undefined && result.displays && result.displays[i]) {
699
+ result.displays[i].pixelDepth = o['Mode']['BitsPerPixel'];
700
+ }
701
+ i++;
702
+ });
703
+ }
704
+
705
+ }
706
+ if (callback) {
707
+ callback(result);
708
+ }
709
+ resolve(result);
710
+ });
711
+ }
712
+ if (_linux) {
713
+ // Raspberry: https://elinux.org/RPI_vcgencmd_usage
714
+ if (util.isRaspberry() && util.isRaspbian()) {
715
+ let cmd = 'fbset -s | grep \'mode "\'; vcgencmd get_mem gpu; tvservice -s; tvservice -n;';
716
+ exec(cmd, function (error, stdout) {
717
+ let lines = stdout.toString().split('\n');
718
+ if (lines.length > 3 && lines[0].indexOf('mode "') >= -1 && lines[2].indexOf('0x12000a') > -1) {
719
+ const parts = lines[0].replace('mode', '').replace(/"/g, '').trim().split('x');
720
+ if (parts.length === 2) {
721
+ result.displays.push({
722
+ vendor: '',
723
+ model: util.getValue(lines, 'device_name', '='),
724
+ main: true,
725
+ builtin: false,
726
+ connection: 'HDMI',
727
+ sizeX: null,
728
+ sizeY: null,
729
+ pixelDepth: null,
730
+ resolutionX: parseInt(parts[0], 10),
731
+ resolutionY: parseInt(parts[1], 10),
732
+ currentResX: null,
733
+ currentResY: null,
734
+ positionX: 0,
735
+ positionY: 0,
736
+ currentRefreshRate: null
737
+ });
738
+ }
739
+ }
740
+ if (lines.length > 1 && stdout.toString().indexOf('gpu=') >= -1) {
741
+ result.controllers.push({
742
+ vendor: 'Broadcom',
743
+ model: 'VideoCore IV',
744
+ bus: '',
745
+ vram: util.getValue(lines, 'gpu', '=').replace('M', ''),
746
+ vramDynamic: true
747
+ });
748
+ }
749
+ if (callback) {
750
+ callback(result);
751
+ }
752
+ resolve(result);
753
+ });
754
+ } else {
755
+ let cmd = 'lspci -vvv 2>/dev/null';
756
+ exec(cmd, function (error, stdout) {
757
+ if (!error) {
758
+ let lines = stdout.toString().split('\n');
759
+ result.controllers = parseLinesLinuxControllers(lines);
760
+ const nvidiaData = nvidiaDevices();
761
+ // needs to be rewritten ... using no spread operators
762
+ result.controllers = result.controllers.map((controller) => { // match by busAddress
763
+ return mergeControllerNvidia(controller, nvidiaData.find((contr) => contr.pciBus.toLowerCase().endsWith(controller.busAddress.toLowerCase())) || {});
764
+ });
765
+ }
766
+ let cmd = 'clinfo --raw';
767
+ exec(cmd, function (error, stdout) {
768
+ if (!error) {
769
+ let lines = stdout.toString().split('\n');
770
+ result.controllers = parseLinesLinuxClinfo(result.controllers, lines);
771
+ }
772
+ let cmd = 'xdpyinfo 2>/dev/null | grep \'depth of root window\' | awk \'{ print $5 }\'';
773
+ exec(cmd, function (error, stdout) {
774
+ let depth = 0;
775
+ if (!error) {
776
+ let lines = stdout.toString().split('\n');
777
+ depth = parseInt(lines[0]) || 0;
778
+ }
779
+ let cmd = 'xrandr --verbose 2>/dev/null';
780
+ exec(cmd, function (error, stdout) {
781
+ if (!error) {
782
+ let lines = stdout.toString().split('\n');
783
+ result.displays = parseLinesLinuxDisplays(lines, depth);
784
+ }
785
+ if (callback) {
786
+ callback(result);
787
+ }
788
+ resolve(result);
789
+ });
790
+ });
791
+ });
792
+ });
793
+ }
794
+ }
795
+ if (_freebsd || _openbsd || _netbsd) {
796
+ if (callback) { callback(null); }
797
+ resolve(null);
798
+ }
799
+ if (_sunos) {
800
+ if (callback) { callback(null); }
801
+ resolve(null);
802
+ }
803
+ if (_windows) {
804
+
805
+ // https://blogs.technet.microsoft.com/heyscriptingguy/2013/10/03/use-powershell-to-discover-multi-monitor-information/
806
+ // https://devblogs.microsoft.com/scripting/use-powershell-to-discover-multi-monitor-information/
807
+ try {
808
+ const workload = [];
809
+ workload.push(util.powerShell('Get-CimInstance win32_VideoController | fl *'));
810
+ 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'));
811
+ workload.push(util.powerShell('Get-CimInstance win32_desktopmonitor | fl *'));
812
+ workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorBasicDisplayParams | fl'));
813
+ workload.push(util.powerShell('Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::AllScreens'));
814
+ workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorConnectionParams | fl'));
815
+ 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}'));
816
+
817
+ const nvidiaData = nvidiaDevices();
818
+
819
+ Promise.all(
820
+ workload
821
+ ).then((data) => {
822
+ // controller + vram
823
+ let csections = data[0].replace(/\r/g, '').split(/\n\s*\n/);
824
+ let vsections = data[1].replace(/\r/g, '').split(/\n\s*\n/);
825
+ result.controllers = parseLinesWindowsControllers(csections, vsections);
826
+ result.controllers = result.controllers.map((controller) => { // match by subDeviceId
827
+ if (controller.vendor.toLowerCase() === 'nvidia') {
828
+ return mergeControllerNvidia(controller, nvidiaData.find(device => {
829
+ let windowsSubDeviceId = (controller.subDeviceId || '').toLowerCase();
830
+ const nvidiaSubDeviceIdParts = device.subDeviceId.split('x');
831
+ let nvidiaSubDeviceId = nvidiaSubDeviceIdParts.length > 1 ? nvidiaSubDeviceIdParts[1].toLowerCase() : nvidiaSubDeviceIdParts[0].toLowerCase();
832
+ const lengthDifference = Math.abs(windowsSubDeviceId.length - nvidiaSubDeviceId.length);
833
+ if (windowsSubDeviceId.length > nvidiaSubDeviceId.length) {
834
+ for (let i = 0; i < lengthDifference; i++) {
835
+ nvidiaSubDeviceId = '0' + nvidiaSubDeviceId;
836
+ }
837
+ } else if (windowsSubDeviceId.length < nvidiaSubDeviceId.length) {
838
+ for (let i = 0; i < lengthDifference; i++) {
839
+ windowsSubDeviceId = '0' + windowsSubDeviceId;
840
+ }
841
+ }
842
+ return windowsSubDeviceId === nvidiaSubDeviceId;
843
+ }) || {});
844
+ } else {
845
+ return controller;
846
+ }
847
+ });
848
+
849
+ // displays
850
+ let dsections = data[2].replace(/\r/g, '').split(/\n\s*\n/);
851
+ // result.displays = parseLinesWindowsDisplays(dsections);
852
+ if (dsections[0].trim() === '') { dsections.shift(); }
853
+ if (dsections.length && dsections[dsections.length - 1].trim() === '') { dsections.pop(); }
854
+
855
+ // monitor (powershell)
856
+ let msections = data[3].replace(/\r/g, '').split('Active ');
857
+ msections.shift();
858
+
859
+ // forms.screens (powershell)
860
+ let ssections = data[4].replace(/\r/g, '').split('BitsPerPixel ');
861
+ ssections.shift();
862
+
863
+ // connection params (powershell) - video type
864
+ let tsections = data[5].replace(/\r/g, '').split(/\n\s*\n/);
865
+ tsections.shift();
866
+
867
+ // monitor ID (powershell) - model / vendor
868
+ const res = data[6].replace(/\r/g, '').split(/\n/);
869
+ let isections = [];
870
+ res.forEach(element => {
871
+ const parts = element.split('|');
872
+ if (parts.length === 5) {
873
+ isections.push({
874
+ vendor: parts[0],
875
+ code: parts[1],
876
+ model: parts[2],
877
+ serial: parts[3],
878
+ instanceId: parts[4]
879
+ });
880
+ }
881
+ });
882
+
883
+ result.displays = parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections);
884
+
885
+ if (result.displays.length === 1) {
886
+ if (_resolutionX) {
887
+ result.displays[0].resolutionX = _resolutionX;
888
+ if (!result.displays[0].currentResX) {
889
+ result.displays[0].currentResX = _resolutionX;
890
+ }
891
+ }
892
+ if (_resolutionY) {
893
+ result.displays[0].resolutionY = _resolutionY;
894
+ if (result.displays[0].currentResY === 0) {
895
+ result.displays[0].currentResY = _resolutionY;
896
+ }
897
+ }
898
+ if (_pixelDepth) {
899
+ result.displays[0].pixelDepth = _pixelDepth;
900
+ }
901
+ }
902
+ result.displays = result.displays.map(element => {
903
+ if (_refreshRate && !element.currentRefreshRate) {
904
+ element.currentRefreshRate = _refreshRate;
905
+ }
906
+ return element;
907
+ });
908
+
909
+ if (callback) {
910
+ callback(result);
911
+ }
912
+ resolve(result);
913
+ })
914
+ .catch(() => {
915
+ if (callback) {
916
+ callback(result);
917
+ }
918
+ resolve(result);
919
+ });
920
+ } catch (e) {
921
+ if (callback) { callback(result); }
922
+ resolve(result);
923
+ }
924
+ }
925
+ });
926
+ });
927
+
928
+ function parseLinesWindowsControllers(sections, vections) {
929
+ const memorySizes = {};
930
+ for (const i in vections) {
931
+ if ({}.hasOwnProperty.call(vections, i)) {
932
+ if (vections[i].trim() !== '') {
933
+ const lines = vections[i].trim().split('\n');
934
+ const matchingDeviceId = util.getValue(lines, 'MatchingDeviceId').match(/PCI\\(VEN_[0-9A-F]{4})&(DEV_[0-9A-F]{4})(?:&(SUBSYS_[0-9A-F]{8}))?(?:&(REV_[0-9A-F]{2}))?/i);
935
+ if (matchingDeviceId) {
936
+ const quadWordmemorySize = parseInt(util.getValue(lines, 'HardwareInformation.qwMemorySize'));
937
+ if (!isNaN(quadWordmemorySize)) {
938
+ let deviceId = matchingDeviceId[1].toUpperCase() + '&' + matchingDeviceId[2].toUpperCase();
939
+ if (matchingDeviceId[3]) {
940
+ deviceId += '&' + matchingDeviceId[3].toUpperCase();
941
+ }
942
+ if (matchingDeviceId[4]) {
943
+ deviceId += '&' + matchingDeviceId[4].toUpperCase();
944
+ }
945
+ memorySizes[deviceId] = quadWordmemorySize;
946
+ }
947
+ }
948
+ }
949
+ }
950
+ }
951
+
952
+ let controllers = [];
953
+ for (let i in sections) {
954
+ if ({}.hasOwnProperty.call(sections, i)) {
955
+ if (sections[i].trim() !== '') {
956
+ let lines = sections[i].trim().split('\n');
957
+ 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);
958
+ let subDeviceId = null;
959
+ let memorySize = null;
960
+ if (pnpDeviceId) {
961
+ subDeviceId = pnpDeviceId[3] || '';
962
+ if (subDeviceId) {
963
+ subDeviceId = subDeviceId.split('_')[1];
964
+ }
965
+
966
+ // Match PCI device identifier (there's an order of increasing generality):
967
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-pci-devices
968
+
969
+ // PCI\VEN_v(4)&DEV_d(4)&SUBSYS_s(4)n(4)&REV_r(2)
970
+ if (memorySize == null && pnpDeviceId[3] && pnpDeviceId[4]) {
971
+ const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[3].toUpperCase() + '&' + pnpDeviceId[4].toUpperCase();
972
+ if ({}.hasOwnProperty.call(memorySizes, deviceId)) {
973
+ memorySize = memorySizes[deviceId];
974
+ }
975
+ }
976
+
977
+ // PCI\VEN_v(4)&DEV_d(4)&SUBSYS_s(4)n(4)
978
+ if (memorySize == null && pnpDeviceId[3]) {
979
+ const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[3].toUpperCase();
980
+ if ({}.hasOwnProperty.call(memorySizes, deviceId)) {
981
+ memorySize = memorySizes[deviceId];
982
+ }
983
+ }
984
+
985
+ // PCI\VEN_v(4)&DEV_d(4)&REV_r(2)
986
+ if (memorySize == null && pnpDeviceId[4]) {
987
+ const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[4].toUpperCase();
988
+ if ({}.hasOwnProperty.call(memorySizes, deviceId)) {
989
+ memorySize = memorySizes[deviceId];
990
+ }
991
+ }
992
+
993
+ // PCI\VEN_v(4)&DEV_d(4)
994
+ if (memorySize == null) {
995
+ const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase();
996
+ if ({}.hasOwnProperty.call(memorySizes, deviceId)) {
997
+ memorySize = memorySizes[deviceId];
998
+ }
999
+ }
1000
+ }
1001
+
1002
+ controllers.push({
1003
+ vendor: util.getValue(lines, 'AdapterCompatibility', ':'),
1004
+ model: util.getValue(lines, 'name', ':'),
1005
+ bus: util.getValue(lines, 'PNPDeviceID', ':').startsWith('PCI') ? 'PCI' : '',
1006
+ vram: (memorySize == null ? util.toInt(util.getValue(lines, 'AdapterRAM', ':')) : memorySize) / 1024 / 1024,
1007
+ vramDynamic: (util.getValue(lines, 'VideoMemoryType', ':') === '2'),
1008
+ subDeviceId
1009
+ });
1010
+ _resolutionX = util.toInt(util.getValue(lines, 'CurrentHorizontalResolution', ':')) || _resolutionX;
1011
+ _resolutionY = util.toInt(util.getValue(lines, 'CurrentVerticalResolution', ':')) || _resolutionY;
1012
+ _refreshRate = util.toInt(util.getValue(lines, 'CurrentRefreshRate', ':')) || _refreshRate;
1013
+ _pixelDepth = util.toInt(util.getValue(lines, 'CurrentBitsPerPixel', ':')) || _pixelDepth;
1014
+ }
1015
+ }
1016
+ }
1017
+ return controllers;
1018
+ }
1019
+
1020
+ function parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections) {
1021
+ let displays = [];
1022
+ let vendor = '';
1023
+ let model = '';
1024
+ let deviceID = '';
1025
+ let resolutionX = 0;
1026
+ let resolutionY = 0;
1027
+ if (dsections && dsections.length) {
1028
+ let linesDisplay = dsections[0].split('\n');
1029
+ vendor = util.getValue(linesDisplay, 'MonitorManufacturer', ':');
1030
+ model = util.getValue(linesDisplay, 'Name', ':');
1031
+ deviceID = util.getValue(linesDisplay, 'PNPDeviceID', ':').replace(/&amp;/g, '&').toLowerCase();
1032
+ resolutionX = util.toInt(util.getValue(linesDisplay, 'ScreenWidth', ':'));
1033
+ resolutionY = util.toInt(util.getValue(linesDisplay, 'ScreenHeight', ':'));
1034
+ }
1035
+ for (let i = 0; i < ssections.length; i++) {
1036
+ if (ssections[i].trim() !== '') {
1037
+ ssections[i] = 'BitsPerPixel ' + ssections[i];
1038
+ msections[i] = 'Active ' + msections[i];
1039
+ // tsections can be empty OR undefined on earlier versions of powershell (<=2.0)
1040
+ // Tag connection type as UNKNOWN by default if this information is missing
1041
+ if (tsections.length === 0 || tsections[i] === undefined) {
1042
+ tsections[i] = 'Unknown';
1043
+ }
1044
+ let linesScreen = ssections[i].split('\n');
1045
+ let linesMonitor = msections[i].split('\n');
1046
+
1047
+ let linesConnection = tsections[i].split('\n');
1048
+ const bitsPerPixel = util.getValue(linesScreen, 'BitsPerPixel');
1049
+ const bounds = util.getValue(linesScreen, 'Bounds').replace('{', '').replace('}', '').replace(/=/g, ':').split(',');
1050
+ const primary = util.getValue(linesScreen, 'Primary');
1051
+ const sizeX = util.getValue(linesMonitor, 'MaxHorizontalImageSize');
1052
+ const sizeY = util.getValue(linesMonitor, 'MaxVerticalImageSize');
1053
+ const instanceName = util.getValue(linesMonitor, 'InstanceName').toLowerCase();
1054
+ const videoOutputTechnology = util.getValue(linesConnection, 'VideoOutputTechnology');
1055
+ const deviceName = util.getValue(linesScreen, 'DeviceName');
1056
+ let displayVendor = '';
1057
+ let displayModel = '';
1058
+ isections.forEach(element => {
1059
+ if (element.instanceId.toLowerCase().startsWith(instanceName) && vendor.startsWith('(') && model.startsWith('PnP')) {
1060
+ displayVendor = element.vendor;
1061
+ displayModel = element.model;
1062
+ }
1063
+ });
1064
+ displays.push({
1065
+ vendor: instanceName.startsWith(deviceID) && displayVendor === '' ? vendor : displayVendor,
1066
+ model: instanceName.startsWith(deviceID) && displayModel === '' ? model : displayModel,
1067
+ deviceName,
1068
+ main: primary.toLowerCase() === 'true',
1069
+ builtin: videoOutputTechnology === '2147483648',
1070
+ connection: videoOutputTechnology && videoTypes[videoOutputTechnology] ? videoTypes[videoOutputTechnology] : '',
1071
+ resolutionX: util.toInt(util.getValue(bounds, 'Width', ':')),
1072
+ resolutionY: util.toInt(util.getValue(bounds, 'Height', ':')),
1073
+ sizeX: sizeX ? parseInt(sizeX, 10) : null,
1074
+ sizeY: sizeY ? parseInt(sizeY, 10) : null,
1075
+ pixelDepth: bitsPerPixel,
1076
+ currentResX: util.toInt(util.getValue(bounds, 'Width', ':')),
1077
+ currentResY: util.toInt(util.getValue(bounds, 'Height', ':')),
1078
+ positionX: util.toInt(util.getValue(bounds, 'X', ':')),
1079
+ positionY: util.toInt(util.getValue(bounds, 'Y', ':')),
1080
+ });
1081
+ }
1082
+ }
1083
+ if (ssections.length === 0) {
1084
+ displays.push({
1085
+ vendor,
1086
+ model,
1087
+ main: true,
1088
+ sizeX: null,
1089
+ sizeY: null,
1090
+ resolutionX,
1091
+ resolutionY,
1092
+ pixelDepth: null,
1093
+ currentResX: resolutionX,
1094
+ currentResY: resolutionY,
1095
+ positionX: 0,
1096
+ positionY: 0
1097
+ });
1098
+ }
1099
+ return displays;
1100
+ }
1101
+ }
1102
+
1103
+ exports.graphics = graphics;