Add new VideoConference module for muting mic/cam (#11798)

* add new VideoConference module for muting mic/cam

Co-authored-by: PrzemyslawTusinski <61138537+PrzemyslawTusinski@users.noreply.github.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
Andrey Nekrasov
2021-06-29 13:06:12 +03:00
committed by GitHub
parent e450669663
commit 4e23832d52
116 changed files with 7425 additions and 81 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-Disabled">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-Disabled" clip-path="url(#clip-Off-Disabled)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#585858" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera not in use</tspan></text>
<path id="Camera_disabled_icon" data-name="Camera disabled icon" d="M12.5,509.031a3.408,3.408,0,0,1,1.359.272,3.634,3.634,0,0,1,1.117.745,3.348,3.348,0,0,1,.75,1.1A3.555,3.555,0,0,1,16,512.508a3.35,3.35,0,0,1-.273,1.351,3.608,3.608,0,0,1-.75,1.11,3.371,3.371,0,0,1-1.109.745,3.617,3.617,0,0,1-1.367.272,3.407,3.407,0,0,1-1.359-.272,3.634,3.634,0,0,1-1.117-.745,3.347,3.347,0,0,1-.75-1.1A3.555,3.555,0,0,1,9,512.508a3.351,3.351,0,0,1,.273-1.351,3.608,3.608,0,0,1,.75-1.11,3.371,3.371,0,0,1,1.109-.745A3.616,3.616,0,0,1,12.5,509.031ZM10,512.508a2.393,2.393,0,0,0,.2.963,2.585,2.585,0,0,0,.531.792,2.423,2.423,0,0,0,.8.536,2.491,2.491,0,0,0,.977.194,2.591,2.591,0,0,0,.719-.1,2.241,2.241,0,0,0,.656-.31l-3.461-3.439a2.334,2.334,0,0,0-.3.652A2.76,2.76,0,0,0,10,512.508Zm4.586,1.366a2.333,2.333,0,0,0,.3-.652,2.759,2.759,0,0,0,.109-.714,2.393,2.393,0,0,0-.2-.963,2.428,2.428,0,0,0-.539-.784,2.725,2.725,0,0,0-.8-.536,2.353,2.353,0,0,0-.969-.2,2.593,2.593,0,0,0-.719.1,2.245,2.245,0,0,0-.656.311ZM16,504v5.326a4.453,4.453,0,0,0-1-.823v-2.9l-3,1.5v.714a4.157,4.157,0,0,0-.508.078,4.1,4.1,0,0,0-.492.14v-2.981H1v5.962H8a4.038,4.038,0,0,0-.219.994H0v-7.95H12v1.925Z" transform="translate(171 -486)" fill="#585858"/>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-Disabled_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-Disabled_Light" data-name="Off-Disabled Light" clip-path="url(#clip-Off-Disabled_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#c1c1c1" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera not in use</tspan></text>
<path id="Camera_disabled_icon" data-name="Camera disabled icon" d="M12.5,509.031a3.408,3.408,0,0,1,1.359.272,3.634,3.634,0,0,1,1.117.745,3.348,3.348,0,0,1,.75,1.1A3.555,3.555,0,0,1,16,512.508a3.35,3.35,0,0,1-.273,1.351,3.608,3.608,0,0,1-.75,1.11,3.371,3.371,0,0,1-1.109.745,3.617,3.617,0,0,1-1.367.272,3.407,3.407,0,0,1-1.359-.272,3.634,3.634,0,0,1-1.117-.745,3.347,3.347,0,0,1-.75-1.1A3.555,3.555,0,0,1,9,512.508a3.351,3.351,0,0,1,.273-1.351,3.608,3.608,0,0,1,.75-1.11,3.371,3.371,0,0,1,1.109-.745A3.616,3.616,0,0,1,12.5,509.031ZM10,512.508a2.393,2.393,0,0,0,.2.963,2.585,2.585,0,0,0,.531.792,2.423,2.423,0,0,0,.8.536,2.491,2.491,0,0,0,.977.194,2.591,2.591,0,0,0,.719-.1,2.241,2.241,0,0,0,.656-.31l-3.461-3.439a2.334,2.334,0,0,0-.3.652A2.76,2.76,0,0,0,10,512.508Zm4.586,1.366a2.333,2.333,0,0,0,.3-.652,2.759,2.759,0,0,0,.109-.714,2.393,2.393,0,0,0-.2-.963,2.428,2.428,0,0,0-.539-.784,2.725,2.725,0,0,0-.8-.536,2.353,2.353,0,0,0-.969-.2,2.593,2.593,0,0,0-.719.1,2.245,2.245,0,0,0-.656.311ZM16,504v5.326a4.453,4.453,0,0,0-1-.823v-2.9l-3,1.5v.714a4.157,4.157,0,0,0-.508.078,4.1,4.1,0,0,0-.492.14v-2.981H1v5.962H8a4.038,4.038,0,0,0-.219.994H0v-7.95H12v1.925Z" transform="translate(171 -486)" fill="#c1c1c1"/>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-Off_Dark">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-Off_Dark" data-name="Off-Off Dark" clip-path="url(#clip-Off-Off_Dark)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera off</tspan></text>
<path id="Camera_off_icon" data-name="Camera off icon" d="M12,24.9l4-2.038v8.279l-1.953-1q-.141-.072-.258-.119t-.242-.1a1.825,1.825,0,0,1-.227-.119l-.25-.159a1.726,1.726,0,0,1-.2-.159,1.556,1.556,0,0,1-.156-.167q-.07-.088-.148-.175l-.18-.2L11,27.533v-3.59H7.477l-1-1.019H12Zm3,4.593V24.508l-3,1.536v1.91ZM.148,19.716.852,19l15,15.284-.7.716-3.859-3.924H0V22.924H3.289ZM1,30.057h9.289l-6-6.113H1Z" transform="translate(171 -5)" fill="#fff"/>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-Off_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-Off_Light" data-name="Off-Off Light" clip-path="url(#clip-Off-Off_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera off</tspan></text>
<path id="Camera_off_icon" data-name="Camera off icon" d="M12,24.9l4-2.038v8.279l-1.953-1q-.141-.072-.258-.119t-.242-.1a1.825,1.825,0,0,1-.227-.119l-.25-.159a1.726,1.726,0,0,1-.2-.159,1.556,1.556,0,0,1-.156-.167q-.07-.088-.148-.175l-.18-.2L11,27.533v-3.59H7.477l-1-1.019H12Zm3,4.593V24.508l-3,1.536v1.91ZM.148,19.716.852,19l15,15.284-.7.716-3.859-3.924H0V22.924H3.289ZM1,30.057h9.289l-6-6.113H1Z" transform="translate(171 -5)"/>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-On_Dark">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-On_Dark" data-name="Off-On Dark" clip-path="url(#clip-Off-On_Dark)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera on</tspan></text>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
<path id="Camera_on_icon" data-name="Camera on icon" d="M16,512.359,12,510.3V512.3H0v-8.231H12v1.993L16,504Zm-5-7.266H1v6.173H11Zm4,.571-3,1.551v1.929l3,1.551Z" transform="translate(171 -486)" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-On_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-On_Light" data-name="Off-On Light" clip-path="url(#clip-Off-On_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera on</tspan></text>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
<path id="Camera_on_icon" data-name="Camera on icon" d="M16,512.359,12,510.3V512.3H0v-8.231H12v1.993L16,504Zm-5-7.266H1v6.173H11Zm4,.571-3,1.551v1.929l3,1.551Z" transform="translate(171 -486)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-Disabled">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-Disabled" clip-path="url(#clip-On-Disabled)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#1f1f1f"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#585858" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera not in use</tspan></text>
<path id="Camer_disabled_icon" data-name="Camer disabled icon" d="M12.5,509.031a3.408,3.408,0,0,1,1.359.272,3.634,3.634,0,0,1,1.117.745,3.348,3.348,0,0,1,.75,1.1A3.555,3.555,0,0,1,16,512.508a3.35,3.35,0,0,1-.273,1.351,3.608,3.608,0,0,1-.75,1.11,3.371,3.371,0,0,1-1.109.745,3.617,3.617,0,0,1-1.367.272,3.407,3.407,0,0,1-1.359-.272,3.634,3.634,0,0,1-1.117-.745,3.347,3.347,0,0,1-.75-1.1A3.555,3.555,0,0,1,9,512.508a3.351,3.351,0,0,1,.273-1.351,3.608,3.608,0,0,1,.75-1.11,3.371,3.371,0,0,1,1.109-.745A3.616,3.616,0,0,1,12.5,509.031ZM10,512.508a2.393,2.393,0,0,0,.2.963,2.585,2.585,0,0,0,.531.792,2.423,2.423,0,0,0,.8.536,2.491,2.491,0,0,0,.977.194,2.591,2.591,0,0,0,.719-.1,2.241,2.241,0,0,0,.656-.31l-3.461-3.439a2.334,2.334,0,0,0-.3.652A2.76,2.76,0,0,0,10,512.508Zm4.586,1.366a2.333,2.333,0,0,0,.3-.652,2.759,2.759,0,0,0,.109-.714,2.393,2.393,0,0,0-.2-.963,2.428,2.428,0,0,0-.539-.784,2.725,2.725,0,0,0-.8-.536,2.353,2.353,0,0,0-.969-.2,2.593,2.593,0,0,0-.719.1,2.245,2.245,0,0,0-.656.311ZM16,504v5.326a4.453,4.453,0,0,0-1-.823v-2.9l-3,1.5v.714a4.157,4.157,0,0,0-.508.078,4.1,4.1,0,0,0-.492.14v-2.981H1v5.962H8a4.038,4.038,0,0,0-.219.994H0v-7.95H12v1.925Z" transform="translate(171 -486)" fill="#585858"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-Disabled_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-Disabled_Light" data-name="On-Disabled Light" clip-path="url(#clip-On-Disabled_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#c1c1c1" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera not in use</tspan></text>
<path id="Camer_disabled_icon" data-name="Camer disabled icon" d="M12.5,509.031a3.408,3.408,0,0,1,1.359.272,3.634,3.634,0,0,1,1.117.745,3.348,3.348,0,0,1,.75,1.1A3.555,3.555,0,0,1,16,512.508a3.35,3.35,0,0,1-.273,1.351,3.608,3.608,0,0,1-.75,1.11,3.371,3.371,0,0,1-1.109.745,3.617,3.617,0,0,1-1.367.272,3.407,3.407,0,0,1-1.359-.272,3.634,3.634,0,0,1-1.117-.745,3.347,3.347,0,0,1-.75-1.1A3.555,3.555,0,0,1,9,512.508a3.351,3.351,0,0,1,.273-1.351,3.608,3.608,0,0,1,.75-1.11,3.371,3.371,0,0,1,1.109-.745A3.616,3.616,0,0,1,12.5,509.031ZM10,512.508a2.393,2.393,0,0,0,.2.963,2.585,2.585,0,0,0,.531.792,2.423,2.423,0,0,0,.8.536,2.491,2.491,0,0,0,.977.194,2.591,2.591,0,0,0,.719-.1,2.241,2.241,0,0,0,.656-.31l-3.461-3.439a2.334,2.334,0,0,0-.3.652A2.76,2.76,0,0,0,10,512.508Zm4.586,1.366a2.333,2.333,0,0,0,.3-.652,2.759,2.759,0,0,0,.109-.714,2.393,2.393,0,0,0-.2-.963,2.428,2.428,0,0,0-.539-.784,2.725,2.725,0,0,0-.8-.536,2.353,2.353,0,0,0-.969-.2,2.593,2.593,0,0,0-.719.1,2.245,2.245,0,0,0-.656.311ZM16,504v5.326a4.453,4.453,0,0,0-1-.823v-2.9l-3,1.5v.714a4.157,4.157,0,0,0-.508.078,4.1,4.1,0,0,0-.492.14v-2.981H1v5.962H8a4.038,4.038,0,0,0-.219.994H0v-7.95H12v1.925Z" transform="translate(171 -486)" fill="#c1c1c1"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-Off_Dark">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-Off_Dark" data-name="On-Off Dark" clip-path="url(#clip-On-Off_Dark)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera off</tspan></text>
<path id="Camera_off_icon" data-name="Camera off icon" d="M12,24.9l4-2.038v8.279l-1.953-1q-.141-.072-.258-.119t-.242-.1a1.825,1.825,0,0,1-.227-.119l-.25-.159a1.726,1.726,0,0,1-.2-.159,1.556,1.556,0,0,1-.156-.167q-.07-.088-.148-.175l-.18-.2L11,27.533v-3.59H7.477l-1-1.019H12Zm3,4.593V24.508l-3,1.536v1.91ZM.148,19.716.852,19l15,15.284-.7.716-3.859-3.924H0V22.924H3.289ZM1,30.057h9.289l-6-6.113H1Z" transform="translate(171 -5)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-Off_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-Off_Light" data-name="On-Off Light" clip-path="url(#clip-On-Off_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera off</tspan></text>
<path id="Camera_off_icon" data-name="Camera off icon" d="M12,24.9l4-2.038v8.279l-1.953-1q-.141-.072-.258-.119t-.242-.1a1.825,1.825,0,0,1-.227-.119l-.25-.159a1.726,1.726,0,0,1-.2-.159,1.556,1.556,0,0,1-.156-.167q-.07-.088-.148-.175l-.18-.2L11,27.533v-3.59H7.477l-1-1.019H12Zm3,4.593V24.508l-3,1.536v1.91ZM.148,19.716.852,19l15,15.284-.7.716-3.859-3.924H0V22.924H3.289ZM1,30.057h9.289l-6-6.113H1Z" transform="translate(171 -5)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-On_Dark">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-On_Dark" data-name="On-On Dark" clip-path="url(#clip-On-On_Dark)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera on</tspan></text>
<path id="Camera_on_icon" data-name="Camera on icon" d="M16,512.359,12,510.3V512.3H0v-8.231H12v1.993L16,504Zm-5-7.266H1v6.173H11Zm4,.571-3,1.551v1.929l3,1.551Z" transform="translate(171 -486)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-On_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-On_Light" data-name="On-On Light" clip-path="url(#clip-On-On_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera on</tspan></text>
<path id="Camera_on_icon" data-name="Camera on icon" d="M16,512.359,12,510.3V512.3H0v-8.231H12v1.993L16,504Zm-5-7.266H1v6.173H11Zm4,.571-3,1.551v1.929l3,1.551Z" transform="translate(171 -486)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,14 @@
# Video Conference Mute
# Introduction
The Video Conference Mute module allows muting microphone and/or web camera video stream during video calls or other activity.
# Usage
If you'd like to mute your web camera, please select "PowerToys VideoConference Mute" device in your web camera-using app, then restart it.
During a video call, you can use default shortcuts to mute microphone, web camera or both. You'll see a toolbar indicating corresponding mute statuses.
# Options
You can tweak the toolbar position on the screen as well as set web camera overlay image during muting.
# Backlog

View File

@@ -0,0 +1,335 @@
#include "pch.h"
#include "Toolbar.h"
#include <windowsx.h>
#include <common/Themes/windows_colors.h>
#include "Logging.h"
#include "VideoConferenceModule.h"
Toolbar* toolbar = nullptr;
const int REFRESH_RATE = 100;
const int OVERLAY_SHOW_TIME = 500;
const int BORDER_OFFSET = 12;
Toolbar::Toolbar()
{
toolbar = this;
darkImages.camOnMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-On Dark.png");
darkImages.camOffMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-Off Dark.png");
darkImages.camOnMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-On Dark.png");
darkImages.camOffMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-Off Dark.png");
darkImages.camUnusedMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-NotInUse Dark.png");
darkImages.camUnusedMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-NotInUse Dark.png");
lightImages.camOnMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-On Light.png");
lightImages.camOffMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-Off Light.png");
lightImages.camOnMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-On Light.png");
lightImages.camOffMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-Off Light.png");
lightImages.camUnusedMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-NotInUse Light.png");
lightImages.camUnusedMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-NotInUse Light.png");
}
void Toolbar::scheduleModuleSettingsUpdate()
{
moduleSettingsUpdateScheduled = true;
}
void Toolbar::scheduleGeneralSettingsUpdate()
{
generalSettingsUpdateScheduled = true;
}
LRESULT Toolbar::WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_DESTROY:
return 0;
case WM_LBUTTONDOWN:
{
int x = GET_X_LPARAM(lparam);
int y = GET_Y_LPARAM(lparam);
if (x < 322 / 2)
{
VideoConferenceModule::reverseMicrophoneMute();
}
else
{
VideoConferenceModule::reverseVirtualCameraMuteState();
}
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
case WM_CREATE:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(hwnd, &ps);
Gdiplus::Graphics graphic(hdc);
ToolbarImages* themeImages = &toolbar->darkImages;
if (toolbar->theme == L"light" || (toolbar->theme == L"system" && !WindowsColors::is_dark_mode()))
{
themeImages = &toolbar->lightImages;
}
else
{
themeImages = &toolbar->darkImages;
}
Gdiplus::Image* toolbarImage = nullptr;
if (!toolbar->cameraInUse)
{
if (toolbar->microphoneMuted)
{
toolbarImage = themeImages->camUnusedMicOff;
}
else
{
toolbarImage = themeImages->camUnusedMicOn;
}
}
else if (toolbar->microphoneMuted)
{
if (toolbar->cameraMuted)
{
toolbarImage = themeImages->camOffMicOff;
}
else
{
toolbarImage = themeImages->camOnMicOff;
}
}
else
{
if (toolbar->cameraMuted)
{
toolbarImage = themeImages->camOffMicOn;
}
else
{
toolbarImage = themeImages->camOnMicOn;
}
}
graphic.DrawImage(toolbarImage, 0, 0, toolbarImage->GetWidth(), toolbarImage->GetHeight());
EndPaint(hwnd, &ps);
break;
}
case WM_TIMER:
{
if (toolbar->generalSettingsUpdateScheduled)
{
instance->onGeneralSettingsChanged();
toolbar->generalSettingsUpdateScheduled = false;
}
if (toolbar->moduleSettingsUpdateScheduled)
{
instance->onModuleSettingsChanged();
toolbar->moduleSettingsUpdateScheduled = false;
}
toolbar->cameraInUse = VideoConferenceModule::getVirtualCameraInUse();
InvalidateRect(hwnd, NULL, NULL);
using namespace std::chrono;
const auto nowMillis = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
const bool showOverlayTimeout = nowMillis - toolbar->lastTimeCamOrMicMuteStateChanged > OVERLAY_SHOW_TIME;
static bool previousShow = false;
bool show = false;
if (toolbar->cameraInUse)
{
show = toolbar->HideToolbarWhenUnmuted ? toolbar->microphoneMuted || toolbar->cameraMuted : true;
}
else if (toolbar->previouscameraInUse)
{
VideoConferenceModule::unmuteAll();
}
else
{
show = toolbar->microphoneMuted;
}
show = show || !showOverlayTimeout;
if (show)
{
ShowWindow(hwnd, SW_SHOW);
}
else
{
ShowWindow(hwnd, SW_HIDE);
}
if (previousShow != show)
{
previousShow = show;
LOG(show ? "Toolbar visibility changed to shown" : "Toolbar visibility changed to hidden");
}
KillTimer(hwnd, toolbar->nTimerId);
toolbar->previouscameraInUse = toolbar->cameraInUse;
break;
}
default:
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
toolbar->nTimerId = SetTimer(hwnd, 101, REFRESH_RATE, nullptr);
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
void Toolbar::show(std::wstring position, std::wstring monitorString)
{
for (auto& hwnd : hwnds)
{
PostMessageW(hwnd, WM_CLOSE, 0, 0);
}
hwnds.clear();
int overlayWidth = darkImages.camOffMicOff->GetWidth();
int overlayHeight = darkImages.camOffMicOff->GetHeight();
// Register the window class
LPCWSTR CLASS_NAME = L"MuteNotificationWindowClass";
WNDCLASS wc{};
wc.hInstance = GetModuleHandleW(nullptr);
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpfnWndProc = WindowProcessMessages;
RegisterClassW(&wc);
// Create the window
DWORD dwExtStyle = 0;
DWORD dwStyle = WS_POPUPWINDOW;
std::vector<MonitorInfo> monitorInfos;
if (monitorString == L"All monitors")
{
monitorInfos = MonitorInfo::GetMonitors(false);
}
else //"Main monitor" or non-present
{
monitorInfos.push_back(MonitorInfo::GetPrimaryMonitor());
}
for (auto& monitorInfo : monitorInfos)
{
int positionX = 0;
int positionY = 0;
if (position == L"Top left corner")
{
positionX = monitorInfo.left() + BORDER_OFFSET;
positionY = monitorInfo.top() + BORDER_OFFSET;
}
else if (position == L"Top center")
{
positionX = monitorInfo.middle().x - overlayWidth / 2;
positionY = monitorInfo.top() + BORDER_OFFSET;
}
else if (position == L"Bottom left corner")
{
positionX = monitorInfo.left() + BORDER_OFFSET;
positionY = monitorInfo.bottom() - overlayHeight - BORDER_OFFSET;
}
else if (position == L"Bottom center")
{
positionX = monitorInfo.middle().x - overlayWidth / 2;
positionY = monitorInfo.bottom() - overlayHeight - BORDER_OFFSET;
}
else if (position == L"Bottom right corner")
{
positionX = monitorInfo.right() - overlayWidth - BORDER_OFFSET;
positionY = monitorInfo.bottom() - overlayHeight - BORDER_OFFSET;
}
else //"Top right corner" or non-present
{
positionX = monitorInfo.right() - overlayWidth - BORDER_OFFSET;
positionY = monitorInfo.top() + BORDER_OFFSET;
}
HWND hwnd;
hwnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
CLASS_NAME,
CLASS_NAME,
WS_POPUP,
positionX,
positionY,
overlayWidth,
overlayHeight,
nullptr,
nullptr,
GetModuleHandleW(nullptr),
nullptr);
auto transparrentColorKey = RGB(0, 0, 255);
HBRUSH brush = CreateSolidBrush(transparrentColorKey);
SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
SetLayeredWindowAttributes(hwnd, transparrentColorKey, 0, LWA_COLORKEY);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
hwnds.push_back(hwnd);
}
}
void Toolbar::hide()
{
for (auto& hwnd : hwnds)
{
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
hwnds.clear();
}
bool Toolbar::getCameraMute()
{
return cameraMuted;
}
void Toolbar::setCameraMute(bool mute)
{
if (mute != cameraMuted)
{
lastTimeCamOrMicMuteStateChanged = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
cameraMuted = mute;
}
bool Toolbar::getMicrophoneMute()
{
return microphoneMuted;
}
void Toolbar::setMicrophoneMute(bool mute)
{
if (mute != microphoneMuted)
{
lastTimeCamOrMicMuteStateChanged = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
microphoneMuted = mute;
}
void Toolbar::setHideToolbarWhenUnmuted(bool hide)
{
HideToolbarWhenUnmuted = hide;
}
void Toolbar::setTheme(std::wstring theme)
{
Toolbar::theme = theme;
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include <Windows.h>
#include <gdiplus.h>
#include <atomic>
#include <common/Display/monitors.h>
struct ToolbarImages
{
Gdiplus::Image* camOnMicOn = nullptr;
Gdiplus::Image* camOffMicOn = nullptr;
Gdiplus::Image* camOnMicOff = nullptr;
Gdiplus::Image* camOffMicOff = nullptr;
Gdiplus::Image* camUnusedMicOn = nullptr;
Gdiplus::Image* camUnusedMicOff = nullptr;
};
class Toolbar
{
public:
Toolbar();
void scheduleModuleSettingsUpdate();
void scheduleGeneralSettingsUpdate();
void show(std::wstring position, std::wstring monitorString);
void hide();
bool getCameraMute();
void setCameraMute(bool mute);
bool getMicrophoneMute();
void setMicrophoneMute(bool mute);
void setTheme(std::wstring theme);
void setHideToolbarWhenUnmuted(bool hide);
private:
static LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
// Window callback can't be non-static so this members can't as well
std::vector<HWND> hwnds;
ToolbarImages darkImages;
ToolbarImages lightImages;
bool cameraMuted = false;
bool cameraInUse = false;
bool previouscameraInUse = false;
bool microphoneMuted = false;
std::wstring theme = L"system";
bool HideToolbarWhenUnmuted = true;
uint64_t lastTimeCamOrMicMuteStateChanged;
std::atomic_bool moduleSettingsUpdateScheduled = false;
std::atomic_bool generalSettingsUpdateScheduled = false;
UINT_PTR nTimerId;
};

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="keyboard_state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="shortcut_guide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="target_state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="overlay_window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="keyboard_state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shortcut_guide.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="target_state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="overlay_window.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{2c7c97f7-0d87-4230-a4b2-baf2cfc35d58}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{aa4b6713-589d-42ef-804d-3a045833f83f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="shortcut_guide.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>overlaywindow</RootNamespace>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<ProjectName>VideoConferenceModule</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionSettings">
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;OVERLAYWINDOW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>..\..\..\;..\..\;..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>mfplat.lib;mf.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>xcopy /y /I "$(ProjectDir)Icons\*" "$(OutDir)Icons"
xcopy /y /I "$(ProjectDir)black.bmp*" "$(OutDir)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;OVERLAYWINDOW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>..\..\..\common\inc;..\..\..\common\Telemetry;..\..\..\;..\..\;..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>mfplat.lib;mf.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;dxguid.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>xcopy /y /I "$(ProjectDir)Icons\*" "$(OutDir)Icons"
xcopy /y /I "$(ProjectDir)black.bmp*" "$(OutDir)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="framework.h" />
<ClInclude Include="Toolbar.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="VideoConferenceModule.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="Toolbar.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="VideoConferenceModule.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VideoConferenceShared\VideoConferenceShared.vcxproj">
<Project>{459e0768-7ebd-4c41-bba1-6db3b3815e0a}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Icons\Off-NotInUse Dark.svg" />
<None Include="Icons\Off-NotInUse Light.svg" />
<None Include="Icons\Off-Off Dark.svg" />
<None Include="Icons\Off-Off Light.svg" />
<None Include="Icons\Off-On Dark.svg" />
<None Include="Icons\Off-On Light.svg" />
<None Include="Icons\On-NotInUse Dark.svg" />
<None Include="Icons\On-NotInUse Light.svg" />
<None Include="Icons\On-Off Dark.svg" />
<None Include="Icons\On-Off Light.svg" />
<None Include="Icons\On-On Dark.svg" />
<None Include="Icons\On-On Light.svg" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Image Include="black.bmp" />
<Image Include="Icons\Off-NotInUse Dark.png" />
<Image Include="Icons\Off-NotInUse Light.png" />
<Image Include="Icons\Off-Off Dark.png" />
<Image Include="Icons\Off-Off Light.png" />
<Image Include="Icons\Off-On Dark.png" />
<Image Include="Icons\Off-On Light.png" />
<Image Include="Icons\On-NotInUse Dark.png" />
<Image Include="Icons\On-NotInUse Light.png" />
<Image Include="Icons\On-Off Dark.png" />
<Image Include="Icons\On-Off Light.png" />
<Image Include="Icons\On-On Dark.png" />
<Image Include="Icons\On-On Light.png" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
<ItemGroup>
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
</ProjectReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="Toolbar.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="VideoConferenceModule.cpp" />
<ClCompile Include="trace.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h" />
<ClInclude Include="Toolbar.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="VideoConferenceModule.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<Image Include="black.bmp" />
<Image Include="Icons\Off-NotInUse Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-NotInUse Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-Off Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-Off Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-On Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-On Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-NotInUse Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-NotInUse Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-Off Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-Off Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-On Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-On Light.png">
<Filter>Icons</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Icons\Off-NotInUse Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-NotInUse Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-Off Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-Off Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-On Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-On Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-NotInUse Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-NotInUse Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-Off Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-Off Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-On Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-On Light.svg">
<Filter>Icons</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Filter Include="Icons">
<UniqueIdentifier>{735361e2-82fa-4034-b9c9-cd6aa099eaa5}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,568 @@
#include "pch.h"
#include "VideoConferenceModule.h"
#include <WinUser.h>
#include <gdiplus.h>
#include <shellapi.h>
#include <filesystem>
#include <common/debug_control.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/elevation.h>
#include <common/utils/process_path.h>
#include <CameraStateUpdateChannels.h>
#include "logging.h"
#include "trace.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
VideoConferenceModule* instance = nullptr;
VideoConferenceSettings VideoConferenceModule::settings;
Toolbar VideoConferenceModule::toolbar;
HHOOK VideoConferenceModule::hook_handle;
IAudioEndpointVolume* endpointVolume = NULL;
bool VideoConferenceModule::isKeyPressed(unsigned int keyCode)
{
return (GetKeyState(keyCode) & 0x8000);
}
namespace fs = std::filesystem;
bool VideoConferenceModule::isHotkeyPressed(DWORD code, PowerToysSettings::HotkeyObject& hotkey)
{
return code == hotkey.get_code() &&
isKeyPressed(VK_SHIFT) == hotkey.shift_pressed() &&
isKeyPressed(VK_CONTROL) == hotkey.ctrl_pressed() &&
isKeyPressed(VK_LWIN) == hotkey.win_pressed() &&
(isKeyPressed(VK_LMENU)) == hotkey.alt_pressed();
}
void VideoConferenceModule::reverseMicrophoneMute()
{
bool muted = false;
for (auto& controlledMic : instance->_controlledMicrophones)
{
const bool was_muted = controlledMic.muted();
controlledMic.toggle_muted();
muted = muted || !was_muted;
}
if (muted)
{
Trace::MicrophoneMuted();
}
toolbar.setMicrophoneMute(muted);
}
bool VideoConferenceModule::getMicrophoneMuteState()
{
return instance->_microphoneTrackedInUI ? instance->_microphoneTrackedInUI->muted() : false;
}
void VideoConferenceModule::reverseVirtualCameraMuteState()
{
bool muted = false;
if (!instance->_settingsUpdateChannel.has_value())
{
return;
}
instance->_settingsUpdateChannel->access([&muted](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
settings->useOverlayImage = !settings->useOverlayImage;
muted = settings->useOverlayImage;
});
if (muted)
{
Trace::CameraMuted();
}
toolbar.setCameraMute(muted);
}
bool VideoConferenceModule::getVirtualCameraMuteState()
{
bool disabled = false;
if (!instance->_settingsUpdateChannel.has_value())
{
return disabled;
}
instance->_settingsUpdateChannel->access([&disabled](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
disabled = settings->useOverlayImage;
});
return disabled;
}
bool VideoConferenceModule::getVirtualCameraInUse()
{
if (!instance->_settingsUpdateChannel.has_value())
{
return false;
}
bool inUse = false;
instance->_settingsUpdateChannel->access([&inUse](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
inUse = settings->cameraInUse;
});
return inUse;
}
LRESULT CALLBACK VideoConferenceModule::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
switch (wParam)
{
case WM_KEYDOWN:
KBDLLHOOKSTRUCT* kbd = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
if (isHotkeyPressed(kbd->vkCode, settings.cameraAndMicrophoneMuteHotkey))
{
const bool cameraInUse = getVirtualCameraInUse();
const bool microphoneIsMuted = getMicrophoneMuteState();
const bool cameraIsMuted = cameraInUse && getVirtualCameraMuteState();
if (cameraInUse)
{
// we're likely on a video call, so we must mute the unmuted cam/mic or reverse the mute state
// of everything, if cam and mic mute states are the same
if (microphoneIsMuted == cameraIsMuted)
{
reverseMicrophoneMute();
reverseVirtualCameraMuteState();
}
else if (cameraIsMuted)
{
reverseMicrophoneMute();
}
else if (microphoneIsMuted)
{
reverseVirtualCameraMuteState();
}
}
else
{
// if the camera is not in use, we just mute/unmute the mic
reverseMicrophoneMute();
}
return 1;
}
else if (isHotkeyPressed(kbd->vkCode, settings.microphoneMuteHotkey))
{
reverseMicrophoneMute();
return 1;
}
else if (isHotkeyPressed(kbd->vkCode, settings.cameraMuteHotkey))
{
reverseVirtualCameraMuteState();
return 1;
}
}
}
return CallNextHookEx(hook_handle, nCode, wParam, lParam);
}
void VideoConferenceModule::onGeneralSettingsChanged()
{
auto settings = PTSettingsHelper::load_general_settings();
bool enabled = false;
try
{
if (json::has(settings, L"enabled"))
{
for (const auto& mod : settings.GetNamedObject(L"enabled"))
{
const auto value = mod.Value();
if (value.ValueType() != json::JsonValueType::Boolean)
{
continue;
}
if (mod.Key() == get_key())
{
enabled = value.GetBoolean();
break;
}
}
}
}
catch (...)
{
LOG("Couldn't get enabled state");
}
if (enabled)
{
enable();
}
else
{
disable();
}
}
void VideoConferenceModule::onModuleSettingsChanged()
{
try
{
PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
//Trace::SettingsChanged(pressTime.value, overlayOpacity.value, theme.value);
if (_enabled)
{
if (const auto val = values.get_json(L"mute_camera_and_microphone_hotkey"))
{
settings.cameraAndMicrophoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_json(L"mute_microphone_hotkey"))
{
settings.microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_json(L"mute_camera_hotkey"))
{
settings.cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_string_value(L"toolbar_position"))
{
settings.toolbarPositionString = val.value();
}
if (const auto val = values.get_string_value(L"toolbar_monitor"))
{
settings.toolbarMonitorString = val.value();
}
if (const auto val = values.get_string_value(L"selected_camera"); val && val != settings.selectedCamera)
{
settings.selectedCamera = val.value();
sendSourceCameraNameUpdate();
}
if (const auto val = values.get_string_value(L"camera_overlay_image_path"); val && val != settings.imageOverlayPath)
{
settings.imageOverlayPath = val.value();
sendOverlayImageUpdate();
}
if (const auto val = values.get_bool_value(L"hide_toolbar_when_unmuted"))
{
toolbar.setHideToolbarWhenUnmuted(val.value());
}
const auto selectedMic = values.get_string_value(L"selected_mic");
if (selectedMic && selectedMic != settings.selectedMicrophone)
{
settings.selectedMicrophone = *selectedMic;
updateControlledMicrophones(settings.selectedMicrophone);
}
toolbar.show(settings.toolbarPositionString, settings.toolbarMonitorString);
}
}
catch (...)
{
LOG("onModuleSettingsChanged encountered an exception");
}
}
VideoConferenceModule::VideoConferenceModule() :
_generalSettingsWatcher{ PTSettingsHelper::get_powertoys_general_save_file_location(), [this] {
toolbar.scheduleGeneralSettingsUpdate();
} },
_moduleSettingsWatcher{ PTSettingsHelper::get_module_save_file_location(get_key()), [this] { toolbar.scheduleModuleSettingsUpdate(); } }
{
init_settings();
_settingsUpdateChannel =
SerializedSharedMemory::create(CameraSettingsUpdateChannel::endpoint(), sizeof(CameraSettingsUpdateChannel), false);
if (_settingsUpdateChannel)
{
_settingsUpdateChannel->access([](auto memory) {
auto updatesChannel = new (memory._data) CameraSettingsUpdateChannel{};
});
}
sendSourceCameraNameUpdate();
sendOverlayImageUpdate();
}
inline VideoConferenceModule::~VideoConferenceModule()
{
instance->unmuteAll();
toolbar.hide();
}
const wchar_t* VideoConferenceModule::get_name()
{
return L"Video Conference";
}
const wchar_t* VideoConferenceModule::get_key()
{
return L"Video Conference";
}
bool VideoConferenceModule::get_config(wchar_t* buffer, int* buffer_size)
{
return true;
}
void VideoConferenceModule::set_config(const wchar_t* config)
{
try
{
PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
values.save_to_settings_file();
}
catch (...)
{
LOG("VideoConferenceModule::set_config: exception during saving new settings values");
}
}
void VideoConferenceModule::init_settings()
{
try
{
PowerToysSettings::PowerToyValues powerToysSettings = PowerToysSettings::PowerToyValues::load_from_settings_file(L"Video Conference");
if (const auto val = powerToysSettings.get_json(L"mute_camera_and_microphone_hotkey"))
{
settings.cameraAndMicrophoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_json(L"mute_microphone_hotkey"))
{
settings.microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_json(L"mute_camera_hotkey"))
{
settings.cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_string_value(L"toolbar_position"))
{
settings.toolbarPositionString = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"toolbar_monitor"))
{
settings.toolbarMonitorString = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"selected_camera"))
{
settings.selectedCamera = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"camera_overlay_image_path"))
{
settings.imageOverlayPath = val.value();
}
if (const auto val = powerToysSettings.get_bool_value(L"hide_toolbar_when_unmuted"))
{
toolbar.setHideToolbarWhenUnmuted(val.value());
}
if (const auto val = powerToysSettings.get_string_value(L"selected_mic"); val && *val != settings.selectedMicrophone)
{
settings.selectedMicrophone = *val;
updateControlledMicrophones(settings.selectedMicrophone);
}
}
catch (std::exception&)
{
// Error while loading from the settings file. Just let default values stay as they are.
}
try
{
auto loaded = PTSettingsHelper::load_general_settings();
std::wstring settings_theme{ static_cast<std::wstring_view>(loaded.GetNamedString(L"theme", L"system")) };
if (settings_theme != L"dark" && settings_theme != L"light")
{
settings_theme = L"system";
}
toolbar.setTheme(settings_theme);
}
catch (...)
{
}
}
void VideoConferenceModule::updateControlledMicrophones(const std::wstring_view new_mic)
{
for (auto& controlledMic : _controlledMicrophones)
{
controlledMic.set_muted(false);
}
_controlledMicrophones.clear();
_microphoneTrackedInUI = nullptr;
auto allMics = MicrophoneDevice::getAllActive();
if (new_mic == L"[All]")
{
_controlledMicrophones = std::move(allMics);
if (auto defaultMic = MicrophoneDevice::getDefault())
{
for (auto& controlledMic : _controlledMicrophones)
{
if (controlledMic.id() == defaultMic->id())
{
_microphoneTrackedInUI = &controlledMic;
break;
}
}
}
}
else
{
for (auto& controlledMic : allMics)
{
if (controlledMic.name() == new_mic)
{
_controlledMicrophones.emplace_back(std::move(controlledMic));
_microphoneTrackedInUI = &_controlledMicrophones[0];
break;
}
}
}
if (_microphoneTrackedInUI)
{
_microphoneTrackedInUI->set_mute_changed_callback([&](const bool muted) {
toolbar.setMicrophoneMute(muted);
});
toolbar.setMicrophoneMute(_microphoneTrackedInUI->muted());
}
}
void toggleProxyCamRegistration(const bool enable)
{
if (!is_process_elevated())
{
return;
}
auto vcmRoot = fs::path{ get_module_folderpath() } / "modules";
vcmRoot /= "VideoConference";
std::array<fs::path, 2> proxyFilters = { vcmRoot / "VideoConferenceProxyFilter_x64.dll", vcmRoot / "VideoConferenceProxyFilter_x86.dll" };
for (const auto filter : proxyFilters)
{
std::wstring params{ L"/s " };
if (!enable)
{
params += L"/u ";
}
params += '"';
params += filter;
params += '"';
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
sei.lpFile = L"regsvr32";
sei.lpParameters = params.c_str();
sei.nShow = SW_SHOWNORMAL;
ShellExecuteExW(&sei);
}
}
void VideoConferenceModule::enable()
{
if (!_enabled)
{
toggleProxyCamRegistration(true);
toolbar.setMicrophoneMute(getMicrophoneMuteState());
toolbar.setCameraMute(getVirtualCameraMuteState());
toolbar.show(settings.toolbarPositionString, settings.toolbarMonitorString);
_enabled = true;
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
if (IsDebuggerPresent())
{
return;
}
#endif
hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), NULL);
}
}
void VideoConferenceModule::unmuteAll()
{
if (getVirtualCameraMuteState())
{
reverseVirtualCameraMuteState();
}
if (getMicrophoneMuteState())
{
reverseMicrophoneMute();
}
}
void VideoConferenceModule::disable()
{
if (_enabled)
{
toggleProxyCamRegistration(false);
if (hook_handle)
{
bool success = UnhookWindowsHookEx(hook_handle);
if (success)
{
hook_handle = nullptr;
}
}
instance->unmuteAll();
toolbar.hide();
_enabled = false;
}
}
bool VideoConferenceModule::is_enabled()
{
return _enabled;
}
void VideoConferenceModule::destroy()
{
delete this;
instance = nullptr;
}
void VideoConferenceModule::sendSourceCameraNameUpdate()
{
if (!_settingsUpdateChannel.has_value() || settings.selectedCamera.empty())
{
return;
}
_settingsUpdateChannel->access([](auto memory) {
auto updatesChannel = reinterpret_cast<CameraSettingsUpdateChannel*>(memory._data);
updatesChannel->sourceCameraName.emplace();
std::copy(begin(settings.selectedCamera), end(settings.selectedCamera), begin(*updatesChannel->sourceCameraName));
});
}
void VideoConferenceModule::sendOverlayImageUpdate()
{
if (!_settingsUpdateChannel.has_value())
{
return;
}
_imageOverlayChannel.reset();
wchar_t powertoysDirectory[MAX_PATH + 1];
DWORD length = GetModuleFileNameW(nullptr, powertoysDirectory, MAX_PATH);
PathRemoveFileSpecW(powertoysDirectory);
std::wstring blankImagePath(powertoysDirectory);
blankImagePath += L"\\modules\\VideoConference\\black.bmp";
_imageOverlayChannel = SerializedSharedMemory::create_readonly(CameraOverlayImageChannel::endpoint(),
settings.imageOverlayPath != L"" ? settings.imageOverlayPath : blankImagePath);
const auto imageSize = static_cast<uint32_t>(_imageOverlayChannel->size());
_settingsUpdateChannel->access([imageSize](auto memory) {
auto updatesChannel = reinterpret_cast<CameraSettingsUpdateChannel*>(memory._data);
updatesChannel->overlayImageSize.emplace(imageSize);
updatesChannel->newOverlayImagePosted = true;
});
}

View File

@@ -0,0 +1,86 @@
#pragma once
#include <common/SettingsAPI/FileWatcher.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <interface/powertoy_module_interface.h>
#include <common/SettingsAPI/settings_objects.h>
#include <MicrophoneDevice.h>
#include "Toolbar.h"
#include <SerializedSharedMemory.h>
extern class VideoConferenceModule* instance;
struct VideoConferenceSettings
{
PowerToysSettings::HotkeyObject cameraAndMicrophoneMuteHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, 78);
PowerToysSettings::HotkeyObject microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, true, 65);
PowerToysSettings::HotkeyObject cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, true, 79);
std::wstring toolbarPositionString;
std::wstring toolbarMonitorString;
std::wstring selectedCamera;
std::wstring imageOverlayPath;
std::wstring selectedMicrophone;
};
class VideoConferenceModule : public PowertoyModuleIface
{
public:
VideoConferenceModule();
~VideoConferenceModule();
virtual const wchar_t* get_name() override;
virtual bool get_config(wchar_t* buffer, int* buffer_size) override;
virtual void set_config(const wchar_t* config) override;
virtual void enable() override;
virtual void disable() override;
virtual bool is_enabled() override;
virtual void destroy() override;
virtual const wchar_t * get_key() override;
void sendSourceCameraNameUpdate();
void sendOverlayImageUpdate();
static void unmuteAll();
static void reverseMicrophoneMute();
static bool getMicrophoneMuteState();
static void reverseVirtualCameraMuteState();
static bool getVirtualCameraMuteState();
static bool getVirtualCameraInUse();
void onGeneralSettingsChanged();
void onModuleSettingsChanged();
private:
void init_settings();
void updateControlledMicrophones(const std::wstring_view new_mic);
// all callback methods and used by callback have to be static
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
static bool isKeyPressed(unsigned int keyCode);
static bool isHotkeyPressed(DWORD code, PowerToysSettings::HotkeyObject& hotkey);
static HHOOK hook_handle;
bool _enabled = false;
std::vector<MicrophoneDevice> _controlledMicrophones;
MicrophoneDevice* _microphoneTrackedInUI = nullptr;
std::optional<SerializedSharedMemory> _imageOverlayChannel;
std::optional<SerializedSharedMemory> _settingsUpdateChannel;
FileWatcher _generalSettingsWatcher;
FileWatcher _moduleSettingsWatcher;
static VideoConferenceSettings settings;
static Toolbar toolbar;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

View File

@@ -0,0 +1,35 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include "trace.h"
#include "VideoConferenceModule.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Trace::RegisterProvider();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
if (!instance)
{
instance = new VideoConferenceModule();
return instance;
}
else
{
return nullptr;
}
}

View File

@@ -0,0 +1,5 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200519.2" targetFramework="native" />
</packages>

View File

@@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

View File

@@ -0,0 +1,24 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Unknwn.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <wil/resource.h>
#include <wil/com.h>
#include <string_view>
#include <optional>
#include <iostream>
#include <fstream>
#include <vector>
#include <functional>
#include <algorithm>
#include <Shobjidl.h>
#include <Shlwapi.h>
#include <common/Telemetry/ProjectTelemetry.h>

View File

@@ -0,0 +1,57 @@
#include "pch.h"
#include "trace.h"
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider() noexcept
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider() noexcept
{
TraceLoggingUnregister(g_hProvider);
}
void Trace::SettingsChanged(const struct VideoConferenceSettings& settings) noexcept
{
bool CustomOverlayImage = (settings.imageOverlayPath.length() > 0);
TraceLoggingWrite(
g_hProvider,
"VideoConference_SettingsChanged",
TraceLoggingWideString(settings.toolbarPositionString.c_str(), "ToolbarPosition"),
TraceLoggingWideString(settings.toolbarMonitorString.c_str(), "ToolbarMonitorSelection"),
TraceLoggingBool(CustomOverlayImage, "CustomImageOverlayUsed"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::MicrophoneMuted() noexcept
{
TraceLoggingWrite(
g_hProvider,
"VideoConference_MicrophoneMuted",
TraceLoggingBoolean(true, "MicrophoneMuted"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::CameraMuted() noexcept
{
TraceLoggingWrite(
g_hProvider,
"VideoConference_CameraMuted",
TraceLoggingBoolean(true, "CameraMuted"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "VideoConferenceModule.h"
class Trace
{
public:
static void RegisterProvider() noexcept;
static void UnregisterProvider() noexcept;
static void SettingsChanged(const struct VideoConferenceSettings &settings) noexcept;
static void MicrophoneMuted() noexcept;
static void CameraMuted() noexcept;
};