Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. 'use strict';
  2. // @ts-check
  3. // ==================================================================================
  4. // graphics.js
  5. // ----------------------------------------------------------------------------------
  6. // Description: System Information - library
  7. // for Node.js
  8. // Copyright: (c) 2014 - 2020
  9. // Author: Sebastian Hildebrandt
  10. // ----------------------------------------------------------------------------------
  11. // License: MIT
  12. // ==================================================================================
  13. // 7. Graphics (controller, display)
  14. // ----------------------------------------------------------------------------------
  15. const os = require('os');
  16. const exec = require('child_process').exec;
  17. const execSync = require('child_process').execSync;
  18. const util = require('./util');
  19. let _platform = process.platform;
  20. const _linux = (_platform === 'linux');
  21. const _darwin = (_platform === 'darwin');
  22. const _windows = (_platform === 'win32');
  23. const _freebsd = (_platform === 'freebsd');
  24. const _openbsd = (_platform === 'openbsd');
  25. const _netbsd = (_platform === 'netbsd');
  26. const _sunos = (_platform === 'sunos');
  27. let _resolutionx = 0;
  28. let _resolutiony = 0;
  29. let _pixeldepth = 0;
  30. let _refreshrate = 0;
  31. const videoTypes = {
  32. '-2': 'UNINITIALIZED',
  33. '-1': 'OTHER',
  34. '0': 'HD15',
  35. '1': 'SVIDEO',
  36. '2': 'Composite video',
  37. '3': 'Component video',
  38. '4': 'DVI',
  39. '5': 'HDMI',
  40. '6': 'LVDS',
  41. '8': 'D_JPN',
  42. '9': 'SDI',
  43. '10': 'DP',
  44. '11': 'DP embedded',
  45. '12': 'UDI',
  46. '13': 'UDI embedded',
  47. '14': 'SDTVDONGLE',
  48. '15': 'MIRACAST',
  49. '2147483648': 'INTERNAL'
  50. };
  51. function graphics(callback) {
  52. function parseLinesDarwin(lines) {
  53. let starts = [];
  54. let level = -1;
  55. let lastlevel = -1;
  56. let controllers = [];
  57. let displays = [];
  58. let currentController = {
  59. vendor: '',
  60. model: '',
  61. bus: '',
  62. vram: -1,
  63. vramDynamic: false
  64. };
  65. let currentDisplay = {
  66. vendor: '',
  67. model: '',
  68. main: false,
  69. builtin: false,
  70. connection: '',
  71. sizex: -1,
  72. sizey: -1,
  73. pixeldepth: -1,
  74. resolutionx: -1,
  75. resolutiony: -1,
  76. currentResX: -1,
  77. currentResY: -1,
  78. positionX: 0,
  79. positionY: 0,
  80. currentRefreshRate: -1
  81. };
  82. for (let i = 0; i < lines.length; i++) {
  83. if ('' !== lines[i].trim()) {
  84. let start = lines[i].search(/\S|$/);
  85. if (-1 === starts.indexOf(start)) {
  86. starts.push(start);
  87. }
  88. level = starts.indexOf(start);
  89. if (level < lastlevel) {
  90. if (Object.keys(currentController).length > 0) {// just changed to Displays
  91. controllers.push(currentController);
  92. currentController = {
  93. vendor: '',
  94. model: '',
  95. bus: '',
  96. vram: -1,
  97. vramDynamic: false
  98. };
  99. }
  100. if (Object.keys(currentDisplay).length > 0) {// just changed to Displays
  101. displays.push(currentDisplay);
  102. currentDisplay = {
  103. vendor: '',
  104. model: '',
  105. main: false,
  106. builtin: false,
  107. connection: '',
  108. sizex: -1,
  109. sizey: -1,
  110. pixeldepth: -1,
  111. resolutionx: -1,
  112. resolutiony: -1,
  113. currentResX: -1,
  114. currentResY: -1,
  115. positionX: 0,
  116. positionY: 0,
  117. currentRefreshRate: -1
  118. };
  119. }
  120. }
  121. lastlevel = level;
  122. let parts = lines[i].split(':');
  123. if (2 === level) { // grafics controller level
  124. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('chipsetmodel') !== -1) currentController.model = parts[1].trim();
  125. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('bus') !== -1) currentController.bus = parts[1].trim();
  126. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('vendor') !== -1) currentController.vendor = parts[1].split('(')[0].trim();
  127. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('vram(total)') !== -1) {
  128. currentController.vram = parseInt(parts[1]); // in MB
  129. if (parts[1].toLowerCase().indexOf('gb') !== -1) {
  130. currentController.vram = currentController.vram * 1024;
  131. }
  132. currentController.vramDynamic = false;
  133. }
  134. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('vram(dynamic,max)') !== -1) {
  135. currentController.vram = parseInt(parts[1]); // in MB
  136. if (parts[1].toLowerCase().indexOf('gb') !== -1) {
  137. currentController.vram = currentController.vram * 1024;
  138. }
  139. currentController.vramDynamic = true;
  140. }
  141. }
  142. if (3 === level) { // display controller level
  143. if (parts.length > 1 && '' === parts[1]) {
  144. currentDisplay.vendor = '';
  145. currentDisplay.model = parts[0].trim();
  146. currentDisplay.main = false;
  147. currentDisplay.builtin = false;
  148. currentDisplay.connection = '';
  149. currentDisplay.sizex = -1;
  150. currentDisplay.sizey = -1;
  151. currentDisplay.positionX = 0;
  152. currentDisplay.positionY = 0;
  153. currentDisplay.pixeldepth = -1;
  154. }
  155. }
  156. if (4 === level) { // display controller details level
  157. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('resolution') !== -1) {
  158. let resolution = parts[1].split('x');
  159. currentDisplay.resolutionx = (resolution.length > 1 ? parseInt(resolution[0]) : 0);
  160. currentDisplay.resolutiony = (resolution.length > 1 ? parseInt(resolution[1]) : 0);
  161. currentDisplay.currentResX = currentDisplay.resolutionx;
  162. currentDisplay.currentResY = currentDisplay.resolutiony;
  163. }
  164. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('pixeldepth') !== -1) currentDisplay.pixeldepth = parseInt(parts[1]); // in BIT
  165. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('framebufferdepth') !== -1) currentDisplay.pixeldepth = parseInt(parts[1]); // in BIT
  166. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('maindisplay') !== -1 && parts[1].replace(/ +/g, '').toLowerCase() === 'yes') currentDisplay.main = true;
  167. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('built-in') !== -1 && parts[1].replace(/ +/g, '').toLowerCase() === 'yes') {
  168. currentDisplay.builtin = true;
  169. currentDisplay.connection = '';
  170. }
  171. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('connectiontype') !== -1) {
  172. currentDisplay.builtin = false;
  173. currentDisplay.connection = parts[1].trim();
  174. }
  175. }
  176. }
  177. }
  178. if (Object.keys(currentController).length > 0) {// just changed to Displays
  179. controllers.push(currentController);
  180. }
  181. if (Object.keys(currentDisplay).length > 0) {// just changed to Displays
  182. displays.push(currentDisplay);
  183. }
  184. return ({
  185. controllers: controllers,
  186. displays: displays
  187. });
  188. }
  189. function parseLinesLinuxControllers(lines) {
  190. let controllers = [];
  191. let currentController = {
  192. vendor: '',
  193. model: '',
  194. bus: '',
  195. vram: -1,
  196. vramDynamic: false
  197. };
  198. let isGraphicsController = false;
  199. // PCI bus IDs
  200. let pciIDs = [];
  201. try {
  202. pciIDs = execSync('export LC_ALL=C; dmidecode -t 9 2>/dev/null; unset LC_ALL | grep "Bus Address: "').toString().split('\n');
  203. for (let i = 0; i < pciIDs.length; i++) {
  204. pciIDs[i] = pciIDs[i].replace('Bus Address:', '').replace('0000:', '').trim();
  205. }
  206. pciIDs = pciIDs.filter(function (el) {
  207. return el != null && el;
  208. });
  209. } catch (e) {
  210. util.noop();
  211. }
  212. for (let i = 0; i < lines.length; i++) {
  213. if ('' !== lines[i].trim()) {
  214. if (' ' !== lines[i][0] && '\t' !== lines[i][0]) { // first line of new entry
  215. let isExternal = (pciIDs.indexOf(lines[i].split(' ')[0]) >= 0);
  216. let vgapos = lines[i].toLowerCase().indexOf(' vga ');
  217. let _3dcontrollerpos = lines[i].toLowerCase().indexOf('3d controller');
  218. if (vgapos !== -1 || _3dcontrollerpos !== -1) { // VGA
  219. if (_3dcontrollerpos !== -1 && vgapos === -1) {
  220. vgapos = _3dcontrollerpos;
  221. }
  222. if (currentController.vendor || currentController.model || currentController.bus || currentController.vram !== -1 || currentController.vramDynamic) { // already a controller found
  223. controllers.push(currentController);
  224. currentController = {
  225. vendor: '',
  226. model: '',
  227. bus: '',
  228. vram: -1,
  229. vramDynamic: false
  230. };
  231. }
  232. isGraphicsController = true;
  233. let endpos = lines[i].search(/\[[0-9a-f]{4}:[0-9a-f]{4}]|$/);
  234. let parts = lines[i].substr(vgapos, endpos - vgapos).split(':');
  235. if (parts.length > 1) {
  236. parts[1] = parts[1].trim();
  237. if (parts[1].toLowerCase().indexOf('corporation') >= 0) {
  238. currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf('corporation') + 11).trim();
  239. currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf('corporation') + 11, 200).trim().split('(')[0];
  240. currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard';
  241. currentController.vram = -1;
  242. currentController.vramDynamic = false;
  243. } else if (parts[1].toLowerCase().indexOf(' inc.') >= 0) {
  244. if ((parts[1].match(new RegExp(']', 'g')) || []).length > 1) {
  245. currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim();
  246. currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim();
  247. } else {
  248. currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' inc.') + 5).trim();
  249. currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' inc.') + 5, 200).trim().split('(')[0].trim();
  250. }
  251. currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard';
  252. currentController.vram = -1;
  253. currentController.vramDynamic = false;
  254. } else if (parts[1].toLowerCase().indexOf(' ltd.') >= 0) {
  255. if ((parts[1].match(new RegExp(']', 'g')) || []).length > 1) {
  256. currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim();
  257. currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim();
  258. } else {
  259. currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' ltd.') + 5).trim();
  260. currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' ltd.') + 5, 200).trim().split('(')[0].trim();
  261. }
  262. }
  263. }
  264. } else {
  265. isGraphicsController = false;
  266. }
  267. }
  268. if (isGraphicsController) { // within VGA details
  269. let parts = lines[i].split(':');
  270. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('devicename') !== -1 && parts[1].toLowerCase().indexOf('onboard') !== -1) currentController.bus = 'Onboard';
  271. if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('region') !== -1 && parts[1].toLowerCase().indexOf('memory') !== -1) {
  272. let memparts = parts[1].split('=');
  273. if (memparts.length > 1) {
  274. currentController.vram = parseInt(memparts[1]);
  275. }
  276. }
  277. }
  278. }
  279. }
  280. if (currentController.vendor || currentController.model || currentController.bus || currentController.vram !== -1 || currentController.vramDynamic) { // already a controller found
  281. controllers.push(currentController);
  282. }
  283. return (controllers);
  284. }
  285. function parseLinesLinuxEdid(edid) {
  286. // parsen EDID
  287. // --> model
  288. // --> resolutionx
  289. // --> resolutiony
  290. // --> builtin = false
  291. // --> pixeldepth (?)
  292. // --> sizex
  293. // --> sizey
  294. let result = {
  295. vendor: '',
  296. model: '',
  297. main: false,
  298. builtin: false,
  299. connection: '',
  300. sizex: -1,
  301. sizey: -1,
  302. pixeldepth: -1,
  303. resolutionx: -1,
  304. resolutiony: -1,
  305. currentResX: -1,
  306. currentResY: -1,
  307. positionX: 0,
  308. positionY: 0,
  309. currentRefreshRate: -1
  310. };
  311. // find first "Detailed Timing Description"
  312. let start = 108;
  313. if (edid.substr(start, 6) === '000000') {
  314. start += 36;
  315. }
  316. if (edid.substr(start, 6) === '000000') {
  317. start += 36;
  318. }
  319. if (edid.substr(start, 6) === '000000') {
  320. start += 36;
  321. }
  322. if (edid.substr(start, 6) === '000000') {
  323. start += 36;
  324. }
  325. result.resolutionx = parseInt('0x0' + edid.substr(start + 8, 1) + edid.substr(start + 4, 2));
  326. result.resolutiony = parseInt('0x0' + edid.substr(start + 14, 1) + edid.substr(start + 10, 2));
  327. result.sizex = parseInt('0x0' + edid.substr(start + 28, 1) + edid.substr(start + 24, 2));
  328. result.sizey = parseInt('0x0' + edid.substr(start + 29, 1) + edid.substr(start + 26, 2));
  329. // monitor name
  330. start = edid.indexOf('000000fc00'); // find first "Monitor Description Data"
  331. if (start >= 0) {
  332. let model_raw = edid.substr(start + 10, 26);
  333. if (model_raw.indexOf('0a') !== -1) {
  334. model_raw = model_raw.substr(0, model_raw.indexOf('0a'));
  335. }
  336. try {
  337. if (model_raw.length > 2) {
  338. result.model = model_raw.match(/.{1,2}/g).map(function (v) {
  339. return String.fromCharCode(parseInt(v, 16));
  340. }).join('');
  341. }
  342. } catch (e) {
  343. util.noop();
  344. }
  345. } else {
  346. result.model = '';
  347. }
  348. return result;
  349. }
  350. function parseLinesLinuxDisplays(lines, depth) {
  351. let displays = [];
  352. let currentDisplay = {
  353. vendor: '',
  354. model: '',
  355. main: false,
  356. builtin: false,
  357. connection: '',
  358. sizex: -1,
  359. sizey: -1,
  360. pixeldepth: -1,
  361. resolutionx: -1,
  362. resolutiony: -1,
  363. currentResX: -1,
  364. currentResY: -1,
  365. positionX: 0,
  366. positionY: 0,
  367. currentRefreshRate: -1
  368. };
  369. let is_edid = false;
  370. let is_current = false;
  371. let edid_raw = '';
  372. let start = 0;
  373. for (let i = 1; i < lines.length; i++) { // start with second line
  374. if ('' !== lines[i].trim()) {
  375. if (' ' !== lines[i][0] && '\t' !== lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') !== -1) { // first line of new entry
  376. if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizex !== -1 || currentDisplay.pixeldepth !== -1 || currentDisplay.resolutionx !== -1) { // push last display to array
  377. displays.push(currentDisplay);
  378. currentDisplay = {
  379. vendor: '',
  380. model: '',
  381. main: false,
  382. builtin: false,
  383. connection: '',
  384. sizex: -1,
  385. sizey: -1,
  386. pixeldepth: -1,
  387. resolutionx: -1,
  388. resolutiony: -1,
  389. currentResX: -1,
  390. currentResY: -1,
  391. positionX: 0,
  392. positionY: 0,
  393. currentRefreshRate: -1
  394. };
  395. }
  396. let parts = lines[i].split(' ');
  397. currentDisplay.connection = parts[0];
  398. currentDisplay.main = lines[i].toLowerCase().indexOf(' primary ') >= 0;
  399. currentDisplay.builtin = (parts[0].toLowerCase().indexOf('edp') >= 0);
  400. }
  401. // try to read EDID information
  402. if (is_edid) {
  403. if (lines[i].search(/\S|$/) > start) {
  404. edid_raw += lines[i].toLowerCase().trim();
  405. } else {
  406. // parsen EDID
  407. let edid_decoded = parseLinesLinuxEdid(edid_raw);
  408. currentDisplay.vendor = edid_decoded.vendor;
  409. currentDisplay.model = edid_decoded.model;
  410. currentDisplay.resolutionx = edid_decoded.resolutionx;
  411. currentDisplay.resolutiony = edid_decoded.resolutiony;
  412. currentDisplay.sizex = edid_decoded.sizex;
  413. currentDisplay.sizey = edid_decoded.sizey;
  414. currentDisplay.pixeldepth = depth;
  415. is_edid = false;
  416. }
  417. }
  418. if (lines[i].toLowerCase().indexOf('edid:') >= 0) {
  419. is_edid = true;
  420. start = lines[i].search(/\S|$/);
  421. }
  422. if (lines[i].toLowerCase().indexOf('*current') >= 0) {
  423. const parts1 = lines[i].split('(');
  424. if (parts1 && parts1.length > 1 && parts1[0].indexOf('x') >= 0) {
  425. const resParts = parts1[0].trim().split('x');
  426. currentDisplay.currentResX = util.toInt(resParts[0]);
  427. currentDisplay.currentResY = util.toInt(resParts[1]);
  428. }
  429. is_current = true;
  430. }
  431. if (is_current && lines[i].toLowerCase().indexOf('clock') >= 0 && lines[i].toLowerCase().indexOf('hz') >= 0 && lines[i].toLowerCase().indexOf('v: height') >= 0) {
  432. const parts1 = lines[i].split('clock');
  433. if (parts1 && parts1.length > 1 && parts1[1].toLowerCase().indexOf('hz') >= 0) {
  434. currentDisplay.currentRefreshRate = util.toInt(parts1[1]);
  435. }
  436. is_current = false;
  437. }
  438. }
  439. }
  440. // pushen displays
  441. if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizex !== -1 || currentDisplay.pixeldepth !== -1 || currentDisplay.resolutionx !== -1) { // still information there
  442. displays.push(currentDisplay);
  443. }
  444. return displays;
  445. }
  446. // function starts here
  447. return new Promise((resolve) => {
  448. process.nextTick(() => {
  449. let result = {
  450. controllers: [],
  451. displays: []
  452. };
  453. if (_darwin) {
  454. let cmd = 'system_profiler SPDisplaysDataType';
  455. exec(cmd, function (error, stdout) {
  456. if (!error) {
  457. let lines = stdout.toString().split('\n');
  458. result = parseLinesDarwin(lines);
  459. }
  460. if (callback) {
  461. callback(result);
  462. }
  463. resolve(result);
  464. });
  465. }
  466. if (_linux) {
  467. // Raspberry: https://elinux.org/RPI_vcgencmd_usage
  468. if (util.isRaspberry() && util.isRaspbian()) {
  469. let cmd = 'fbset -s | grep \'mode "\'; vcgencmd get_mem gpu; tvservice -s; tvservice -n;';
  470. exec(cmd, function (error, stdout) {
  471. let lines = stdout.toString().split('\n');
  472. if (lines.length > 3 && lines[0].indexOf('mode "') >= -1 && lines[2].indexOf('0x12000a') > -1) {
  473. const parts = lines[0].replace('mode', '').replace(/"/g, '').trim().split('x');
  474. if (parts.length === 2) {
  475. result.displays.push({
  476. vendor: '',
  477. model: util.getValue(lines, 'device_name', '='),
  478. main: true,
  479. builtin: false,
  480. connection: 'HDMI',
  481. sizex: -1,
  482. sizey: -1,
  483. pixeldepth: -1,
  484. resolutionx: parseInt(parts[0], 10),
  485. resolutiony: parseInt(parts[1], 10),
  486. currentResX: -1,
  487. currentResY: -1,
  488. positionX: 0,
  489. positionY: 0,
  490. currentRefreshRate: -1
  491. });
  492. }
  493. }
  494. if (lines.length > 1 && lines[1].indexOf('gpu=') >= -1) {
  495. result.controllers.push({
  496. vendor: 'Broadcom',
  497. model: 'VideoCore IV',
  498. bus: '',
  499. vram: lines[1].replace('gpu=', ''),
  500. vramDynamic: true
  501. });
  502. }
  503. if (callback) {
  504. callback(result);
  505. }
  506. resolve(result);
  507. });
  508. } else {
  509. let cmd = 'lspci -vvv 2>/dev/null';
  510. exec(cmd, function (error, stdout) {
  511. if (!error) {
  512. let lines = stdout.toString().split('\n');
  513. result.controllers = parseLinesLinuxControllers(lines);
  514. }
  515. let cmd = 'xdpyinfo 2>/dev/null | grep \'depth of root window\' | awk \'{ print $5 }\'';
  516. exec(cmd, function (error, stdout) {
  517. let depth = 0;
  518. if (!error) {
  519. let lines = stdout.toString().split('\n');
  520. depth = parseInt(lines[0]) || 0;
  521. }
  522. let cmd = 'xrandr --verbose 2>/dev/null';
  523. exec(cmd, function (error, stdout) {
  524. if (!error) {
  525. let lines = stdout.toString().split('\n');
  526. result.displays = parseLinesLinuxDisplays(lines, depth);
  527. }
  528. if (callback) {
  529. callback(result);
  530. }
  531. resolve(result);
  532. });
  533. });
  534. });
  535. }
  536. }
  537. if (_freebsd || _openbsd || _netbsd) {
  538. if (callback) { callback(result); }
  539. resolve(result);
  540. }
  541. if (_sunos) {
  542. if (callback) { callback(result); }
  543. resolve(result);
  544. }
  545. if (_windows) {
  546. // https://blogs.technet.microsoft.com/heyscriptingguy/2013/10/03/use-powershell-to-discover-multi-monitor-information/
  547. // https://devblogs.microsoft.com/scripting/use-powershell-to-discover-multi-monitor-information/
  548. try {
  549. const workload = [];
  550. workload.push(util.wmic('path win32_VideoController get /value'));
  551. workload.push(util.wmic('path win32_desktopmonitor get /value'));
  552. workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorBasicDisplayParams | fl'));
  553. workload.push(util.powerShell('Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::AllScreens'));
  554. workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorConnectionParams | fl'));
  555. 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}'));
  556. Promise.all(
  557. workload
  558. ).then(data => {
  559. // controller
  560. let csections = data[0].split(/\n\s*\n/);
  561. result.controllers = parseLinesWindowsControllers(csections);
  562. // displays
  563. let dsections = data[1].split(/\n\s*\n/);
  564. // result.displays = parseLinesWindowsDisplays(dsections);
  565. dsections.shift();
  566. dsections.pop();
  567. // monitor (powershell)
  568. let msections = data[2].split('Active ');
  569. msections.shift();
  570. // forms.screens (powershell)
  571. let ssections = data[3].split('BitsPerPixel ');
  572. ssections.shift();
  573. // connection params (powershell) - video type
  574. let tsections = data[4].split(/\n\s*\n/);
  575. tsections.shift();
  576. // monitor ID (powershell) - model / vendor
  577. const res = data[5].split(/\r\n/);
  578. let isections = [];
  579. res.forEach(element => {
  580. const parts = element.split('|');
  581. if (parts.length === 5) {
  582. isections.push({
  583. vendor: parts[0],
  584. code: parts[1],
  585. model: parts[2],
  586. serial: parts[3],
  587. instanceId: parts[4]
  588. });
  589. }
  590. });
  591. result.displays = parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections);
  592. if (result.displays.length === 1) {
  593. if (_resolutionx) {
  594. result.displays[0].resolutionx = _resolutionx;
  595. if (!result.displays[0].currentResX) {
  596. result.displays[0].currentResX = _resolutionx;
  597. }
  598. }
  599. if (_resolutiony) {
  600. result.displays[0].resolutiony = _resolutiony;
  601. if (result.displays[0].currentResY === 0) {
  602. result.displays[0].currentResY = _resolutiony;
  603. }
  604. }
  605. if (_pixeldepth) {
  606. result.displays[0].pixeldepth = _pixeldepth;
  607. }
  608. if (_refreshrate && !result.displays[0].refreshrate) {
  609. result.displays[0].currentRefreshRate = _refreshrate;
  610. }
  611. }
  612. if (callback) {
  613. callback(result);
  614. }
  615. resolve(result);
  616. })
  617. .catch(() => {
  618. if (callback) {
  619. callback(result);
  620. }
  621. resolve(result);
  622. });
  623. } catch (e) {
  624. if (callback) { callback(result); }
  625. resolve(result);
  626. }
  627. }
  628. });
  629. });
  630. function parseLinesWindowsControllers(sections) {
  631. let controllers = [];
  632. for (let i in sections) {
  633. if ({}.hasOwnProperty.call(sections, i)) {
  634. if (sections[i].trim() !== '') {
  635. let lines = sections[i].trim().split('\r\n');
  636. controllers.push({
  637. vendor: util.getValue(lines, 'AdapterCompatibility', '='),
  638. model: util.getValue(lines, 'name', '='),
  639. bus: util.getValue(lines, 'PNPDeviceID', '=').startsWith('PCI') ? 'PCI' : '',
  640. vram: parseInt(util.getValue(lines, 'AdapterRAM', '='), 10) / 1024 / 1024,
  641. vramDynamic: (util.getValue(lines, 'VideoMemoryType', '=') === '2')
  642. });
  643. _resolutionx = util.toInt(util.getValue(lines, 'CurrentHorizontalResolution', '=')) || _resolutionx;
  644. _resolutiony = util.toInt(util.getValue(lines, 'CurrentVerticalResolution', '=')) || _resolutiony;
  645. _refreshrate = util.toInt(util.getValue(lines, 'CurrentRefreshRate', '=')) || _refreshrate;
  646. _pixeldepth = util.toInt(util.getValue(lines, 'CurrentBitsPerPixel', '=')) || _pixeldepth;
  647. }
  648. }
  649. }
  650. return controllers;
  651. }
  652. // function parseLinesWindowsDisplays(sections) {
  653. // let displays = [];
  654. // for (let i in sections) {
  655. // if (sections.hasOwnProperty(i)) {
  656. // if (sections[i].trim() !== '') {
  657. // let lines = sections[i].trim().split('\r\n');
  658. // displays.push({
  659. // vendor: util.getValue(lines, 'MonitorManufacturer', '='),
  660. // model: util.getValue(lines, 'Name', '='),
  661. // main: false,
  662. // builtin: false,
  663. // connection: '',
  664. // sizex: -1,
  665. // sizey: -1,
  666. // pixeldepth: -1,
  667. // resolutionx: util.toInt(util.getValue(lines, 'ScreenWidth', '=')),
  668. // resolutiony: util.toInt(util.getValue(lines, 'ScreenHeight', '=')),
  669. // });
  670. // }
  671. // }
  672. // }
  673. // return displays;
  674. // }
  675. function parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections) {
  676. let displays = [];
  677. let vendor = '';
  678. let model = '';
  679. let deviceID = '';
  680. let resolutionx = 0;
  681. let resolutiony = 0;
  682. if (dsections && dsections.length) {
  683. let linesDisplay = dsections[0].split(os.EOL);
  684. vendor = util.getValue(linesDisplay, 'MonitorManufacturer', '=');
  685. model = util.getValue(linesDisplay, 'Name', '=');
  686. deviceID = util.getValue(linesDisplay, 'PNPDeviceID', '=').replace(/&amp;/g, '&').toLowerCase();
  687. resolutionx = util.toInt(util.getValue(linesDisplay, 'ScreenWidth', '='));
  688. resolutiony = util.toInt(util.getValue(linesDisplay, 'ScreenHeight', '='));
  689. }
  690. for (let i = 0; i < ssections.length; i++) {
  691. if (ssections[i].trim() !== '') {
  692. ssections[i] = 'BitsPerPixel ' + ssections[i];
  693. msections[i] = 'Active ' + msections[i];
  694. let linesScreen = ssections[i].split(os.EOL);
  695. let linesMonitor = msections[i].split(os.EOL);
  696. let linesConnection = tsections[i].split(os.EOL);
  697. const bitsPerPixel = util.getValue(linesScreen, 'BitsPerPixel');
  698. const bounds = util.getValue(linesScreen, 'Bounds').replace('{', '').replace('}', '').split(',');
  699. const primary = util.getValue(linesScreen, 'Primary');
  700. const sizex = util.getValue(linesMonitor, 'MaxHorizontalImageSize');
  701. const sizey = util.getValue(linesMonitor, 'MaxVerticalImageSize');
  702. const instanceName = util.getValue(linesMonitor, 'InstanceName').toLowerCase();
  703. const videoOutputTechnology = util.getValue(linesConnection, 'VideoOutputTechnology');
  704. let displayVendor = '';
  705. let displayModel = '';
  706. isections.forEach(element => {
  707. if (element.instanceId.toLowerCase().startsWith(instanceName) && vendor.startsWith('(') && model.startsWith('PnP')) {
  708. displayVendor = element.vendor;
  709. displayModel = element.model;
  710. }
  711. });
  712. displays.push({
  713. vendor: instanceName.startsWith(deviceID) && displayVendor === '' ? vendor : displayVendor,
  714. model: instanceName.startsWith(deviceID) && displayModel === '' ? model : displayModel,
  715. main: primary.toLowerCase() === 'true',
  716. builtin: videoOutputTechnology === '2147483648',
  717. connection: videoOutputTechnology && videoTypes[videoOutputTechnology] ? videoTypes[videoOutputTechnology] : '',
  718. resolutionx: util.toInt(util.getValue(bounds, 'Width', '=')),
  719. resolutiony: util.toInt(util.getValue(bounds, 'Height', '=')),
  720. sizex: sizex ? parseInt(sizex, 10) : -1,
  721. sizey: sizey ? parseInt(sizey, 10) : -1,
  722. pixeldepth: bitsPerPixel,
  723. currentResX: util.toInt(util.getValue(bounds, 'Width', '=')),
  724. currentResY: util.toInt(util.getValue(bounds, 'Height', '=')),
  725. positionX: util.toInt(util.getValue(bounds, 'X', '=')),
  726. positionY: util.toInt(util.getValue(bounds, 'Y', '=')),
  727. });
  728. }
  729. }
  730. if (ssections.length === 0) {
  731. displays.push({
  732. vendor,
  733. model,
  734. main: true,
  735. resolutionx,
  736. resolutiony,
  737. sizex: -1,
  738. sizey: -1,
  739. pixeldepth: -1,
  740. currentResX: resolutionx,
  741. currentResY: resolutiony,
  742. positionX: 0,
  743. positionY: 0
  744. });
  745. }
  746. return displays;
  747. }
  748. }
  749. exports.graphics = graphics;