From 0612e3aa20cebcc3a3800b35d249ad4b73e71f7e Mon Sep 17 00:00:00 2001 From: Sebastian Hildebrandt Date: Thu, 21 Oct 2021 13:41:34 +0200 Subject: [PATCH] graphics() fix after wmic refactor (win) --- docs/styles.scss | 960 +++++++++---------- docs/v4/styles.scss | 825 ++++++++--------- lib/graphics.js | 2125 +++++++++++++++++++++---------------------- 3 files changed, 1964 insertions(+), 1946 deletions(-) diff --git a/docs/styles.scss b/docs/styles.scss index 33fb4df..5ae324d 100644 --- a/docs/styles.scss +++ b/docs/styles.scss @@ -1,230 +1,233 @@ body { - font-family: 'Roboto', 'HelveticaNeue-Light','Helvetica Neue Light','Helvetica Neue',Helvetica,Arial,sans-serif; - background-color: rgb(14, 24, 46); - font-weight: 300; - min-height: 100%; + font-family: "Roboto", "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, sans-serif; + background-color: rgb(14, 24, 46); + font-weight: 300; + min-height: 100%; } -h1, h2, h3, h4 { - font-family: 'Roboto', 'HelveticaNeue-Light','Helvetica Neue Light','Helvetica Neue',Helvetica,Arial,sans-serif; - font-weight: 500; - color: rgb(61, 92, 179); +h1, +h2, +h3, +h4 { + font-family: "Roboto", "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 500; + color: rgb(61, 92, 179); } .center { - text-align: center; + text-align: center; } .bold { - font-weight: 500; + font-weight: 500; } .warning { - font-weight: 500; - color: rgb(156, 1, 1); + font-weight: 500; + color: rgb(156, 1, 1); } .larger { margin-top: 10px; font-size: 1.5rem !important; } .medium { - font-weight: 500; + font-weight: 500; } .blue { - color: rgb(61, 92, 179); + color: rgb(61, 92, 179); } .container-fluid { - background-color: rgb(255,255,255); + background-color: rgb(255, 255, 255); padding-bottom: 30px; } .bg-image-full { - // background: no-repeat center center scroll; - background-color: #1A1F30; /* For browsers that do not support gradients */ - background-image: linear-gradient(210deg, #2A2D66, #060520); /* Standard syntax (must be last) */ + // background: no-repeat center center scroll; + background-color: #1a1f30; /* For browsers that do not support gradients */ + background-image: linear-gradient(210deg, #2a2d66, #060520); /* Standard syntax (must be last) */ - background-position: center; - background-repeat: no-repeat; - background-size: cover; - background-image: url("assets/cpu.jpg"); + background-position: center; + background-repeat: no-repeat; + background-size: cover; + background-image: url("assets/cpu.jpg"); - -webkit-background-size: cover; - -moz-background-size: cover; - background-size: cover; - -o-background-size: cover; - height: 100vh; //calc(100vh); - height: calc(var(--vh, 1vh) * 100); - opacity: 0.95; + -webkit-background-size: cover; + -moz-background-size: cover; + background-size: cover; + -o-background-size: cover; + height: 100vh; //calc(100vh); + height: calc(var(--vh, 1vh) * 100); + opacity: 0.95; + display: flex; + justify-content: center; + text-align: center; + width: 100% !important; + // align-items: center; + flex-direction: column; + .top-container { + text-align: center; + align-self: center; display: flex; justify-content: center; - text-align: center; - width: 100% !important; - // align-items: center; flex-direction: column; - .top-container { - text-align: center; - align-self: center; - display: flex; - justify-content: center; - flex-direction: column; - align-items: center; - flex-grow: 1; - overflow: hidden; - position: relative; - width: 100% !important; + align-items: center; + flex-grow: 1; + overflow: hidden; + position: relative; + width: 100% !important; + } + .recommendation { + position: absolute; + top: 30px; + right: 0px; + margin-right: -85px; + z-index: 10; + display: inline-block; + width: 280px; + text-align: center; + padding: 0.25em 0; + color: white; + transform: rotate(45deg); + background: rgb(151, 29, 29); + font-size: 0.9rem; + @media only screen and (min-width: 420px) { + font-size: 1rem; } - .recommendation { - position: absolute; - top: 30px; + @media only screen and (min-width: 767px) { + font-size: 1.1rem; + top: 40px; + margin-right: -95px; + width: 330px; + } + @media only screen and (min-width: 1024px) { + width: 330px; + top: 60px; right: 0px; - margin-right:-85px; - z-index: 10; - display: inline-block; - width: 280px; - text-align: center; - padding: .25em 0; - color: white; - transform: rotate(45deg); - background: rgb(151, 29, 29); + margin-right: -71px; + font-size: 1.2rem; + } + &:hover { + text-decoration: none; + } + } + .title { + color: #fff; + padding-top: 10px; + font-size: 1.8rem; + margin-bottom: 10px; + font-weight: 500; + @media only screen and (min-width: 374px) { + font-size: 2.1rem; + } + @media only screen and (min-width: 420px) { + font-size: 2.4rem; + } + @media only screen and (min-width: 767px) { + font-size: 3.5rem; + } + @media only screen and (min-width: 1024px) { + font-size: 4rem; + } + @media only screen and (min-width: 1200px) { + font-size: 5rem; + } + @media only screen and (min-width: 1900px) { + font-size: 6rem; + } + } + .logo { + width: 100px; + @media only screen and (min-width: 420px) { + width: 120px; + } + @media only screen and (min-width: 767px) { + width: 160px; + } + @media only screen and (min-width: 1024px) { + width: 200px; + } + @media only screen and (min-width: 1900px) { + width: 250px; + } + } + .subtitle { + text-align: center; + color: #bbb; + font-size: 0.78rem; + @media only screen and (min-width: 375px) { font-size: 0.9rem; - @media only screen and (min-width: 420px) { - font-size: 1.0rem; - } - @media only screen and (min-width: 767px) { - font-size: 1.1rem; - top: 40px; - margin-right:-95px; - width: 330px; - } - @media only screen and (min-width: 1024px) { - width: 330px; - top: 60px; - right: 0px; - margin-right:-71px; - font-size: 1.2rem; - } - &:hover { - text-decoration: none;; - } } - .title { - color: #fff; - padding-top: 10px; - font-size: 1.8rem; - margin-bottom: 10px; - font-weight: 500; - @media only screen and (min-width: 374px) { - font-size: 2.1rem; - } - @media only screen and (min-width: 420px) { - font-size: 2.4rem; - } - @media only screen and (min-width: 767px) { - font-size: 3.5rem; - } - @media only screen and (min-width: 1024px) { - font-size: 4rem; - } - @media only screen and (min-width: 1200px) { - font-size: 5rem; - } - @media only screen and (min-width: 1900px) { - font-size: 6rem; - } + @media only screen and (min-width: 420px) { + font-size: 1rem; } - .logo { - width: 100px; - @media only screen and (min-width: 420px) { - width: 120px; - } - @media only screen and (min-width: 767px) { - width: 160px; - } - @media only screen and (min-width: 1024px) { - width: 200px; - } - @media only screen and (min-width: 1900px) { - width: 250px; - } + @media only screen and (min-width: 767px) { + font-size: 1.1rem; } - .subtitle { - text-align: center; - color: #bbb; - font-size: 0.78rem; - @media only screen and (min-width: 375px) { - font-size: 0.9rem; - } - @media only screen and (min-width: 420px) { - font-size: 1rem; - } - @media only screen and (min-width: 767px) { - font-size: 1.1rem; - } - @media only screen and (min-width: 1024px) { - font-size: 1.3rem; - } - @media only screen and (min-width: 1200px) { - font-size: 1.4rem; - } - @media only screen and (min-width: 1900px) { - font-size: 1.6rem; - } - } - .text > a { - text-align: center; - color: #fff; - font-size: 0.9rem; - &:hover { - color: #aaa; - text-decoration: none; - } - @media only screen and (min-width: 375px) { - font-size: 0.9rem; - } - @media only screen and (min-width: 420px) { - font-size: 1rem; - } - @media only screen and (min-width: 767px) { - font-size: 1rem; - } - @media only screen and (min-width: 1024px) { - font-size: 1.1rem; - } - @media only screen and (min-width: 1200px) { - font-size: 1.2rem; - } - @media only screen and (min-width: 1900px) { - font-size: 1.5rem; - } - } - .version { - text-align: center; - margin-bottom: 10px; - color: #fff; - font-size: .9rem; - @media only screen and (min-width: 420px) { - font-size: .9rem; - } - @media only screen and (min-width: 768px) { - font-size: 1rem; - } - @media only screen and (min-width: 1024px) { - font-size: 1.1rem; - } - @media only screen and (min-width: 1200px) { - font-size: 1.2rem; - } - @media only screen and (min-width: 1900px) { - font-size: 1.5rem; - } - } - .down { - text-align: center; - color: #fff; + @media only screen and (min-width: 1024px) { font-size: 1.3rem; - line-height: 1; - margin-bottom: 15px; - cursor: pointer; - .caret { - font-size: 3rem; - } } + @media only screen and (min-width: 1200px) { + font-size: 1.4rem; + } + @media only screen and (min-width: 1900px) { + font-size: 1.6rem; + } + } + .text > a { + text-align: center; + color: #fff; + font-size: 0.9rem; + &:hover { + color: #aaa; + text-decoration: none; + } + @media only screen and (min-width: 375px) { + font-size: 0.9rem; + } + @media only screen and (min-width: 420px) { + font-size: 1rem; + } + @media only screen and (min-width: 767px) { + font-size: 1rem; + } + @media only screen and (min-width: 1024px) { + font-size: 1.1rem; + } + @media only screen and (min-width: 1200px) { + font-size: 1.2rem; + } + @media only screen and (min-width: 1900px) { + font-size: 1.5rem; + } + } + .version { + text-align: center; + margin-bottom: 10px; + color: #fff; + font-size: 0.9rem; + @media only screen and (min-width: 420px) { + font-size: 0.9rem; + } + @media only screen and (min-width: 768px) { + font-size: 1rem; + } + @media only screen and (min-width: 1024px) { + font-size: 1.1rem; + } + @media only screen and (min-width: 1200px) { + font-size: 1.2rem; + } + @media only screen and (min-width: 1900px) { + font-size: 1.5rem; + } + } + .down { + text-align: center; + color: #fff; + font-size: 1.3rem; + line-height: 1; + margin-bottom: 15px; + cursor: pointer; + .caret { + font-size: 3rem; + } + } } // a[ id="quickstart" ]:target ~ #main div.row { @@ -235,330 +238,335 @@ h1, h2, h3, h4 { // background-color: #ff3333; // } .nav { - background: no-repeat center center scroll; - background-color: #1A1F30; /* For browsers that do not support gradients */ - background-image: linear-gradient(210deg, #2A2D66, #060520); /* Standard syntax (must be last) */ - -webkit-background-size: cover; - -moz-background-size: cover; - background-size: cover; - -o-background-size: cover; - height: 65px; - position: relative; - .logo { - width: 60px; - margin-top: 12px; - } - .title { - display: none; - color: #fff; - font-size: 1.5rem; - margin-left: 15px; - margin-top: 15px; - font-weight: 500; - @media only screen and (min-width: 767px) { - display: inline-block; - } - @media only screen and (min-width: 1024px) { - font-size: 1.5rem; - } - - } - .text { - margin-top: 18px; - - a { - color: #fff; - margin-top: 12px; - font-size: 1.0rem; - @media only screen and (min-width: 1023px) { - margin-top: 10px; - font-size: 1.2rem; - } - &:hover { - color: #aaa; - text-decoration: none; - } - } - } - .github { - display: none; - @media only screen and (min-width: 768px) { - display: inline-block; - } - } - .todocs { + background: no-repeat center center scroll; + background-color: #1a1f30; /* For browsers that do not support gradients */ + background-image: linear-gradient(210deg, #2a2d66, #060520); /* Standard syntax (must be last) */ + -webkit-background-size: cover; + -moz-background-size: cover; + background-size: cover; + -o-background-size: cover; + height: 65px; + position: relative; + .logo { + width: 60px; + margin-top: 12px; + } + .title { + display: none; + color: #fff; + font-size: 1.5rem; + margin-left: 15px; + margin-top: 15px; + font-weight: 500; + @media only screen and (min-width: 767px) { display: inline-block; - @media only screen and (min-width: 768px) { - display: none; + } + @media only screen and (min-width: 1024px) { + font-size: 1.5rem; + } + } + .text { + margin-top: 18px; + + a { + color: #fff; + margin-top: 12px; + font-size: 1rem; + @media only screen and (min-width: 1023px) { + margin-top: 10px; + font-size: 1.2rem; + } + &:hover { + color: #aaa; + text-decoration: none; } } + } + .github { + display: none; + @media only screen and (min-width: 768px) { + display: inline-block; + } + } + .todocs { + display: inline-block; + @media only screen and (min-width: 768px) { + display: none; + } + } } .features { - text-align: center; - color: #333; - .inner { - padding: 10px 0px; + text-align: center; + color: #333; + .inner { + padding: 10px 0px; + @media only screen and (min-width: 768px) { + padding: 15px 20px; + } + .icons { + display: block; + font-size: 3rem; + line-height: 1.3; + color: #bbb; + @media only screen and (min-width: 420px) { + font-size: 3.5rem; + } @media only screen and (min-width: 768px) { - padding: 15px 20px; + font-size: 4rem; } - .icons { - display: block; - font-size: 3rem; - line-height: 1.3; - color: #bbb; - @media only screen and (min-width: 420px) { - font-size: 3.5rem; - } - @media only screen and (min-width: 768px) { - font-size: 4rem; - } - @media only screen and (min-width: 1024px) { - font-size: 5rem; - } - } - .icontitle { - font-size: 1.2rem; - } - - &:hover { - background: #f4f4f4; - color: rgb(86, 122, 223); - .icons { - color: rgb(86, 122, 223); - } + @media only screen and (min-width: 1024px) { + font-size: 5rem; } } + .icontitle { + font-size: 1.2rem; + } + + &:hover { + background: #f4f4f4; + color: rgb(86, 122, 223); + .icons { + color: rgb(86, 122, 223); + } + } + } } .sectionheader { - .title { - text-align: center; - padding-top: 20px; - font-size: 2rem; - font-weight: 500; - color: rgb(61, 92, 179); - @media only screen and (min-width: 420px) { - font-size: 2.3rem; - } - @media only screen and (min-width: 768px) { - text-align: left; - padding-top: 25px; - font-size: 2.6rem; - } - @media only screen and (min-width: 1024px) { - padding-top: 40px; - font-size: 3rem; - } + .title { + text-align: center; + padding-top: 20px; + font-size: 2rem; + font-weight: 500; + color: rgb(61, 92, 179); + @media only screen and (min-width: 420px) { + font-size: 2.3rem; } - .title-small { - text-align: center; - font-size: 1.4rem; - font-weight: 500; - color: rgb(61, 92, 179); - @media only screen and (min-width: 420px) { - font-size: 1.6rem; - } - @media only screen and (min-width: 768px) { - text-align: left; - padding-top: 25px; - font-size: 1.8rem; - } - @media only screen and (min-width: 1024px) { - padding-top: 30px; - font-size: 2rem; - } + @media only screen and (min-width: 768px) { + text-align: left; + padding-top: 25px; + font-size: 2.6rem; } - .subtitle { - text-align: center; - font-size: .9rem; - padding-top: 3px; - padding-bottom: 5px; - @media only screen and (min-width: 420px) { - font-size: 1rem; - } - @media only screen and (min-width: 768px) { - text-align: left; - font-size: 1.1rem; - } - @media only screen and (min-width: 1024px) { - font-size: 1.3rem; - } + @media only screen and (min-width: 1024px) { + padding-top: 40px; + font-size: 3rem; } - .npmicons { - text-align: center; + } + .title-small { + text-align: center; + font-size: 1.4rem; + font-weight: 500; + color: rgb(61, 92, 179); + @media only screen and (min-width: 420px) { + font-size: 1.6rem; } - .text { - text-align: center; - padding-top: 10px; - @media only screen and (min-width: 768px) { - text-align: left; - } + @media only screen and (min-width: 768px) { + text-align: left; + padding-top: 25px; + font-size: 1.8rem; } - hr { - margin-top: 40px; - margin-bottom: 0px; - border: 1px solid #ddd; + @media only screen and (min-width: 1024px) { + padding-top: 30px; + font-size: 2rem; } + } + .subtitle { + text-align: center; + font-size: 0.9rem; + padding-top: 3px; + padding-bottom: 5px; + @media only screen and (min-width: 420px) { + font-size: 1rem; + } + @media only screen and (min-width: 768px) { + text-align: left; + font-size: 1.1rem; + } + @media only screen and (min-width: 1024px) { + font-size: 1.3rem; + } + } + .npmicons { + text-align: center; + } + .text { + text-align: center; + padding-top: 10px; + @media only screen and (min-width: 768px) { + text-align: left; + } + } + hr { + margin-top: 40px; + margin-bottom: 0px; + border: 1px solid #ddd; + } } .index { margin: 0 0 10px 0; - .text, .subtitle, .title, .title-small, .npmicons { - text-align: center; + .text, + .subtitle, + .title, + .title-small, + .npmicons { + text-align: center; } .larger { font-size: 1.2rem; } a { + text-decoration: none; + &:hover { text-decoration: none; - &:hover { - text-decoration: none; - color: rgb(86, 122, 223); - } + color: rgb(86, 122, 223); + } } } .left { - .text, .subtitle, .title, .title-small .npmicons { - text-align: left; - } + .text, + .subtitle, + .title, + .title-small .npmicons { + text-align: left; + } } .table { - text-align: left; + text-align: left; } ul { - text-align: left; + text-align: left; } .number-section { - margin-top: 40px; - text-align: center; - .numbers { - font-size: 2rem; - @media only screen and (min-width: 420px) { - font-size: 2.4rem; - } - @media only screen and (min-width: 768px) { - font-size: 2.8rem; - } + margin-top: 40px; + text-align: center; + .numbers { + font-size: 2rem; + @media only screen and (min-width: 420px) { + font-size: 2.4rem; } - .title { - color: #999; - font-size: 1rem; - @media only screen and (min-width: 420px) { - font-size: 1.1rem; - } - @media only screen and (min-width: 768px) { - font-size: 1.2rem; - } - } -} -.menu { - display: none; @media only screen and (min-width: 768px) { - margin-top: 30px; - display: inline-block; + font-size: 2.8rem; } - .title { - margin: 25px 0 5px 0; - color: #000; - a { - width: 100%; - color: #000; - text-decoration: none; - &:hover { - color: rgb(86, 122, 223); - } - } - .navtitle { - font-size: 1.3rem; - margin-bottom: 5px; - display: inline-block; - color: rgb(61, 92, 179); - } - } - hr { - margin: 5px 0; - } - ul { - list-style-type: none; - margin: 0; - padding: 0; - li { - padding: 2px 0 1px 0; - a { - width: 100%; - color: #666; - text-decoration: none; - &:hover { - color: rgb(86, 122, 223); - } - } - &.active > a { - color: rgb(86, 122, 223); - font-weight: 500; - } - } - } -} -.code { - font-family: Menlo, "dejavu sans mono", "lucida console", monospace; - -webkit-font-smoothing: antialiased; - font-size: 14px; - font-style: normal; - font-variant: normal; - font-weight: 400; - display: inline-block; - background-color: #eee; - margin: 0; - padding: 0 5px; -} -pre { - padding: 4px 10px 4px 10px; - font-family: Menlo, "dejavu sans mono", "lucida console", monospace; - -webkit-font-smoothing: antialiased; - font-size: 13px; - font-style: normal; - font-variant: normal; - font-weight: 400; - line-height: 18px; - background-color: #eee; - width: 100%; - text-align: left; -} -.example { - td:first-child { - background-color: #fff !important; - } - td:last-child { - background-color: #f8faff !important; - } - h5 { - padding-top: 5px; - padding-left: 10px; - } - pre { - background-color: unset; - .js { - background-color: #f0f0f4; - - } - } -} -.footer { - background-color: #eee; - padding-top: 30px; - padding-bottom: 30px; - position: relative; - .home { - color: rgb(61, 92, 179); + } + .title { + color: #999; + font-size: 1rem; + @media only screen and (min-width: 420px) { font-size: 1.1rem; } - a { - color: #000; - &:hover { - text-decoration: none - } - } - .badge { - color: #fff; - font-size: 0.9rem; + @media only screen and (min-width: 768px) { + font-size: 1.2rem; } + } +} +.menu { + display: none; + @media only screen and (min-width: 768px) { + margin-top: 30px; + display: inline-block; + } + .title { + margin: 25px 0 5px 0; + color: #000; + a { + width: 100%; + color: #000; + text-decoration: none; + &:hover { + color: rgb(86, 122, 223); + } + } + .navtitle { + font-size: 1.3rem; + margin-bottom: 5px; + display: inline-block; + color: rgb(61, 92, 179); + } + } + hr { + margin: 5px 0; + } + ul { + list-style-type: none; + margin: 0; + padding: 0; + li { + padding: 2px 0 1px 0; + a { + width: 100%; + color: #666; + text-decoration: none; + &:hover { + color: rgb(86, 122, 223); + } + } + &.active > a { + color: rgb(86, 122, 223); + font-weight: 500; + } + } + } +} +.code { + font-family: Menlo, "dejavu sans mono", "lucida console", monospace; + -webkit-font-smoothing: antialiased; + font-size: 14px; + font-style: normal; + font-variant: normal; + font-weight: 400; + display: inline-block; + background-color: #eee; + margin: 0; + padding: 0 5px; +} +pre { + padding: 4px 10px 4px 10px; + font-family: Menlo, "dejavu sans mono", "lucida console", monospace; + -webkit-font-smoothing: antialiased; + font-size: 13px; + font-style: normal; + font-variant: normal; + font-weight: 400; + line-height: 18px; + background-color: #eee; + width: 100%; + text-align: left; +} +.example { + td:first-child { + background-color: #fff !important; + } + td:last-child { + background-color: #f8faff !important; + } + h5 { + padding-top: 5px; + padding-left: 10px; + } + pre { + background-color: unset; + .js { + background-color: #f0f0f4; + } + } +} +.footer { + background-color: #eee; + padding-top: 30px; + padding-bottom: 30px; + position: relative; + .home { + color: rgb(61, 92, 179); + font-size: 1.1rem; + } + a { + color: #000; + &:hover { + text-decoration: none; + } + } + .badge { + color: #fff; + font-size: 0.9rem; + } } diff --git a/docs/v4/styles.scss b/docs/v4/styles.scss index cdb4357..ece9bc0 100644 --- a/docs/v4/styles.scss +++ b/docs/v4/styles.scss @@ -1,173 +1,178 @@ body { - font-family: -apple-system, BlinkMacSystemFont, "segoe ui", Roboto, Helvetica, Arial, sans-serif, "apple color emoji", "segoe ui emoji", "segoe ui symbol"; - font-weight: 300; - min-height: 100%; - background-color: rgb(89, 53, 30); + font-family: -apple-system, BlinkMacSystemFont, "segoe ui", Roboto, Helvetica, Arial, sans-serif, "apple color emoji", + "segoe ui emoji", "segoe ui symbol"; + font-weight: 300; + min-height: 100%; + background-color: rgb(89, 53, 30); } -h1, h2, h3, h4 { - font-family: -apple-system, BlinkMacSystemFont, "segoe ui", Roboto, Helvetica, Arial, sans-serif, "apple color emoji", "segoe ui emoji", "segoe ui symbol"; - font-weight: 300; +h1, +h2, +h3, +h4 { + font-family: -apple-system, BlinkMacSystemFont, "segoe ui", Roboto, Helvetica, Arial, sans-serif, "apple color emoji", + "segoe ui emoji", "segoe ui symbol"; + font-weight: 300; } .center { - text-align: center; + text-align: center; } .bold { - font-weight: 700; + font-weight: 700; } .warning { - font-weight: 500; - color: rgb(156, 1, 1); + font-weight: 500; + color: rgb(156, 1, 1); } .container-fluid { - background-color: rgb(255,255,255); + background-color: rgb(255, 255, 255); padding-bottom: 30px; } .bg-image-full { - background: no-repeat center center scroll; - background-color: #4b2313; /* For browsers that do not support gradients */ - background-image: linear-gradient(210deg, #4b2313, #2c0c0a ); /* Standard syntax (must be last) */ + background: no-repeat center center scroll; + background-color: #4b2313; /* For browsers that do not support gradients */ + background-image: linear-gradient(210deg, #4b2313, #2c0c0a); /* Standard syntax (must be last) */ - -webkit-background-size: cover; - -moz-background-size: cover; - background-size: cover; - -o-background-size: cover; - height: 100vh; //calc(100vh); - height: calc(var(--vh, 1vh) * 100); - opacity: 0.95; + -webkit-background-size: cover; + -moz-background-size: cover; + background-size: cover; + -o-background-size: cover; + height: 100vh; //calc(100vh); + height: calc(var(--vh, 1vh) * 100); + opacity: 0.95; + display: flex; + justify-content: center; + text-align: center; + + // align-items: center; + flex-direction: column; + .container { + text-align: center; + align-self: center; display: flex; justify-content: center; - text-align: center; - - // align-items: center; flex-direction: column; - .container { - text-align: center; - align-self: center; - display: flex; - justify-content: center; - flex-direction: column; - align-items: center; - flex-grow: 1; + align-items: center; + flex-grow: 1; + } + .recommendation { + position: absolute; + top: 40px; + right: -95px; + z-index: 10; + display: inline-block; + width: 330px; + text-align: center; + padding: 0.25em 0; + color: white; + transform: rotate(45deg); + background: rgb(151, 29, 29); + font-size: 0.9rem; + @media only screen and (min-width: 420px) { + font-size: 1rem; } - .recommendation { - position: absolute; - top: 40px; - right: -95px; - z-index: 10; - display: inline-block; - width: 330px; - text-align: center; - padding: .25em 0; - color: white; - transform: rotate(45deg); - background: rgb(151, 29, 29); + @media only screen and (min-width: 768px) { + font-size: 1.1rem; + } + @media only screen and (min-width: 1024px) { + top: 50px; + right: -75px; + font-size: 1.2rem; + } + &:hover { + text-decoration: none; + } + } + .title { + color: #fff; + padding-top: 10px; + font-size: 1.8rem; + margin-bottom: 10px; + @media only screen and (min-width: 420px) { + font-size: 2.4rem; + } + @media only screen and (min-width: 768px) { + font-size: 3.2rem; + } + @media only screen and (min-width: 1024px) { + font-size: 4rem; + } + } + .logo { + width: 100px; + @media only screen and (min-width: 420px) { + width: 120px; + } + @media only screen and (min-width: 768px) { + width: 160px; + } + @media only screen and (min-width: 1024px) { + width: 200px; + } + } + .subtitle { + text-align: center; + color: #bbb; + font-size: 0.78rem; + @media only screen and (min-width: 375px) { font-size: 0.9rem; - @media only screen and (min-width: 420px) { - font-size: 1.0rem; - } - @media only screen and (min-width: 768px) { - font-size: 1.1rem; - } - @media only screen and (min-width: 1024px) { - top: 50px; - right: -75px; - font-size: 1.2rem; - } - &:hover { - text-decoration: none;; - } } - .title { - color: #fff; - padding-top: 10px; - font-size: 1.8rem; - margin-bottom: 10px; - @media only screen and (min-width: 420px) { - font-size: 2.4rem; - } - @media only screen and (min-width: 768px) { - font-size: 3.2rem; - } - @media only screen and (min-width: 1024px) { - font-size: 4rem; - } + @media only screen and (min-width: 420px) { + font-size: 1rem; } - .logo { - width: 100px; - @media only screen and (min-width: 420px) { - width: 120px; - } - @media only screen and (min-width: 768px) { - width: 160px; - } - @media only screen and (min-width: 1024px) { - width: 200px; - } + @media only screen and (min-width: 768px) { + font-size: 1.1rem; } - .subtitle { - text-align: center; - color: #bbb; - font-size: 0.78rem; - @media only screen and (min-width: 375px) { - font-size: 0.9rem; - } - @media only screen and (min-width: 420px) { - font-size: 1rem; - } - @media only screen and (min-width: 768px) { - font-size: 1.1rem; - } - @media only screen and (min-width: 1024px) { - font-size: 1.3rem; - } - } - .text > a { - text-align: center; - color: #fff; - font-size: 0.9rem; - &:hover { - color: #aaa; - text-decoration: none; - } - @media only screen and (min-width: 375px) { - font-size: 0.9rem; - } - @media only screen and (min-width: 420px) { - font-size: 1rem; - } - @media only screen and (min-width: 768px) { - font-size: 1rem; - } - @media only screen and (min-width: 1024px) { - font-size: 1.1rem; - } - } - .version { - text-align: center; - margin-bottom: 10px; - color: #fff; - font-size: .9rem; - @media only screen and (min-width: 420px) { - font-size: .9rem; - } - @media only screen and (min-width: 768px) { - font-size: 1rem; - } - @media only screen and (min-width: 1024px) { - font-size: 1.1rem; - } - } - .down { - text-align: center; - color: #fff; + @media only screen and (min-width: 1024px) { font-size: 1.3rem; - line-height: 1; - margin-bottom: 15px; - cursor: pointer; - .caret { - font-size: 3rem; - } } + } + .text > a { + text-align: center; + color: #fff; + font-size: 0.9rem; + &:hover { + color: #aaa; + text-decoration: none; + } + @media only screen and (min-width: 375px) { + font-size: 0.9rem; + } + @media only screen and (min-width: 420px) { + font-size: 1rem; + } + @media only screen and (min-width: 768px) { + font-size: 1rem; + } + @media only screen and (min-width: 1024px) { + font-size: 1.1rem; + } + } + .version { + text-align: center; + margin-bottom: 10px; + color: #fff; + font-size: 0.9rem; + @media only screen and (min-width: 420px) { + font-size: 0.9rem; + } + @media only screen and (min-width: 768px) { + font-size: 1rem; + } + @media only screen and (min-width: 1024px) { + font-size: 1.1rem; + } + } + .down { + text-align: center; + color: #fff; + font-size: 1.3rem; + line-height: 1; + margin-bottom: 15px; + cursor: pointer; + .caret { + font-size: 3rem; + } + } } // a[ id="quickstart" ]:target ~ #main div.row { @@ -178,312 +183,318 @@ h1, h2, h3, h4 { // background-color: #ff3333; // } .nav { - background: no-repeat center center scroll; - // background-color: #1A1F30; /* For browsers that do not support gradients */ - // background-image: linear-gradient(210deg, #2A2D66, #060520); /* Standard syntax (must be last) */ - background-color: #4b2313; /* For browsers that do not support gradients */ - background-image: linear-gradient(210deg, #4b2313, #2c0c0a ); /* Standard syntax (must be last) */ - -webkit-background-size: cover; - -moz-background-size: cover; - background-size: cover; - -o-background-size: cover; - height: 65px; - position: relative; - .logo { - width: 60px; - margin-top: 12px; - } - .title { - display: none; - color: #fff; - font-size: 1.2rem; - margin-left: 15px; - margin-top: 18px; - @media only screen and (min-width: 768px) { - display: inline-block; - } - @media only screen and (min-width: 1024px) { - font-size: 1.2rem; - } - } - .text { - margin-top: 18px; - - a { - color: #fff; - margin-top: 12px; - font-size: 1.0rem; - @media only screen and (min-width: 1023px) { - margin-top: 10px; - font-size: 1.2rem; - } - &:hover { - color: #aaa; - text-decoration: none; - } - } - } - .github { - display: none; - @media only screen and (min-width: 768px) { - display: inline-block; - } - } - .todocs { + background: no-repeat center center scroll; + // background-color: #1A1F30; /* For browsers that do not support gradients */ + // background-image: linear-gradient(210deg, #2A2D66, #060520); /* Standard syntax (must be last) */ + background-color: #4b2313; /* For browsers that do not support gradients */ + background-image: linear-gradient(210deg, #4b2313, #2c0c0a); /* Standard syntax (must be last) */ + -webkit-background-size: cover; + -moz-background-size: cover; + background-size: cover; + -o-background-size: cover; + height: 65px; + position: relative; + .logo { + width: 60px; + margin-top: 12px; + } + .title { + display: none; + color: #fff; + font-size: 1.2rem; + margin-left: 15px; + margin-top: 18px; + @media only screen and (min-width: 768px) { display: inline-block; - @media only screen and (min-width: 768px) { - display: none; + } + @media only screen and (min-width: 1024px) { + font-size: 1.2rem; + } + } + .text { + margin-top: 18px; + + a { + color: #fff; + margin-top: 12px; + font-size: 1rem; + @media only screen and (min-width: 1023px) { + margin-top: 10px; + font-size: 1.2rem; + } + &:hover { + color: #aaa; + text-decoration: none; } } + } + .github { + display: none; + @media only screen and (min-width: 768px) { + display: inline-block; + } + } + .todocs { + display: inline-block; + @media only screen and (min-width: 768px) { + display: none; + } + } } .features { - text-align: center; - color: #333; - .inner { - padding: 10px 0px; + text-align: center; + color: #333; + .inner { + padding: 10px 0px; + @media only screen and (min-width: 768px) { + padding: 15px 20px; + } + .icons { + display: block; + font-size: 3rem; + color: #bbb; + @media only screen and (min-width: 420px) { + font-size: 3.5rem; + } @media only screen and (min-width: 768px) { - padding: 15px 20px; + font-size: 4rem; } - .icons { - display: block; - font-size: 3rem; - color: #bbb; - @media only screen and (min-width: 420px) { - font-size: 3.5rem; - } - @media only screen and (min-width: 768px) { - font-size: 4rem; - } - @media only screen and (min-width: 1024px) { - font-size: 5rem; - } - } - .icontitle { - font-size: 1.2rem; - } - - &:hover { - background: #f4f4f4; - color: rgb(86, 122, 223); - .icons { - color: rgb(86, 122, 223); - } + @media only screen and (min-width: 1024px) { + font-size: 5rem; } } + .icontitle { + font-size: 1.2rem; + } + + &:hover { + background: #f4f4f4; + color: rgb(86, 122, 223); + .icons { + color: rgb(86, 122, 223); + } + } + } } .sectionheader { - .title { - text-align: center; - padding-top: 20px; - font-size: 2rem; - @media only screen and (min-width: 420px) { - font-size: 2.3rem; - } - @media only screen and (min-width: 768px) { - text-align: left; - padding-top: 25px; - font-size: 2.6rem; - } - @media only screen and (min-width: 1024px) { - padding-top: 30px; - font-size: 3rem; - } + .title { + text-align: center; + padding-top: 20px; + font-size: 2rem; + @media only screen and (min-width: 420px) { + font-size: 2.3rem; } - .title-small { - text-align: center; - font-size: 1.4rem; - @media only screen and (min-width: 420px) { - font-size: 1.6rem; - } - @media only screen and (min-width: 768px) { - text-align: left; - padding-top: 25px; - font-size: 1.8rem; - } - @media only screen and (min-width: 1024px) { - padding-top: 30px; - font-size: 2rem; - } + @media only screen and (min-width: 768px) { + text-align: left; + padding-top: 25px; + font-size: 2.6rem; } - .subtitle { - text-align: center; - font-size: .9rem; - @media only screen and (min-width: 420px) { - font-size: 1rem; - } - @media only screen and (min-width: 768px) { - text-align: left; - font-size: 1.1rem; - } - @media only screen and (min-width: 1024px) { - font-size: 1.3rem; - } + @media only screen and (min-width: 1024px) { + padding-top: 30px; + font-size: 3rem; } - .npmicons { - text-align: center; + } + .title-small { + text-align: center; + font-size: 1.4rem; + @media only screen and (min-width: 420px) { + font-size: 1.6rem; } - .text { - text-align: center; - padding-top: 10px; - @media only screen and (min-width: 768px) { - text-align: left; - } + @media only screen and (min-width: 768px) { + text-align: left; + padding-top: 25px; + font-size: 1.8rem; } - hr { - margin-top: 40px; - margin-bottom: 0px; - border: 1px solid #ddd; + @media only screen and (min-width: 1024px) { + padding-top: 30px; + font-size: 2rem; } + } + .subtitle { + text-align: center; + font-size: 0.9rem; + @media only screen and (min-width: 420px) { + font-size: 1rem; + } + @media only screen and (min-width: 768px) { + text-align: left; + font-size: 1.1rem; + } + @media only screen and (min-width: 1024px) { + font-size: 1.3rem; + } + } + .npmicons { + text-align: center; + } + .text { + text-align: center; + padding-top: 10px; + @media only screen and (min-width: 768px) { + text-align: left; + } + } + hr { + margin-top: 40px; + margin-bottom: 0px; + border: 1px solid #ddd; + } } .index { margin: 0 0 10px 0; - .text, .subtitle, .title, .title-small, .npmicons { - text-align: center; + .text, + .subtitle, + .title, + .title-small, + .npmicons { + text-align: center; } a { + text-decoration: none; + &:hover { text-decoration: none; - &:hover { - text-decoration: none; - color: rgb(86, 122, 223); - } + color: rgb(86, 122, 223); + } } } .left { - .text, .subtitle, .title, .title-small .npmicons { - text-align: left; - } + .text, + .subtitle, + .title, + .title-small .npmicons { + text-align: left; + } } .table { - text-align: left; + text-align: left; } ul { - text-align: left; + text-align: left; } .number-section { - margin-top: 40px; - text-align: center; - .numbers { - font-size: 2rem; - @media only screen and (min-width: 420px) { - font-size: 2.4rem; - } - @media only screen and (min-width: 768px) { - font-size: 2.8rem; - } + margin-top: 40px; + text-align: center; + .numbers { + font-size: 2rem; + @media only screen and (min-width: 420px) { + font-size: 2.4rem; } - .title { - color: #999; - font-size: 1rem; - @media only screen and (min-width: 420px) { - font-size: 1.1rem; - } - @media only screen and (min-width: 768px) { - font-size: 1.2rem; - } + @media only screen and (min-width: 768px) { + font-size: 2.8rem; } + } + .title { + color: #999; + font-size: 1rem; + @media only screen and (min-width: 420px) { + font-size: 1.1rem; + } + @media only screen and (min-width: 768px) { + font-size: 1.2rem; + } + } } .menu { - display: none; - @media only screen and (min-width: 768px) { - margin-top: 30px; - display: inline-block; - } - .title { - margin: 25px 0 5px 0; - color: #000; - a { - width: 100%; - color: #000; - text-decoration: none; - &:hover { - color: rgb(86, 122, 223); - } - } - } - hr { - margin: 5px 0; - } - ul { - list-style-type: none; - margin: 0; - padding: 0; - li { - padding: 2px 0; - a { - width: 100%; - color: #666; - text-decoration: none; - &:hover { - color: rgb(86, 122, 223); - } - } - &.active > a { - color: rgb(86, 122, 223); - } - } - } -} -.code { - font-family: Menlo, "dejavu sans mono", "lucida console", monospace; - -webkit-font-smoothing: antialiased; - font-size: 14px; - font-style: normal; - font-variant: normal; - font-weight: 400; + display: none; + @media only screen and (min-width: 768px) { + margin-top: 30px; display: inline-block; - background-color: #eee; - margin: 0; - padding: 0 5px; -} -pre { - padding: 4px 10px 4px 10px; - font-family: Menlo, "dejavu sans mono", "lucida console", monospace; - -webkit-font-smoothing: antialiased; - font-size: 13px; - font-style: normal; - font-variant: normal; - font-weight: 400; - line-height: 18px; - background-color: #eee; - width: 100%; - text-align: left; -} -.example { - td:first-child { - background-color: #fff !important; - } - td:last-child { - background-color: #f8faff !important; - } - h5 { - padding-top: 5px; - padding-left: 10px; - } - pre { - background-color: unset; - .js { - background-color: #f0f0f4; - + } + .title { + margin: 25px 0 5px 0; + color: #000; + a { + width: 100%; + color: #000; + text-decoration: none; + &:hover { + color: rgb(86, 122, 223); } } + } + hr { + margin: 5px 0; + } + ul { + list-style-type: none; + margin: 0; + padding: 0; + li { + padding: 2px 0; + a { + width: 100%; + color: #666; + text-decoration: none; + &:hover { + color: rgb(86, 122, 223); + } + } + &.active > a { + color: rgb(86, 122, 223); + } + } + } +} +.code { + font-family: Menlo, "dejavu sans mono", "lucida console", monospace; + -webkit-font-smoothing: antialiased; + font-size: 14px; + font-style: normal; + font-variant: normal; + font-weight: 400; + display: inline-block; + background-color: #eee; + margin: 0; + padding: 0 5px; +} +pre { + padding: 4px 10px 4px 10px; + font-family: Menlo, "dejavu sans mono", "lucida console", monospace; + -webkit-font-smoothing: antialiased; + font-size: 13px; + font-style: normal; + font-variant: normal; + font-weight: 400; + line-height: 18px; + background-color: #eee; + width: 100%; + text-align: left; +} +.example { + td:first-child { + background-color: #fff !important; + } + td:last-child { + background-color: #f8faff !important; + } + h5 { + padding-top: 5px; + padding-left: 10px; + } + pre { + background-color: unset; + .js { + background-color: #f0f0f4; + } + } } .footer { - background-color: #eee; - padding-top: 30px; - padding-bottom: 30px; - position: relative; - a { - color: #000; - } - .badge { - color: #fff; - font-size: 0.9rem; - } + background-color: #eee; + padding-top: 30px; + padding-bottom: 30px; + position: relative; + a { + color: #000; + } + .badge { + color: #fff; + font-size: 0.9rem; + } } .larger { margin-top: 10px; font-size: 1.5rem !important; } .v5 { - color:rgb(86, 122, 223); + color: rgb(86, 122, 223); } diff --git a/lib/graphics.js b/lib/graphics.js index c588b4f..d002516 100644 --- a/lib/graphics.js +++ b/lib/graphics.js @@ -1,1063 +1,1062 @@ -'use strict'; -// @ts-check -// ================================================================================== -// graphics.js -// ---------------------------------------------------------------------------------- -// Description: System Information - library -// for Node.js -// Copyright: (c) 2014 - 2021 -// Author: Sebastian Hildebrandt -// ---------------------------------------------------------------------------------- -// License: MIT -// ================================================================================== -// 7. Graphics (controller, display) -// ---------------------------------------------------------------------------------- - -const os = require('os'); -const fs = require('fs'); -const exec = require('child_process').exec; -const execSync = require('child_process').execSync; -const util = require('./util'); - -let _platform = process.platform; -let _nvidiaSmiPath = ''; - -const _linux = (_platform === 'linux'); -const _darwin = (_platform === 'darwin'); -const _windows = (_platform === 'win32'); -const _freebsd = (_platform === 'freebsd'); -const _openbsd = (_platform === 'openbsd'); -const _netbsd = (_platform === 'netbsd'); -const _sunos = (_platform === 'sunos'); - -let _resolutionX = 0; -let _resolutionY = 0; -let _pixelDepth = 0; -let _refreshRate = 0; - -const videoTypes = { - '-2': 'UNINITIALIZED', - '-1': 'OTHER', - '0': 'HD15', - '1': 'SVIDEO', - '2': 'Composite video', - '3': 'Component video', - '4': 'DVI', - '5': 'HDMI', - '6': 'LVDS', - '8': 'D_JPN', - '9': 'SDI', - '10': 'DP', - '11': 'DP embedded', - '12': 'UDI', - '13': 'UDI embedded', - '14': 'SDTVDONGLE', - '15': 'MIRACAST', - '2147483648': 'INTERNAL' -}; - -function getVendorFromModel(model) { - const manufacturers = [ - { pattern: '^LG.+', manufacturer: 'LG' }, - { pattern: '^BENQ.+', manufacturer: 'BenQ' }, - { pattern: '^ASUS.+', manufacturer: 'Asus' }, - { pattern: '^DELL.+', manufacturer: 'Dell' }, - { pattern: '^SAMSUNG.+', manufacturer: 'Samsung' }, - { pattern: '^VIEWSON.+', manufacturer: 'ViewSonic' }, - { pattern: '^SONY.+', manufacturer: 'Sony' }, - { pattern: '^ACER.+', manufacturer: 'Acer' }, - { pattern: '^AOC.+', manufacturer: 'AOC Monitors' }, - { pattern: '^HP.+', manufacturer: 'HP' }, - { pattern: '^EIZO.?', manufacturer: 'Eizo' }, - { pattern: '^PHILIPS.?', manufacturer: 'Philips' }, - { pattern: '^IIYAMA.?', manufacturer: 'Iiyama' }, - { pattern: '^SHARP.?', manufacturer: 'Sharp' }, - { pattern: '^NEC.?', manufacturer: 'NEC' }, - { pattern: '^LENOVO.?', manufacturer: 'Lenovo' }, - { pattern: 'COMPAQ.?', manufacturer: 'Compaq' }, - { pattern: 'APPLE.?', manufacturer: 'Apple' }, - { pattern: 'INTEL.?', manufacturer: 'Intel' }, - { pattern: 'AMD.?', manufacturer: 'AMD' }, - { pattern: 'NVIDIA.?', manufacturer: 'NVDIA' }, - ]; - - let result = ''; - if (model) { - model = model.toUpperCase(); - manufacturers.forEach((manufacturer) => { - const re = RegExp(manufacturer.pattern); - if (re.test(model)) { result = manufacturer.manufacturer; } - }); - } - return result; -} - -function getVendorFromId(id) { - const vendors = { - '610': 'Apple', - '1e6d': 'LG', - '10ac': 'DELL', - '4dd9': 'Sony', - '38a3': 'NEC', - }; - return vendors[id] || ''; -} - -function vendorToId(str) { - let result = ''; - str = (str || '').toLowerCase(); - if (str.indexOf('apple') >= 0) { result = '0x05ac'; } - else if (str.indexOf('nvidia') >= 0) { result = '0x10de'; } - else if (str.indexOf('intel') >= 0) { result = '0x8086'; } - else if (str.indexOf('ati') >= 0 || str.indexOf('amd') >= 0) { result = '0x1002'; } - - return result; -} - -function getMetalVersion(id) { - const families = { - 'spdisplays_mtlgpufamilymac1': 'mac1', - 'spdisplays_mtlgpufamilymac2': 'mac2', - 'spdisplays_mtlgpufamilyapple1': 'apple1', - 'spdisplays_mtlgpufamilyapple2': 'apple2', - 'spdisplays_mtlgpufamilyapple3': 'apple3', - 'spdisplays_mtlgpufamilyapple4': 'apple4', - 'spdisplays_mtlgpufamilyapple5': 'apple5', - 'spdisplays_mtlgpufamilyapple6': 'apple6', - 'spdisplays_mtlgpufamilyapple7': 'apple7', - 'spdisplays_metalfeaturesetfamily11': 'family1_v1', - 'spdisplays_metalfeaturesetfamily12': 'family1_v2', - 'spdisplays_metalfeaturesetfamily13': 'family1_v3', - 'spdisplays_metalfeaturesetfamily14': 'family1_v4', - 'spdisplays_metalfeaturesetfamily21': 'family2_v1' - }; - return families[id] || ''; -} - -function graphics(callback) { - - function parseLinesDarwin(graphicsArr) { - const res = { - controllers: [], - displays: [] - }; - try { - graphicsArr.forEach(function (item) { - // controllers - const bus = ((item.sppci_bus || '').indexOf('builtin') > -1 ? 'Built-In' : ((item.sppci_bus || '').indexOf('pcie') > -1 ? 'PCIe' : '')); - const vram = (parseInt((item.spdisplays_vram || ''), 10) || 0) * (((item.spdisplays_vram || '').indexOf('GB') > -1) ? 1024 : 1); - const vramDyn = (parseInt((item.spdisplays_vram_shared || ''), 10) || 0) * (((item.spdisplays_vram_shared || '').indexOf('GB') > -1) ? 1024 : 1); - let metalVersion = getMetalVersion(item.spdisplays_metal || item.spdisplays_metalfamily || ''); - res.controllers.push({ - vendor: getVendorFromModel(item.spdisplays_vendor || '') || item.spdisplays_vendor || '', - model: item.sppci_model || '', - bus, - vramDynamic: bus === 'Built-In', - vram: vram || vramDyn || null, - deviceId: item['spdisplays_device-id'] || '', - vendorId: item['spdisplays_vendor-id'] || vendorToId((item['spdisplays_vendor'] || '') + (item.sppci_model || '')), - external: (item.sppci_device_type === 'spdisplays_egpu'), - cores: item['sppci_cores'] || null, - metalVersion - }); - - // displays - if (item.spdisplays_ndrvs && item.spdisplays_ndrvs.length) { - item.spdisplays_ndrvs.forEach(function (displayItem) { - const connectionType = displayItem['spdisplays_connection_type'] || ''; - const currentResolutionParts = (displayItem['_spdisplays_resolution'] || '').split('@'); - const currentResolution = currentResolutionParts[0].split('x'); - const pixelParts = (displayItem['_spdisplays_pixels'] || '').split('x'); - const pixelDepthString = displayItem['spdisplays_depth'] || ''; - const serial = displayItem['_spdisplays_display-serial-number'] || displayItem['_spdisplays_display-serial-number2'] || null; - res.displays.push({ - vendor: getVendorFromId(displayItem['_spdisplays_display-vendor-id'] || '') || getVendorFromModel(displayItem['_name'] || ''), - vendorId: displayItem['_spdisplays_display-vendor-id'] || '', - model: displayItem['_name'] || '', - productionYear: displayItem['_spdisplays_display-year'] || null, - serial: serial !== '0' ? serial : null, - displayId: displayItem['_spdisplays_displayID'] || null, - main: displayItem['spdisplays_main'] ? displayItem['spdisplays_main'] === 'spdisplays_yes' : false, - builtin: (displayItem['spdisplays_display_type'] || '').indexOf('built-in') > -1, - connection: ((connectionType.indexOf('_internal') > -1) ? 'Internal' : ((connectionType.indexOf('_displayport') > -1) ? 'Display Port' : ((connectionType.indexOf('_hdmi') > -1) ? 'HDMI' : null))), - sizeX: null, - sizeY: null, - pixelDepth: (pixelDepthString === 'CGSThirtyBitColor' ? 30 : (pixelDepthString === 'CGSThirtytwoBitColor' ? 32 : (pixelDepthString === 'CGSTwentyfourBitColor' ? 24 : ''))), - resolutionX: pixelParts.length > 1 ? parseInt(pixelParts[0], 10) : null, - resolutionY: pixelParts.length > 1 ? parseInt(pixelParts[1], 10) : null, - currentResX: currentResolution.length > 1 ? parseInt(currentResolution[0], 10) : null, - currentResY: currentResolution.length > 1 ? parseInt(currentResolution[1], 10) : null, - positionX: 0, - positionY: 0, - currentRefreshRate: currentResolutionParts.length > 1 ? parseInt(currentResolutionParts[1], 10) : null, - - }); - }); - } - }); - return res; - } catch (e) { - return res; - } - } - - function parseLinesLinuxControllers(lines) { - let controllers = []; - let currentController = { - vendor: '', - model: '', - bus: '', - busAddress: '', - vram: null, - vramDynamic: false, - pciID: '' - }; - let isGraphicsController = false; - // PCI bus IDs - let pciIDs = []; - try { - pciIDs = execSync('export LC_ALL=C; dmidecode -t 9 2>/dev/null; unset LC_ALL | grep "Bus Address: "').toString().split('\n'); - for (let i = 0; i < pciIDs.length; i++) { - pciIDs[i] = pciIDs[i].replace('Bus Address:', '').replace('0000:', '').trim(); - } - pciIDs = pciIDs.filter(function (el) { - return el != null && el; - }); - } catch (e) { - util.noop(); - } - for (let i = 0; i < lines.length; i++) { - if ('' !== lines[i].trim()) { - if (' ' !== lines[i][0] && '\t' !== lines[i][0]) { // first line of new entry - let isExternal = (pciIDs.indexOf(lines[i].split(' ')[0]) >= 0); - let vgapos = lines[i].toLowerCase().indexOf(' vga '); - let _3dcontrollerpos = lines[i].toLowerCase().indexOf('3d controller'); - if (vgapos !== -1 || _3dcontrollerpos !== -1) { // VGA - if (_3dcontrollerpos !== -1 && vgapos === -1) { - vgapos = _3dcontrollerpos; - } - if (currentController.vendor || currentController.model || currentController.bus || currentController.vram !== null || currentController.vramDynamic) { // already a controller found - controllers.push(currentController); - currentController = { - vendor: '', - model: '', - bus: '', - busAddress: '', - vram: null, - vramDynamic: false, - }; - } - - const pciIDCandidate = lines[i].split(' ')[0]; - if (/[\da-fA-F]{2}:[\da-fA-F]{2}\.[\da-fA-F]/.test(pciIDCandidate)) { - currentController.busAddress = pciIDCandidate; - } - isGraphicsController = true; - let endpos = lines[i].search(/\[[0-9a-f]{4}:[0-9a-f]{4}]|$/); - let parts = lines[i].substr(vgapos, endpos - vgapos).split(':'); - currentController.busAddress = lines[i].substr(0, vgapos).trim(); - if (parts.length > 1) { - parts[1] = parts[1].trim(); - if (parts[1].toLowerCase().indexOf('corporation') >= 0) { - currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf('corporation') + 11).trim(); - currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf('corporation') + 11, 200).trim().split('(')[0]; - currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard'; - currentController.vram = null; - currentController.vramDynamic = false; - } else if (parts[1].toLowerCase().indexOf(' inc.') >= 0) { - if ((parts[1].match(new RegExp(']', 'g')) || []).length > 1) { - currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim(); - currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim(); - } else { - currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' inc.') + 5).trim(); - currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' inc.') + 5, 200).trim().split('(')[0].trim(); - } - currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard'; - currentController.vram = null; - currentController.vramDynamic = false; - } else if (parts[1].toLowerCase().indexOf(' ltd.') >= 0) { - if ((parts[1].match(new RegExp(']', 'g')) || []).length > 1) { - currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim(); - currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim(); - } else { - currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' ltd.') + 5).trim(); - currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' ltd.') + 5, 200).trim().split('(')[0].trim(); - } - } - } - - } else { - isGraphicsController = false; - } - } - if (isGraphicsController) { // within VGA details - let parts = lines[i].split(':'); - if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('devicename') !== -1 && parts[1].toLowerCase().indexOf('onboard') !== -1) { currentController.bus = 'Onboard'; } - if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('region') !== -1 && parts[1].toLowerCase().indexOf('memory') !== -1) { - let memparts = parts[1].split('='); - if (memparts.length > 1) { - currentController.vram = parseInt(memparts[1]); - } - } - } - } - } - if (currentController.vendor || currentController.model || currentController.bus || currentController.busAddress || currentController.vram !== null || currentController.vramDynamic) { // already a controller found - controllers.push(currentController); - } - return (controllers); - } - - function parseLinesLinuxClinfo(controllers, lines) { - const fieldPattern = /\[([^\]]+)\]\s+(\w+)\s+(.*)/; - const devices = lines.reduce((devices, line) => { - const field = fieldPattern.exec(line.trim()); - if (field) { - if (!devices[field[1]]) { - devices[field[1]] = {}; - } - devices[field[1]][field[2]] = field[3]; - } - return devices; - }, {}); - for (let deviceId in devices) { - const device = devices[deviceId]; - if (device['CL_DEVICE_TYPE'] === 'CL_DEVICE_TYPE_GPU') { - let busAddress; - if (device['CL_DEVICE_TOPOLOGY_AMD']) { - const bdf = device['CL_DEVICE_TOPOLOGY_AMD'].match(/[a-zA-Z0-9]+:\d+\.\d+/); - if (bdf) { - busAddress = bdf[0]; - } - } else if (device['CL_DEVICE_PCI_BUS_ID_NV'] && device['CL_DEVICE_PCI_SLOT_ID_NV']) { - const bus = parseInt(device['CL_DEVICE_PCI_BUS_ID_NV']); - const slot = parseInt(device['CL_DEVICE_PCI_SLOT_ID_NV']); - if (!isNaN(bus) && !isNaN(slot)) { - const b = bus & 0xff; - const d = (slot >> 3) & 0xff; - const f = slot & 0x07; - busAddress = `${b.toString().padStart(2, '0')}:${d.toString().padStart(2, '0')}.${f}`; - } - } - if (busAddress) { - let controller = controllers.find(controller => controller.busAddress === busAddress); - if (!controller) { - controller = { - vendor: '', - model: '', - bus: '', - busAddress, - vram: null, - vramDynamic: false - }; - controllers.push(controller); - } - controller.vendor = device['CL_DEVICE_VENDOR']; - if (device['CL_DEVICE_BOARD_NAME_AMD']) { - controller.model = device['CL_DEVICE_BOARD_NAME_AMD']; - } else { - controller.model = device['CL_DEVICE_NAME']; - } - const memory = parseInt(device['CL_DEVICE_GLOBAL_MEM_SIZE']); - if (!isNaN(memory)) { - controller.vram = Math.round(memory / 1024 / 1024); - } - } - } - } - return controllers; - } - - function getNvidiaSmi() { - if (_nvidiaSmiPath) { - return _nvidiaSmiPath; - } - - if (_windows) { - try { - const basePath = util.WINDIR + '\\System32\\DriverStore\\FileRepository'; - // find all directories that have an nvidia-smi.exe file - const candidateDirs = fs.readdirSync(basePath).filter(dir => { - return fs.readdirSync([basePath, dir].join('/')).includes('nvidia-smi.exe'); - }); - // use the directory with the most recently created nvidia-smi.exe file - const targetDir = candidateDirs.reduce((prevDir, currentDir) => { - const previousNvidiaSmi = fs.statSync([basePath, prevDir, 'nvidia-smi.exe'].join('/')); - const currentNvidiaSmi = fs.statSync([basePath, currentDir, 'nvidia-smi.exe'].join('/')); - return (previousNvidiaSmi.ctimeMs > currentNvidiaSmi.ctimeMs) ? prevDir : currentDir; - }); - - if (targetDir) { - _nvidiaSmiPath = [basePath, targetDir, 'nvidia-smi.exe'].join('/'); - } - } catch (e) { - util.noop(); - } - } else if (_linux) { - _nvidiaSmiPath = 'nvidia-smi'; - } - return _nvidiaSmiPath; - } - - function nvidiaSmi(options) { - const nvidiaSmiExe = getNvidiaSmi(); - options = options || util.execOptsWin; - if (nvidiaSmiExe) { - 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'; - const cmd = nvidiaSmiExe + ' ' + nvidiaSmiOpts + (_linux ? ' 2>/dev/null' : ''); - try { - const res = execSync(cmd, options).toString(); - return res; - } catch (e) { - util.noop(); - } - } - return ''; - } - - function nvidiaDevices() { - - function safeParseNumber(value) { - if ([null, undefined].includes(value)) { - return value; - } - return parseFloat(value); - } - - const stdout = nvidiaSmi(); - if (!stdout) { - return []; - } - - const gpus = stdout.split('\n').filter(Boolean); - const results = gpus.map(gpu => { - const splittedData = gpu.split(', ').map(value => value.includes('N/A') ? undefined : value); - if (splittedData.length === 16) { - return { - driverVersion: splittedData[0], - subDeviceId: splittedData[1], - name: splittedData[2], - pciBus: splittedData[3], - fanSpeed: safeParseNumber(splittedData[4]), - memoryTotal: safeParseNumber(splittedData[5]), - memoryUsed: safeParseNumber(splittedData[6]), - memoryFree: safeParseNumber(splittedData[7]), - utilizationGpu: safeParseNumber(splittedData[8]), - utilizationMemory: safeParseNumber(splittedData[9]), - temperatureGpu: safeParseNumber(splittedData[10]), - temperatureMemory: safeParseNumber(splittedData[11]), - powerDraw: safeParseNumber(splittedData[12]), - powerLimit: safeParseNumber(splittedData[13]), - clockCore: safeParseNumber(splittedData[14]), - clockMemory: safeParseNumber(splittedData[15]), - }; - } - }); - - return results; - } - - function mergeControllerNvidia(controller, nvidia) { - if (nvidia.driverVersion) { controller.driverVersion = nvidia.driverVersion; } - if (nvidia.subDeviceId) { controller.subDeviceId = nvidia.subDeviceId; } - if (nvidia.name) { controller.name = nvidia.name; } - if (nvidia.pciBus) { controller.pciBus = nvidia.pciBus; } - if (nvidia.fanSpeed) { controller.fanSpeed = nvidia.fanSpeed; } - if (nvidia.memoryTotal) { - controller.memoryTotal = nvidia.memoryTotal; - controller.vram = nvidia.memoryTotal; - controller.vramDynamic = false; - } - if (nvidia.memoryUsed) { controller.memoryUsed = nvidia.memoryUsed; } - if (nvidia.memoryFree) { controller.memoryFree = nvidia.memoryFree; } - if (nvidia.utilizationGpu) { controller.utilizationGpu = nvidia.utilizationGpu; } - if (nvidia.utilizationMemory) { controller.utilizationMemory = nvidia.utilizationMemory; } - if (nvidia.temperatureGpu) { controller.temperatureGpu = nvidia.temperatureGpu; } - if (nvidia.temperatureMemory) { controller.temperatureMemory = nvidia.temperatureMemory; } - if (nvidia.powerDraw) { controller.powerDraw = nvidia.powerDraw; } - if (nvidia.powerLimit) { controller.powerLimit = nvidia.powerLimit; } - if (nvidia.clockCore) { controller.clockCore = nvidia.clockCore; } - if (nvidia.clockMemory) { controller.clockMemory = nvidia.clockMemory; } - return controller; - } - - function parseLinesLinuxEdid(edid) { - // parsen EDID - // --> model - // --> resolutionx - // --> resolutiony - // --> builtin = false - // --> pixeldepth (?) - // --> sizex - // --> sizey - let result = { - vendor: '', - model: '', - deviceName: '', - main: false, - builtin: false, - connection: '', - sizeX: null, - sizeY: null, - pixelDepth: null, - resolutionX: null, - resolutionY: null, - currentResX: null, - currentResY: null, - positionX: 0, - positionY: 0, - currentRefreshRate: null - }; - // find first "Detailed Timing Description" - let start = 108; - if (edid.substr(start, 6) === '000000') { - start += 36; - } - if (edid.substr(start, 6) === '000000') { - start += 36; - } - if (edid.substr(start, 6) === '000000') { - start += 36; - } - if (edid.substr(start, 6) === '000000') { - start += 36; - } - result.resolutionX = parseInt('0x0' + edid.substr(start + 8, 1) + edid.substr(start + 4, 2)); - result.resolutionY = parseInt('0x0' + edid.substr(start + 14, 1) + edid.substr(start + 10, 2)); - result.sizeX = parseInt('0x0' + edid.substr(start + 28, 1) + edid.substr(start + 24, 2)); - result.sizeY = parseInt('0x0' + edid.substr(start + 29, 1) + edid.substr(start + 26, 2)); - // monitor name - start = edid.indexOf('000000fc00'); // find first "Monitor Description Data" - if (start >= 0) { - let model_raw = edid.substr(start + 10, 26); - if (model_raw.indexOf('0a') !== -1) { - model_raw = model_raw.substr(0, model_raw.indexOf('0a')); - } - try { - if (model_raw.length > 2) { - result.model = model_raw.match(/.{1,2}/g).map(function (v) { - return String.fromCharCode(parseInt(v, 16)); - }).join(''); - } - } catch (e) { - util.noop(); - } - } else { - result.model = ''; - } - return result; - } - - function parseLinesLinuxDisplays(lines, depth) { - let displays = []; - let currentDisplay = { - vendor: '', - model: '', - deviceName: '', - main: false, - builtin: false, - connection: '', - sizeX: null, - sizeY: null, - pixelDepth: null, - resolutionX: null, - resolutionY: null, - currentResX: null, - currentResY: null, - positionX: 0, - positionY: 0, - currentRefreshRate: null - }; - let is_edid = false; - let is_current = false; - let edid_raw = ''; - let start = 0; - for (let i = 1; i < lines.length; i++) { // start with second line - if ('' !== lines[i].trim()) { - if (' ' !== lines[i][0] && '\t' !== lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') !== -1) { // first line of new entry - if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // push last display to array - displays.push(currentDisplay); - currentDisplay = { - vendor: '', - model: '', - main: false, - builtin: false, - connection: '', - sizeX: null, - sizeY: null, - pixelDepth: null, - resolutionX: null, - resolutionY: null, - currentResX: null, - currentResY: null, - positionX: 0, - positionY: 0, - currentRefreshRate: null - }; - } - let parts = lines[i].split(' '); - currentDisplay.connection = parts[0]; - currentDisplay.main = lines[i].toLowerCase().indexOf(' primary ') >= 0; - currentDisplay.builtin = (parts[0].toLowerCase().indexOf('edp') >= 0); - } - - // try to read EDID information - if (is_edid) { - if (lines[i].search(/\S|$/) > start) { - edid_raw += lines[i].toLowerCase().trim(); - } else { - // parsen EDID - let edid_decoded = parseLinesLinuxEdid(edid_raw); - currentDisplay.vendor = edid_decoded.vendor; - currentDisplay.model = edid_decoded.model; - currentDisplay.resolutionX = edid_decoded.resolutionX; - currentDisplay.resolutionY = edid_decoded.resolutionY; - currentDisplay.sizeX = edid_decoded.sizeX; - currentDisplay.sizeY = edid_decoded.sizeY; - currentDisplay.pixelDepth = depth; - is_edid = false; - } - } - if (lines[i].toLowerCase().indexOf('edid:') >= 0) { - is_edid = true; - start = lines[i].search(/\S|$/); - } - if (lines[i].toLowerCase().indexOf('*current') >= 0) { - const parts1 = lines[i].split('('); - if (parts1 && parts1.length > 1 && parts1[0].indexOf('x') >= 0) { - const resParts = parts1[0].trim().split('x'); - currentDisplay.currentResX = util.toInt(resParts[0]); - currentDisplay.currentResY = util.toInt(resParts[1]); - } - is_current = true; - } - if (is_current && lines[i].toLowerCase().indexOf('clock') >= 0 && lines[i].toLowerCase().indexOf('hz') >= 0 && lines[i].toLowerCase().indexOf('v: height') >= 0) { - const parts1 = lines[i].split('clock'); - if (parts1 && parts1.length > 1 && parts1[1].toLowerCase().indexOf('hz') >= 0) { - currentDisplay.currentRefreshRate = util.toInt(parts1[1]); - } - is_current = false; - } - } - } - - // pushen displays - if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // still information there - displays.push(currentDisplay); - } - return displays; - } - - // function starts here - return new Promise((resolve) => { - process.nextTick(() => { - let result = { - controllers: [], - displays: [] - }; - if (_darwin) { - let cmd = 'system_profiler -xml -detailLevel full SPDisplaysDataType'; - exec(cmd, function (error, stdout) { - if (!error) { - try { - let output = stdout.toString(); - result = parseLinesDarwin(util.plistParser(output)[0]._items); - } catch (e) { - util.noop(); - } - } - if (callback) { - callback(result); - } - resolve(result); - }); - } - if (_linux) { - // Raspberry: https://elinux.org/RPI_vcgencmd_usage - if (util.isRaspberry() && util.isRaspbian()) { - let cmd = 'fbset -s | grep \'mode "\'; vcgencmd get_mem gpu; tvservice -s; tvservice -n;'; - exec(cmd, function (error, stdout) { - let lines = stdout.toString().split('\n'); - if (lines.length > 3 && lines[0].indexOf('mode "') >= -1 && lines[2].indexOf('0x12000a') > -1) { - const parts = lines[0].replace('mode', '').replace(/"/g, '').trim().split('x'); - if (parts.length === 2) { - result.displays.push({ - vendor: '', - model: util.getValue(lines, 'device_name', '='), - main: true, - builtin: false, - connection: 'HDMI', - sizeX: null, - sizeY: null, - pixelDepth: null, - resolutionX: parseInt(parts[0], 10), - resolutionY: parseInt(parts[1], 10), - currentResX: null, - currentResY: null, - positionX: 0, - positionY: 0, - currentRefreshRate: null - }); - } - } - if (lines.length > 1 && stdout.toString().indexOf('gpu=') >= -1) { - result.controllers.push({ - vendor: 'Broadcom', - model: 'VideoCore IV', - bus: '', - vram: util.getValue(lines, 'gpu', '=').replace('M', ''), - vramDynamic: true - }); - } - if (callback) { - callback(result); - } - resolve(result); - }); - } else { - let cmd = 'lspci -vvv 2>/dev/null'; - exec(cmd, function (error, stdout) { - if (!error) { - let lines = stdout.toString().split('\n'); - result.controllers = parseLinesLinuxControllers(lines); - const nvidiaData = nvidiaDevices(); - // needs to be rewritten ... using no spread operators - result.controllers = result.controllers.map((controller) => { // match by busAddress - return mergeControllerNvidia(controller, nvidiaData.find((contr) => contr.pciBus.toLowerCase().endsWith(controller.busAddress.toLowerCase())) || {}); - }); - } - let cmd = 'clinfo --raw'; - exec(cmd, function (error, stdout) { - if (!error) { - let lines = stdout.toString().split('\n'); - result.controllers = parseLinesLinuxClinfo(result.controllers, lines); - } - let cmd = 'xdpyinfo 2>/dev/null | grep \'depth of root window\' | awk \'{ print $5 }\''; - exec(cmd, function (error, stdout) { - let depth = 0; - if (!error) { - let lines = stdout.toString().split('\n'); - depth = parseInt(lines[0]) || 0; - } - let cmd = 'xrandr --verbose 2>/dev/null'; - exec(cmd, function (error, stdout) { - if (!error) { - let lines = stdout.toString().split('\n'); - result.displays = parseLinesLinuxDisplays(lines, depth); - } - if (callback) { - callback(result); - } - resolve(result); - }); - }); - }); - }); - } - } - if (_freebsd || _openbsd || _netbsd) { - if (callback) { callback(null); } - resolve(null); - } - if (_sunos) { - if (callback) { callback(null); } - resolve(null); - } - if (_windows) { - - // https://blogs.technet.microsoft.com/heyscriptingguy/2013/10/03/use-powershell-to-discover-multi-monitor-information/ - // https://devblogs.microsoft.com/scripting/use-powershell-to-discover-multi-monitor-information/ - try { - const workload = []; - workload.push(util.powerShell('Get-WmiObject 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-WmiObject 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 -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}')); - - const nvidiaData = nvidiaDevices(); - - Promise.all( - workload - ).then(data => { - // controller + vram - let csections = data[0].split(/\n\s*\n/); - let vsections = data[1].split(/\n\s*\n/); - result.controllers = parseLinesWindowsControllers(csections, vsections); - result.controllers = result.controllers.map((controller) => { // match by subDeviceId - if (controller.vendor.toLowerCase() === 'nvidia') { - return mergeControllerNvidia(controller, nvidiaData.find(device => { - let windowsSubDeviceId = controller.subDeviceId.toLowerCase(); - const nvidiaSubDeviceIdParts = device.subDeviceId.split('x'); - let nvidiaSubDeviceId = nvidiaSubDeviceIdParts.length > 1 ? nvidiaSubDeviceIdParts[1].toLowerCase() : nvidiaSubDeviceIdParts[0].toLowerCase(); - const lengthDifference = Math.abs(windowsSubDeviceId.length - nvidiaSubDeviceId.length); - if (windowsSubDeviceId.length > nvidiaSubDeviceId.length) { - for (let i = 0; i < lengthDifference; i++) { - nvidiaSubDeviceId = '0' + nvidiaSubDeviceId; - } - } else if (windowsSubDeviceId.length < nvidiaSubDeviceId.length) { - for (let i = 0; i < lengthDifference; i++) { - windowsSubDeviceId = '0' + windowsSubDeviceId; - } - } - return windowsSubDeviceId === nvidiaSubDeviceId; - }) || {}); - } else { - return controller; - } - }); - - // displays - let dsections = data[2].split(/\n\s*\n/); - // result.displays = parseLinesWindowsDisplays(dsections); - dsections.shift(); - dsections.pop(); - - // monitor (powershell) - let msections = data[3].split('Active '); - msections.shift(); - - // forms.screens (powershell) - let ssections = data[4].split('BitsPerPixel '); - ssections.shift(); - - // connection params (powershell) - video type - let tsections = data[5].split(/\n\s*\n/); - tsections.shift(); - - // monitor ID (powershell) - model / vendor - const res = data[6].split(/\r\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; - } - } - if (_pixelDepth) { - result.displays[0].pixelDepth = _pixelDepth; - } - if (_refreshRate && !result.displays[0].refreshRate) { - result.displays[0].currentRefreshRate = _refreshRate; - } - } - - if (callback) { - callback(result); - } - resolve(result); - }) - .catch(() => { - if (callback) { - callback(result); - } - resolve(result); - }); - } catch (e) { - if (callback) { callback(result); } - resolve(result); - } - } - }); - }); - - function parseLinesWindowsControllers(sections, vections) { - const memorySizes = {}; - for (const i in vections) { - if ({}.hasOwnProperty.call(vections, i)) { - if (vections[i].trim() !== '') { - const lines = vections[i].trim().split(os.EOL); - 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); - if (matchingDeviceId) { - const quadWordmemorySize = parseInt(util.getValue(lines, 'HardwareInformation.qwMemorySize')); - if (!isNaN(quadWordmemorySize)) { - let deviceId = matchingDeviceId[1].toUpperCase() + '&' + matchingDeviceId[2].toUpperCase(); - if (matchingDeviceId[3]) { - deviceId += '&' + matchingDeviceId[3].toUpperCase(); - } - if (matchingDeviceId[4]) { - deviceId += '&' + matchingDeviceId[4].toUpperCase(); - } - memorySizes[deviceId] = quadWordmemorySize; - } - } - } - } - } - - let controllers = []; - for (let i in sections) { - if ({}.hasOwnProperty.call(sections, i)) { - if (sections[i].trim() !== '') { - let lines = sections[i].trim().split('\r\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); - let subDeviceId = null; - let memorySize = null; - if (pnpDeviceId) { - subDeviceId = pnpDeviceId[3]; - if (subDeviceId) { - subDeviceId = subDeviceId.split('_')[1]; - } - - // Match PCI device identifier (there's an order of increasing generality): - // https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-pci-devices - - // PCI\VEN_v(4)&DEV_d(4)&SUBSYS_s(4)n(4)&REV_r(2) - if (memorySize == null && pnpDeviceId[3] && pnpDeviceId[4]) { - const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[3].toUpperCase() + '&' + pnpDeviceId[4].toUpperCase(); - if ({}.hasOwnProperty.call(memorySizes, deviceId)) { - memorySize = memorySizes[deviceId]; - } - } - - // PCI\VEN_v(4)&DEV_d(4)&SUBSYS_s(4)n(4) - if (memorySize == null && pnpDeviceId[3]) { - const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[3].toUpperCase(); - if ({}.hasOwnProperty.call(memorySizes, deviceId)) { - memorySize = memorySizes[deviceId]; - } - } - - // PCI\VEN_v(4)&DEV_d(4)&REV_r(2) - if (memorySize == null && pnpDeviceId[4]) { - const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[4].toUpperCase(); - if ({}.hasOwnProperty.call(memorySizes, deviceId)) { - memorySize = memorySizes[deviceId]; - } - } - - // PCI\VEN_v(4)&DEV_d(4) - if (memorySize == null) { - const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase(); - if ({}.hasOwnProperty.call(memorySizes, deviceId)) { - memorySize = memorySizes[deviceId]; - } - } - } - - controllers.push({ - vendor: util.getValue(lines, 'AdapterCompatibility', ':'), - model: util.getValue(lines, 'name', ':'), - bus: util.getValue(lines, 'PNPDeviceID', ':').startsWith('PCI') ? 'PCI' : '', - vram: (memorySize == null ? util.toInt(util.getValue(lines, 'AdapterRAM', ':')) : memorySize) / 1024 / 1024, - vramDynamic: (util.getValue(lines, 'VideoMemoryType', ':') === '2'), - subDeviceId - }); - _resolutionX = util.toInt(util.getValue(lines, 'CurrentHorizontalResolution', ':')) || _resolutionX; - _resolutionY = util.toInt(util.getValue(lines, 'CurrentVerticalResolution', ':')) || _resolutionY; - _refreshRate = util.toInt(util.getValue(lines, 'CurrentRefreshRate', ':')) || _refreshRate; - _pixelDepth = util.toInt(util.getValue(lines, 'CurrentBitsPerPixel', ':')) || _pixelDepth; - } - } - } - 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(os.EOL); - 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(os.EOL); - let linesMonitor = msections[i].split(os.EOL); - - let linesConnection = tsections[i].split(os.EOL); - const bitsPerPixel = util.getValue(linesScreen, 'BitsPerPixel'); - const bounds = util.getValue(linesScreen, 'Bounds').replace('{', '').replace('}', '').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; +'use strict'; +// @ts-check +// ================================================================================== +// graphics.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2021 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 7. Graphics (controller, display) +// ---------------------------------------------------------------------------------- + +const fs = require('fs'); +const exec = require('child_process').exec; +const execSync = require('child_process').execSync; +const util = require('./util'); + +let _platform = process.platform; +let _nvidiaSmiPath = ''; + +const _linux = (_platform === 'linux'); +const _darwin = (_platform === 'darwin'); +const _windows = (_platform === 'win32'); +const _freebsd = (_platform === 'freebsd'); +const _openbsd = (_platform === 'openbsd'); +const _netbsd = (_platform === 'netbsd'); +const _sunos = (_platform === 'sunos'); + +let _resolutionX = 0; +let _resolutionY = 0; +let _pixelDepth = 0; +let _refreshRate = 0; + +const videoTypes = { + '-2': 'UNINITIALIZED', + '-1': 'OTHER', + '0': 'HD15', + '1': 'SVIDEO', + '2': 'Composite video', + '3': 'Component video', + '4': 'DVI', + '5': 'HDMI', + '6': 'LVDS', + '8': 'D_JPN', + '9': 'SDI', + '10': 'DP', + '11': 'DP embedded', + '12': 'UDI', + '13': 'UDI embedded', + '14': 'SDTVDONGLE', + '15': 'MIRACAST', + '2147483648': 'INTERNAL' +}; + +function getVendorFromModel(model) { + const manufacturers = [ + { pattern: '^LG.+', manufacturer: 'LG' }, + { pattern: '^BENQ.+', manufacturer: 'BenQ' }, + { pattern: '^ASUS.+', manufacturer: 'Asus' }, + { pattern: '^DELL.+', manufacturer: 'Dell' }, + { pattern: '^SAMSUNG.+', manufacturer: 'Samsung' }, + { pattern: '^VIEWSON.+', manufacturer: 'ViewSonic' }, + { pattern: '^SONY.+', manufacturer: 'Sony' }, + { pattern: '^ACER.+', manufacturer: 'Acer' }, + { pattern: '^AOC.+', manufacturer: 'AOC Monitors' }, + { pattern: '^HP.+', manufacturer: 'HP' }, + { pattern: '^EIZO.?', manufacturer: 'Eizo' }, + { pattern: '^PHILIPS.?', manufacturer: 'Philips' }, + { pattern: '^IIYAMA.?', manufacturer: 'Iiyama' }, + { pattern: '^SHARP.?', manufacturer: 'Sharp' }, + { pattern: '^NEC.?', manufacturer: 'NEC' }, + { pattern: '^LENOVO.?', manufacturer: 'Lenovo' }, + { pattern: 'COMPAQ.?', manufacturer: 'Compaq' }, + { pattern: 'APPLE.?', manufacturer: 'Apple' }, + { pattern: 'INTEL.?', manufacturer: 'Intel' }, + { pattern: 'AMD.?', manufacturer: 'AMD' }, + { pattern: 'NVIDIA.?', manufacturer: 'NVDIA' }, + ]; + + let result = ''; + if (model) { + model = model.toUpperCase(); + manufacturers.forEach((manufacturer) => { + const re = RegExp(manufacturer.pattern); + if (re.test(model)) { result = manufacturer.manufacturer; } + }); + } + return result; +} + +function getVendorFromId(id) { + const vendors = { + '610': 'Apple', + '1e6d': 'LG', + '10ac': 'DELL', + '4dd9': 'Sony', + '38a3': 'NEC', + }; + return vendors[id] || ''; +} + +function vendorToId(str) { + let result = ''; + str = (str || '').toLowerCase(); + if (str.indexOf('apple') >= 0) { result = '0x05ac'; } + else if (str.indexOf('nvidia') >= 0) { result = '0x10de'; } + else if (str.indexOf('intel') >= 0) { result = '0x8086'; } + else if (str.indexOf('ati') >= 0 || str.indexOf('amd') >= 0) { result = '0x1002'; } + + return result; +} + +function getMetalVersion(id) { + const families = { + 'spdisplays_mtlgpufamilymac1': 'mac1', + 'spdisplays_mtlgpufamilymac2': 'mac2', + 'spdisplays_mtlgpufamilyapple1': 'apple1', + 'spdisplays_mtlgpufamilyapple2': 'apple2', + 'spdisplays_mtlgpufamilyapple3': 'apple3', + 'spdisplays_mtlgpufamilyapple4': 'apple4', + 'spdisplays_mtlgpufamilyapple5': 'apple5', + 'spdisplays_mtlgpufamilyapple6': 'apple6', + 'spdisplays_mtlgpufamilyapple7': 'apple7', + 'spdisplays_metalfeaturesetfamily11': 'family1_v1', + 'spdisplays_metalfeaturesetfamily12': 'family1_v2', + 'spdisplays_metalfeaturesetfamily13': 'family1_v3', + 'spdisplays_metalfeaturesetfamily14': 'family1_v4', + 'spdisplays_metalfeaturesetfamily21': 'family2_v1' + }; + return families[id] || ''; +} + +function graphics(callback) { + + function parseLinesDarwin(graphicsArr) { + const res = { + controllers: [], + displays: [] + }; + try { + graphicsArr.forEach(function (item) { + // controllers + const bus = ((item.sppci_bus || '').indexOf('builtin') > -1 ? 'Built-In' : ((item.sppci_bus || '').indexOf('pcie') > -1 ? 'PCIe' : '')); + const vram = (parseInt((item.spdisplays_vram || ''), 10) || 0) * (((item.spdisplays_vram || '').indexOf('GB') > -1) ? 1024 : 1); + const vramDyn = (parseInt((item.spdisplays_vram_shared || ''), 10) || 0) * (((item.spdisplays_vram_shared || '').indexOf('GB') > -1) ? 1024 : 1); + let metalVersion = getMetalVersion(item.spdisplays_metal || item.spdisplays_metalfamily || ''); + res.controllers.push({ + vendor: getVendorFromModel(item.spdisplays_vendor || '') || item.spdisplays_vendor || '', + model: item.sppci_model || '', + bus, + vramDynamic: bus === 'Built-In', + vram: vram || vramDyn || null, + deviceId: item['spdisplays_device-id'] || '', + vendorId: item['spdisplays_vendor-id'] || vendorToId((item['spdisplays_vendor'] || '') + (item.sppci_model || '')), + external: (item.sppci_device_type === 'spdisplays_egpu'), + cores: item['sppci_cores'] || null, + metalVersion + }); + + // displays + if (item.spdisplays_ndrvs && item.spdisplays_ndrvs.length) { + item.spdisplays_ndrvs.forEach(function (displayItem) { + const connectionType = displayItem['spdisplays_connection_type'] || ''; + const currentResolutionParts = (displayItem['_spdisplays_resolution'] || '').split('@'); + const currentResolution = currentResolutionParts[0].split('x'); + const pixelParts = (displayItem['_spdisplays_pixels'] || '').split('x'); + const pixelDepthString = displayItem['spdisplays_depth'] || ''; + const serial = displayItem['_spdisplays_display-serial-number'] || displayItem['_spdisplays_display-serial-number2'] || null; + res.displays.push({ + vendor: getVendorFromId(displayItem['_spdisplays_display-vendor-id'] || '') || getVendorFromModel(displayItem['_name'] || ''), + vendorId: displayItem['_spdisplays_display-vendor-id'] || '', + model: displayItem['_name'] || '', + productionYear: displayItem['_spdisplays_display-year'] || null, + serial: serial !== '0' ? serial : null, + displayId: displayItem['_spdisplays_displayID'] || null, + main: displayItem['spdisplays_main'] ? displayItem['spdisplays_main'] === 'spdisplays_yes' : false, + builtin: (displayItem['spdisplays_display_type'] || '').indexOf('built-in') > -1, + connection: ((connectionType.indexOf('_internal') > -1) ? 'Internal' : ((connectionType.indexOf('_displayport') > -1) ? 'Display Port' : ((connectionType.indexOf('_hdmi') > -1) ? 'HDMI' : null))), + sizeX: null, + sizeY: null, + pixelDepth: (pixelDepthString === 'CGSThirtyBitColor' ? 30 : (pixelDepthString === 'CGSThirtytwoBitColor' ? 32 : (pixelDepthString === 'CGSTwentyfourBitColor' ? 24 : ''))), + resolutionX: pixelParts.length > 1 ? parseInt(pixelParts[0], 10) : null, + resolutionY: pixelParts.length > 1 ? parseInt(pixelParts[1], 10) : null, + currentResX: currentResolution.length > 1 ? parseInt(currentResolution[0], 10) : null, + currentResY: currentResolution.length > 1 ? parseInt(currentResolution[1], 10) : null, + positionX: 0, + positionY: 0, + currentRefreshRate: currentResolutionParts.length > 1 ? parseInt(currentResolutionParts[1], 10) : null, + + }); + }); + } + }); + return res; + } catch (e) { + return res; + } + } + + function parseLinesLinuxControllers(lines) { + let controllers = []; + let currentController = { + vendor: '', + model: '', + bus: '', + busAddress: '', + vram: null, + vramDynamic: false, + pciID: '' + }; + let isGraphicsController = false; + // PCI bus IDs + let pciIDs = []; + try { + pciIDs = execSync('export LC_ALL=C; dmidecode -t 9 2>/dev/null; unset LC_ALL | grep "Bus Address: "').toString().split('\n'); + for (let i = 0; i < pciIDs.length; i++) { + pciIDs[i] = pciIDs[i].replace('Bus Address:', '').replace('0000:', '').trim(); + } + pciIDs = pciIDs.filter(function (el) { + return el != null && el; + }); + } catch (e) { + util.noop(); + } + for (let i = 0; i < lines.length; i++) { + if ('' !== lines[i].trim()) { + if (' ' !== lines[i][0] && '\t' !== lines[i][0]) { // first line of new entry + let isExternal = (pciIDs.indexOf(lines[i].split(' ')[0]) >= 0); + let vgapos = lines[i].toLowerCase().indexOf(' vga '); + let _3dcontrollerpos = lines[i].toLowerCase().indexOf('3d controller'); + if (vgapos !== -1 || _3dcontrollerpos !== -1) { // VGA + if (_3dcontrollerpos !== -1 && vgapos === -1) { + vgapos = _3dcontrollerpos; + } + if (currentController.vendor || currentController.model || currentController.bus || currentController.vram !== null || currentController.vramDynamic) { // already a controller found + controllers.push(currentController); + currentController = { + vendor: '', + model: '', + bus: '', + busAddress: '', + vram: null, + vramDynamic: false, + }; + } + + const pciIDCandidate = lines[i].split(' ')[0]; + if (/[\da-fA-F]{2}:[\da-fA-F]{2}\.[\da-fA-F]/.test(pciIDCandidate)) { + currentController.busAddress = pciIDCandidate; + } + isGraphicsController = true; + let endpos = lines[i].search(/\[[0-9a-f]{4}:[0-9a-f]{4}]|$/); + let parts = lines[i].substr(vgapos, endpos - vgapos).split(':'); + currentController.busAddress = lines[i].substr(0, vgapos).trim(); + if (parts.length > 1) { + parts[1] = parts[1].trim(); + if (parts[1].toLowerCase().indexOf('corporation') >= 0) { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf('corporation') + 11).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf('corporation') + 11, 200).trim().split('(')[0]; + currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard'; + currentController.vram = null; + currentController.vramDynamic = false; + } else if (parts[1].toLowerCase().indexOf(' inc.') >= 0) { + if ((parts[1].match(new RegExp(']', 'g')) || []).length > 1) { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim(); + } else { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' inc.') + 5).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' inc.') + 5, 200).trim().split('(')[0].trim(); + } + currentController.bus = (pciIDs.length > 0 && isExternal) ? 'PCIe' : 'Onboard'; + currentController.vram = null; + currentController.vramDynamic = false; + } else if (parts[1].toLowerCase().indexOf(' ltd.') >= 0) { + if ((parts[1].match(new RegExp(']', 'g')) || []).length > 1) { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(']') + 1).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(']') + 1, 200).trim().split('(')[0].trim(); + } else { + currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf(' ltd.') + 5).trim(); + currentController.model = parts[1].substr(parts[1].toLowerCase().indexOf(' ltd.') + 5, 200).trim().split('(')[0].trim(); + } + } + } + + } else { + isGraphicsController = false; + } + } + if (isGraphicsController) { // within VGA details + let parts = lines[i].split(':'); + if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('devicename') !== -1 && parts[1].toLowerCase().indexOf('onboard') !== -1) { currentController.bus = 'Onboard'; } + if (parts.length > 1 && parts[0].replace(/ +/g, '').toLowerCase().indexOf('region') !== -1 && parts[1].toLowerCase().indexOf('memory') !== -1) { + let memparts = parts[1].split('='); + if (memparts.length > 1) { + currentController.vram = parseInt(memparts[1]); + } + } + } + } + } + if (currentController.vendor || currentController.model || currentController.bus || currentController.busAddress || currentController.vram !== null || currentController.vramDynamic) { // already a controller found + controllers.push(currentController); + } + return (controllers); + } + + function parseLinesLinuxClinfo(controllers, lines) { + const fieldPattern = /\[([^\]]+)\]\s+(\w+)\s+(.*)/; + const devices = lines.reduce((devices, line) => { + const field = fieldPattern.exec(line.trim()); + if (field) { + if (!devices[field[1]]) { + devices[field[1]] = {}; + } + devices[field[1]][field[2]] = field[3]; + } + return devices; + }, {}); + for (let deviceId in devices) { + const device = devices[deviceId]; + if (device['CL_DEVICE_TYPE'] === 'CL_DEVICE_TYPE_GPU') { + let busAddress; + if (device['CL_DEVICE_TOPOLOGY_AMD']) { + const bdf = device['CL_DEVICE_TOPOLOGY_AMD'].match(/[a-zA-Z0-9]+:\d+\.\d+/); + if (bdf) { + busAddress = bdf[0]; + } + } else if (device['CL_DEVICE_PCI_BUS_ID_NV'] && device['CL_DEVICE_PCI_SLOT_ID_NV']) { + const bus = parseInt(device['CL_DEVICE_PCI_BUS_ID_NV']); + const slot = parseInt(device['CL_DEVICE_PCI_SLOT_ID_NV']); + if (!isNaN(bus) && !isNaN(slot)) { + const b = bus & 0xff; + const d = (slot >> 3) & 0xff; + const f = slot & 0x07; + busAddress = `${b.toString().padStart(2, '0')}:${d.toString().padStart(2, '0')}.${f}`; + } + } + if (busAddress) { + let controller = controllers.find(controller => controller.busAddress === busAddress); + if (!controller) { + controller = { + vendor: '', + model: '', + bus: '', + busAddress, + vram: null, + vramDynamic: false + }; + controllers.push(controller); + } + controller.vendor = device['CL_DEVICE_VENDOR']; + if (device['CL_DEVICE_BOARD_NAME_AMD']) { + controller.model = device['CL_DEVICE_BOARD_NAME_AMD']; + } else { + controller.model = device['CL_DEVICE_NAME']; + } + const memory = parseInt(device['CL_DEVICE_GLOBAL_MEM_SIZE']); + if (!isNaN(memory)) { + controller.vram = Math.round(memory / 1024 / 1024); + } + } + } + } + return controllers; + } + + function getNvidiaSmi() { + if (_nvidiaSmiPath) { + return _nvidiaSmiPath; + } + + if (_windows) { + try { + const basePath = util.WINDIR + '\\System32\\DriverStore\\FileRepository'; + // find all directories that have an nvidia-smi.exe file + const candidateDirs = fs.readdirSync(basePath).filter(dir => { + return fs.readdirSync([basePath, dir].join('/')).includes('nvidia-smi.exe'); + }); + // use the directory with the most recently created nvidia-smi.exe file + const targetDir = candidateDirs.reduce((prevDir, currentDir) => { + const previousNvidiaSmi = fs.statSync([basePath, prevDir, 'nvidia-smi.exe'].join('/')); + const currentNvidiaSmi = fs.statSync([basePath, currentDir, 'nvidia-smi.exe'].join('/')); + return (previousNvidiaSmi.ctimeMs > currentNvidiaSmi.ctimeMs) ? prevDir : currentDir; + }); + + if (targetDir) { + _nvidiaSmiPath = [basePath, targetDir, 'nvidia-smi.exe'].join('/'); + } + } catch (e) { + util.noop(); + } + } else if (_linux) { + _nvidiaSmiPath = 'nvidia-smi'; + } + return _nvidiaSmiPath; + } + + function nvidiaSmi(options) { + const nvidiaSmiExe = getNvidiaSmi(); + options = options || util.execOptsWin; + if (nvidiaSmiExe) { + 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'; + const cmd = nvidiaSmiExe + ' ' + nvidiaSmiOpts + (_linux ? ' 2>/dev/null' : ''); + try { + const res = execSync(cmd, options).toString(); + return res; + } catch (e) { + util.noop(); + } + } + return ''; + } + + function nvidiaDevices() { + + function safeParseNumber(value) { + if ([null, undefined].includes(value)) { + return value; + } + return parseFloat(value); + } + + const stdout = nvidiaSmi(); + if (!stdout) { + return []; + } + + const gpus = stdout.split('\n').filter(Boolean); + const results = gpus.map(gpu => { + const splittedData = gpu.split(', ').map(value => value.includes('N/A') ? undefined : value); + if (splittedData.length === 16) { + return { + driverVersion: splittedData[0], + subDeviceId: splittedData[1], + name: splittedData[2], + pciBus: splittedData[3], + fanSpeed: safeParseNumber(splittedData[4]), + memoryTotal: safeParseNumber(splittedData[5]), + memoryUsed: safeParseNumber(splittedData[6]), + memoryFree: safeParseNumber(splittedData[7]), + utilizationGpu: safeParseNumber(splittedData[8]), + utilizationMemory: safeParseNumber(splittedData[9]), + temperatureGpu: safeParseNumber(splittedData[10]), + temperatureMemory: safeParseNumber(splittedData[11]), + powerDraw: safeParseNumber(splittedData[12]), + powerLimit: safeParseNumber(splittedData[13]), + clockCore: safeParseNumber(splittedData[14]), + clockMemory: safeParseNumber(splittedData[15]), + }; + } + }); + + return results; + } + + function mergeControllerNvidia(controller, nvidia) { + if (nvidia.driverVersion) { controller.driverVersion = nvidia.driverVersion; } + if (nvidia.subDeviceId) { controller.subDeviceId = nvidia.subDeviceId; } + if (nvidia.name) { controller.name = nvidia.name; } + if (nvidia.pciBus) { controller.pciBus = nvidia.pciBus; } + if (nvidia.fanSpeed) { controller.fanSpeed = nvidia.fanSpeed; } + if (nvidia.memoryTotal) { + controller.memoryTotal = nvidia.memoryTotal; + controller.vram = nvidia.memoryTotal; + controller.vramDynamic = false; + } + if (nvidia.memoryUsed) { controller.memoryUsed = nvidia.memoryUsed; } + if (nvidia.memoryFree) { controller.memoryFree = nvidia.memoryFree; } + if (nvidia.utilizationGpu) { controller.utilizationGpu = nvidia.utilizationGpu; } + if (nvidia.utilizationMemory) { controller.utilizationMemory = nvidia.utilizationMemory; } + if (nvidia.temperatureGpu) { controller.temperatureGpu = nvidia.temperatureGpu; } + if (nvidia.temperatureMemory) { controller.temperatureMemory = nvidia.temperatureMemory; } + if (nvidia.powerDraw) { controller.powerDraw = nvidia.powerDraw; } + if (nvidia.powerLimit) { controller.powerLimit = nvidia.powerLimit; } + if (nvidia.clockCore) { controller.clockCore = nvidia.clockCore; } + if (nvidia.clockMemory) { controller.clockMemory = nvidia.clockMemory; } + return controller; + } + + function parseLinesLinuxEdid(edid) { + // parsen EDID + // --> model + // --> resolutionx + // --> resolutiony + // --> builtin = false + // --> pixeldepth (?) + // --> sizex + // --> sizey + let result = { + vendor: '', + model: '', + deviceName: '', + main: false, + builtin: false, + connection: '', + sizeX: null, + sizeY: null, + pixelDepth: null, + resolutionX: null, + resolutionY: null, + currentResX: null, + currentResY: null, + positionX: 0, + positionY: 0, + currentRefreshRate: null + }; + // find first "Detailed Timing Description" + let start = 108; + if (edid.substr(start, 6) === '000000') { + start += 36; + } + if (edid.substr(start, 6) === '000000') { + start += 36; + } + if (edid.substr(start, 6) === '000000') { + start += 36; + } + if (edid.substr(start, 6) === '000000') { + start += 36; + } + result.resolutionX = parseInt('0x0' + edid.substr(start + 8, 1) + edid.substr(start + 4, 2)); + result.resolutionY = parseInt('0x0' + edid.substr(start + 14, 1) + edid.substr(start + 10, 2)); + result.sizeX = parseInt('0x0' + edid.substr(start + 28, 1) + edid.substr(start + 24, 2)); + result.sizeY = parseInt('0x0' + edid.substr(start + 29, 1) + edid.substr(start + 26, 2)); + // monitor name + start = edid.indexOf('000000fc00'); // find first "Monitor Description Data" + if (start >= 0) { + let model_raw = edid.substr(start + 10, 26); + if (model_raw.indexOf('0a') !== -1) { + model_raw = model_raw.substr(0, model_raw.indexOf('0a')); + } + try { + if (model_raw.length > 2) { + result.model = model_raw.match(/.{1,2}/g).map(function (v) { + return String.fromCharCode(parseInt(v, 16)); + }).join(''); + } + } catch (e) { + util.noop(); + } + } else { + result.model = ''; + } + return result; + } + + function parseLinesLinuxDisplays(lines, depth) { + let displays = []; + let currentDisplay = { + vendor: '', + model: '', + deviceName: '', + main: false, + builtin: false, + connection: '', + sizeX: null, + sizeY: null, + pixelDepth: null, + resolutionX: null, + resolutionY: null, + currentResX: null, + currentResY: null, + positionX: 0, + positionY: 0, + currentRefreshRate: null + }; + let is_edid = false; + let is_current = false; + let edid_raw = ''; + let start = 0; + for (let i = 1; i < lines.length; i++) { // start with second line + if ('' !== lines[i].trim()) { + if (' ' !== lines[i][0] && '\t' !== lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') !== -1) { // first line of new entry + if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // push last display to array + displays.push(currentDisplay); + currentDisplay = { + vendor: '', + model: '', + main: false, + builtin: false, + connection: '', + sizeX: null, + sizeY: null, + pixelDepth: null, + resolutionX: null, + resolutionY: null, + currentResX: null, + currentResY: null, + positionX: 0, + positionY: 0, + currentRefreshRate: null + }; + } + let parts = lines[i].split(' '); + currentDisplay.connection = parts[0]; + currentDisplay.main = lines[i].toLowerCase().indexOf(' primary ') >= 0; + currentDisplay.builtin = (parts[0].toLowerCase().indexOf('edp') >= 0); + } + + // try to read EDID information + if (is_edid) { + if (lines[i].search(/\S|$/) > start) { + edid_raw += lines[i].toLowerCase().trim(); + } else { + // parsen EDID + let edid_decoded = parseLinesLinuxEdid(edid_raw); + currentDisplay.vendor = edid_decoded.vendor; + currentDisplay.model = edid_decoded.model; + currentDisplay.resolutionX = edid_decoded.resolutionX; + currentDisplay.resolutionY = edid_decoded.resolutionY; + currentDisplay.sizeX = edid_decoded.sizeX; + currentDisplay.sizeY = edid_decoded.sizeY; + currentDisplay.pixelDepth = depth; + is_edid = false; + } + } + if (lines[i].toLowerCase().indexOf('edid:') >= 0) { + is_edid = true; + start = lines[i].search(/\S|$/); + } + if (lines[i].toLowerCase().indexOf('*current') >= 0) { + const parts1 = lines[i].split('('); + if (parts1 && parts1.length > 1 && parts1[0].indexOf('x') >= 0) { + const resParts = parts1[0].trim().split('x'); + currentDisplay.currentResX = util.toInt(resParts[0]); + currentDisplay.currentResY = util.toInt(resParts[1]); + } + is_current = true; + } + if (is_current && lines[i].toLowerCase().indexOf('clock') >= 0 && lines[i].toLowerCase().indexOf('hz') >= 0 && lines[i].toLowerCase().indexOf('v: height') >= 0) { + const parts1 = lines[i].split('clock'); + if (parts1 && parts1.length > 1 && parts1[1].toLowerCase().indexOf('hz') >= 0) { + currentDisplay.currentRefreshRate = util.toInt(parts1[1]); + } + is_current = false; + } + } + } + + // pushen displays + if (currentDisplay.model || currentDisplay.main || currentDisplay.builtin || currentDisplay.connection || currentDisplay.sizeX !== null || currentDisplay.pixelDepth !== null || currentDisplay.resolutionX !== null) { // still information there + displays.push(currentDisplay); + } + return displays; + } + + // function starts here + return new Promise((resolve) => { + process.nextTick(() => { + let result = { + controllers: [], + displays: [] + }; + if (_darwin) { + let cmd = 'system_profiler -xml -detailLevel full SPDisplaysDataType'; + exec(cmd, function (error, stdout) { + if (!error) { + try { + let output = stdout.toString(); + result = parseLinesDarwin(util.plistParser(output)[0]._items); + } catch (e) { + util.noop(); + } + } + if (callback) { + callback(result); + } + resolve(result); + }); + } + if (_linux) { + // Raspberry: https://elinux.org/RPI_vcgencmd_usage + if (util.isRaspberry() && util.isRaspbian()) { + let cmd = 'fbset -s | grep \'mode "\'; vcgencmd get_mem gpu; tvservice -s; tvservice -n;'; + exec(cmd, function (error, stdout) { + let lines = stdout.toString().split('\n'); + if (lines.length > 3 && lines[0].indexOf('mode "') >= -1 && lines[2].indexOf('0x12000a') > -1) { + const parts = lines[0].replace('mode', '').replace(/"/g, '').trim().split('x'); + if (parts.length === 2) { + result.displays.push({ + vendor: '', + model: util.getValue(lines, 'device_name', '='), + main: true, + builtin: false, + connection: 'HDMI', + sizeX: null, + sizeY: null, + pixelDepth: null, + resolutionX: parseInt(parts[0], 10), + resolutionY: parseInt(parts[1], 10), + currentResX: null, + currentResY: null, + positionX: 0, + positionY: 0, + currentRefreshRate: null + }); + } + } + if (lines.length > 1 && stdout.toString().indexOf('gpu=') >= -1) { + result.controllers.push({ + vendor: 'Broadcom', + model: 'VideoCore IV', + bus: '', + vram: util.getValue(lines, 'gpu', '=').replace('M', ''), + vramDynamic: true + }); + } + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + let cmd = 'lspci -vvv 2>/dev/null'; + exec(cmd, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result.controllers = parseLinesLinuxControllers(lines); + const nvidiaData = nvidiaDevices(); + // needs to be rewritten ... using no spread operators + result.controllers = result.controllers.map((controller) => { // match by busAddress + return mergeControllerNvidia(controller, nvidiaData.find((contr) => contr.pciBus.toLowerCase().endsWith(controller.busAddress.toLowerCase())) || {}); + }); + } + let cmd = 'clinfo --raw'; + exec(cmd, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result.controllers = parseLinesLinuxClinfo(result.controllers, lines); + } + let cmd = 'xdpyinfo 2>/dev/null | grep \'depth of root window\' | awk \'{ print $5 }\''; + exec(cmd, function (error, stdout) { + let depth = 0; + if (!error) { + let lines = stdout.toString().split('\n'); + depth = parseInt(lines[0]) || 0; + } + let cmd = 'xrandr --verbose 2>/dev/null'; + exec(cmd, function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + result.displays = parseLinesLinuxDisplays(lines, depth); + } + if (callback) { + callback(result); + } + resolve(result); + }); + }); + }); + }); + } + } + if (_freebsd || _openbsd || _netbsd) { + if (callback) { callback(null); } + resolve(null); + } + if (_sunos) { + if (callback) { callback(null); } + resolve(null); + } + if (_windows) { + + // https://blogs.technet.microsoft.com/heyscriptingguy/2013/10/03/use-powershell-to-discover-multi-monitor-information/ + // https://devblogs.microsoft.com/scripting/use-powershell-to-discover-multi-monitor-information/ + try { + const workload = []; + workload.push(util.powerShell('Get-WmiObject 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-WmiObject 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 -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}')); + + 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/); + result.controllers = parseLinesWindowsControllers(csections, vsections); + result.controllers = result.controllers.map((controller) => { // match by subDeviceId + if (controller.vendor.toLowerCase() === 'nvidia') { + return mergeControllerNvidia(controller, nvidiaData.find(device => { + let windowsSubDeviceId = controller.subDeviceId.toLowerCase(); + const nvidiaSubDeviceIdParts = device.subDeviceId.split('x'); + let nvidiaSubDeviceId = nvidiaSubDeviceIdParts.length > 1 ? nvidiaSubDeviceIdParts[1].toLowerCase() : nvidiaSubDeviceIdParts[0].toLowerCase(); + const lengthDifference = Math.abs(windowsSubDeviceId.length - nvidiaSubDeviceId.length); + if (windowsSubDeviceId.length > nvidiaSubDeviceId.length) { + for (let i = 0; i < lengthDifference; i++) { + nvidiaSubDeviceId = '0' + nvidiaSubDeviceId; + } + } else if (windowsSubDeviceId.length < nvidiaSubDeviceId.length) { + for (let i = 0; i < lengthDifference; i++) { + windowsSubDeviceId = '0' + windowsSubDeviceId; + } + } + return windowsSubDeviceId === nvidiaSubDeviceId; + }) || {}); + } else { + return controller; + } + }); + + // 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; + } + } + if (_pixelDepth) { + result.displays[0].pixelDepth = _pixelDepth; + } + if (_refreshRate && !result.displays[0].refreshRate) { + result.displays[0].currentRefreshRate = _refreshRate; + } + } + + if (callback) { + callback(result); + } + resolve(result); + }) + .catch(() => { + if (callback) { + callback(result); + } + resolve(result); + }); + } catch (e) { + if (callback) { callback(result); } + resolve(result); + } + } + }); + }); + + function parseLinesWindowsControllers(sections, vections) { + const memorySizes = {}; + for (const i in vections) { + if ({}.hasOwnProperty.call(vections, i)) { + if (vections[i].trim() !== '') { + const lines = vections[i].trim().split('\n'); + 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); + if (matchingDeviceId) { + const quadWordmemorySize = parseInt(util.getValue(lines, 'HardwareInformation.qwMemorySize')); + if (!isNaN(quadWordmemorySize)) { + let deviceId = matchingDeviceId[1].toUpperCase() + '&' + matchingDeviceId[2].toUpperCase(); + if (matchingDeviceId[3]) { + deviceId += '&' + matchingDeviceId[3].toUpperCase(); + } + if (matchingDeviceId[4]) { + deviceId += '&' + matchingDeviceId[4].toUpperCase(); + } + memorySizes[deviceId] = quadWordmemorySize; + } + } + } + } + } + + let controllers = []; + for (let 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); + let subDeviceId = null; + let memorySize = null; + if (pnpDeviceId) { + subDeviceId = pnpDeviceId[3]; + if (subDeviceId) { + subDeviceId = subDeviceId.split('_')[1]; + } + + // Match PCI device identifier (there's an order of increasing generality): + // https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-pci-devices + + // PCI\VEN_v(4)&DEV_d(4)&SUBSYS_s(4)n(4)&REV_r(2) + if (memorySize == null && pnpDeviceId[3] && pnpDeviceId[4]) { + const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[3].toUpperCase() + '&' + pnpDeviceId[4].toUpperCase(); + if ({}.hasOwnProperty.call(memorySizes, deviceId)) { + memorySize = memorySizes[deviceId]; + } + } + + // PCI\VEN_v(4)&DEV_d(4)&SUBSYS_s(4)n(4) + if (memorySize == null && pnpDeviceId[3]) { + const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[3].toUpperCase(); + if ({}.hasOwnProperty.call(memorySizes, deviceId)) { + memorySize = memorySizes[deviceId]; + } + } + + // PCI\VEN_v(4)&DEV_d(4)&REV_r(2) + if (memorySize == null && pnpDeviceId[4]) { + const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase() + '&' + pnpDeviceId[4].toUpperCase(); + if ({}.hasOwnProperty.call(memorySizes, deviceId)) { + memorySize = memorySizes[deviceId]; + } + } + + // PCI\VEN_v(4)&DEV_d(4) + if (memorySize == null) { + const deviceId = pnpDeviceId[1].toUpperCase() + '&' + pnpDeviceId[2].toUpperCase(); + if ({}.hasOwnProperty.call(memorySizes, deviceId)) { + memorySize = memorySizes[deviceId]; + } + } + } + + controllers.push({ + vendor: util.getValue(lines, 'AdapterCompatibility', ':'), + model: util.getValue(lines, 'name', ':'), + bus: util.getValue(lines, 'PNPDeviceID', ':').startsWith('PCI') ? 'PCI' : '', + vram: (memorySize == null ? util.toInt(util.getValue(lines, 'AdapterRAM', ':')) : memorySize) / 1024 / 1024, + vramDynamic: (util.getValue(lines, 'VideoMemoryType', ':') === '2'), + subDeviceId + }); + _resolutionX = util.toInt(util.getValue(lines, 'CurrentHorizontalResolution', ':')) || _resolutionX; + _resolutionY = util.toInt(util.getValue(lines, 'CurrentVerticalResolution', ':')) || _resolutionY; + _refreshRate = util.toInt(util.getValue(lines, 'CurrentRefreshRate', ':')) || _refreshRate; + _pixelDepth = util.toInt(util.getValue(lines, 'CurrentBitsPerPixel', ':')) || _pixelDepth; + } + } + } + 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('}', '').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;