Compare commits

...

14 Commits

Author SHA1 Message Date
yuyoyuppe
d4e0265821 VCM: fix compilation warning 2020-10-15 15:43:26 +03:00
Niels Laute
6e92e20884 [VideoConferenceMute] Add camera overlay image preview to settings (#7303)
* Added video conf image

* Added image preview

Co-authored-by: Niels Laute <niels9001@hotmail.com>
2020-10-15 14:25:18 +02:00
yuyoyuppe
8611a9a29a fix possible exception sites 2020-10-14 13:52:55 +03:00
yuyoyuppe
d2913bc249 fix logging issue 2020-10-13 13:43:23 +03:00
yuyoyuppe
9ae3efa98e fix crash on shutdown 2020-10-13 11:33:26 +03:00
yuyoyuppe
128a305ae4 VCM: unmute both cam and mic when we detect that a camera-using app has quitted 2020-10-12 20:02:34 +03:00
yuyoyuppe
c48424a3eb style fix 2020-10-12 19:31:53 +03:00
yuyoyuppe
813fe15f52 VCMute: refactor toolbar and settings
- settings are not reset upon closing
- can't select virtual cam as physical
- win+N works reliably
- add missed telemetry call for mic muting
2020-10-12 19:19:17 +03:00
yuyoyuppe
a2b30209f4 fix VCM toolbar always appearing on startup 2020-10-09 13:40:58 +03:00
yuyoyuppe
c958a4a95e build fix: add missed dlls 2020-10-08 21:09:23 +03:00
yuyoyuppe
d4db76a859 build fix #2 2020-10-08 12:49:46 +03:00
yuyoyuppe
3386b953bb build fix (wil isn't available by default) 2020-10-07 22:42:10 +03:00
yuyoyuppe
d9dd1e1649 bump the version! 2020-10-07 20:10:09 +03:00
yuyoyuppe
3ba6a48157 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>
2020-10-07 20:03:18 +03:00
145 changed files with 7500 additions and 241 deletions

View File

@@ -0,0 +1,7 @@
cd /D "%~dp0"
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 -winsdk=10.0.18362.0
call msbuild -m ../PowerToys.sln /t:modules\VideoConference\VideoConferenceCustomMediaSource,modules\VideoConference\VideoConferenceVirtualDriver /p:Configuration=Release /p:Platform=x64 /p:CIBuild=true || exit /b 1
SET PTRoot=..

View File

@@ -0,0 +1,100 @@
environment:
host:
os: 'windows'
flavor: 'server'
version: '2019'
runtime:
provider: 'appcontainer'
image: 'cdpxwin1809.azurecr.io/global/vse2019:latest'
source_mode: 'map'
version:
name: 'PowerToys'
major: 1
minor: 0
signing_options:
profile: 'external_distribution'
package_sources:
nuget:
feeds:
'Nuget': 'https://api.nuget.org/v3/index.json'
'Toolset': 'https://msazure.pkgs.visualstudio.com/_packaging/Toolset/nuget/v3/index.json'
'PipelineBuildSupplement': 'https://msazure.pkgs.visualstudio.com/_packaging/PipelineBuildSupplement/nuget/v3/index.json'
restore:
commands:
- !!defaultcommand
name: 'Restore Power Toys Telemetry'
command: '.pipelines\restore-telemetry.cmd'
- !!defaultcommand
name: 'Restore Power Toys'
command: '.pipelines\restore.cmd'
- !!defaultcommand
name: 'Restore Localization packages'
command: '.pipelines\restore-localization.cmd'
build:
commands:
- !!buildcommand
name: 'Build Power Toys'
command: '.pipelines\build-videoconference.cmd'
artifacts:
- to: 'Symbols'
include:
- 'x64/**/*.pdb'
exclude:
- 'x64/Release/obj/**/*.pdb'
- from: 'x64/Release'
to: 'Build_Output'
include:
- 'modules\VideoConference\VideoConferenceModule.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceCustomMediaSource.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\videoconferencevirtualdriver.cat'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.inf'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.cab'
signing_options:
sign_inline: true # This does signing a soon as this command completes
- !!buildcommand
name: 'Localize Power Toys'
command: '.pipelines\build-localization.cmd'
artifacts:
- from: 'out\loc'
to: 'loc'
include:
- '**/*'
#package:
# commands:
# - !!buildcommand
# name: 'Build MSIX package'
# command: 'installer\msix\build_msix_cdpx.cmd'
# artifacts:
# - from: 'installer\msix\bin'
# to: 'Build_MSIX_Package_Output'
# include:
# - '*.msix'
# - '*.msixbundle'
# signing_options:
# profile: '400'
static_analysis_options:
binskim_options:
files_to_scan:
- from: 'installer/packages'
exclude:
- 'WiX.*/**/*.dll'
- 'Wix.*/**/*.exe'
moderncop_options:
files_to_scan:
- from: 'src'
exclude:
- '**/just.config.js'
- '**/webpack.config.js'
- '**/webpack.serve.config.js'
- '**/dist/bundle.js'

View File

@@ -116,6 +116,12 @@ build:
- 'modules\Microsoft.Launcher.dll'
- 'modules\PowerRename\PowerRenameExt.dll'
- 'modules\ShortcutGuide\ShortcutGuide.dll'
- 'modules\VideoConference\VideoConferenceModule.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceCustomMediaSource.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\videoconferencevirtualdriver.cat'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.inf'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.cab'
- 'Notifications.dll'
- 'os-detection.dll'
- 'PowerToys.exe'

View File

@@ -1,3 +1,12 @@
cd /D "%~dp0"
nuget restore ../PowerToys.sln || exit /b 1
powershell.exe -Command "Invoke-WebRequest -OutFile %tmp%\wdksetup.exe https://go.microsoft.com/fwlink/p/?linkid=2085767"
%tmp%\wdksetup.exe /q
copy "C:\Program Files (x86)\Windows Kits\10\Vsix\VS2019\WDK.vsix" %tmp%\wdkvsix.zip
powershell Expand-Archive %tmp%\wdkvsix.zip -DestinationPath %tmp%\wdkvsix -Force
robocopy /e %tmp%\wdkvsix\$MSBuild\Microsoft\VC\v160 "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160" || IF %ERRORLEVEL% LEQ 7 EXIT 0
robocopy /e %tmp%\wdkvsix\$VCTargets "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VC\VCTargets" || IF %ERRORLEVEL% LEQ 7 EXIT 0

View File

@@ -267,6 +267,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Setting
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.Calculator.UnitTest", "src\modules\launcher\Plugins\Microsoft.Plugin.Calculator.UnitTest\Microsoft.Plugin.Calculator.UnitTest.csproj", "{632BBE62-5421-49EA-835A-7FFA4F499BD6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VideoConference", "VideoConference", "{05BE6150-D5B3-48F0-AEB9-C44096950C6D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceCustomMediaSource", "src\modules\videoconference\VideoConferenceCustomMediaSource\VideoConferenceCustomMediaSource.vcxproj", "{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}"
ProjectSection(ProjectDependencies) = postProject
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A} = {459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceModule", "src\modules\videoconference\VideoConferenceModule\Video Conference.vcxproj", "{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceShared", "src\modules\videoconference\VideoConferenceShared\VideoConferenceShared.vcxproj", "{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceVirtualDriver", "src\modules\videoconference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.vcxproj", "{3098C6BF-E96E-4793-A70E-FB09B741580A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -533,6 +546,24 @@ Global
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Debug|x64.Build.0 = Debug|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Release|x64.ActiveCfg = Release|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Release|x64.Build.0 = Release|x64
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}.Debug|x64.ActiveCfg = Debug|x64
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}.Debug|x64.Build.0 = Debug|x64
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}.Release|x64.ActiveCfg = Release|x64
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}.Release|x64.Build.0 = Release|x64
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}.Debug|x64.ActiveCfg = Debug|x64
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}.Debug|x64.Build.0 = Debug|x64
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}.Release|x64.ActiveCfg = Release|x64
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}.Release|x64.Build.0 = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x64.ActiveCfg = Debug|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x64.Build.0 = Debug|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x64.ActiveCfg = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x64.Build.0 = Release|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Debug|x64.ActiveCfg = Debug|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Debug|x64.Build.0 = Debug|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Debug|x64.Deploy.0 = Debug|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Release|x64.ActiveCfg = Release|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Release|x64.Build.0 = Release|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Release|x64.Deploy.0 = Release|x64
{632BBE62-5421-49EA-835A-7FFA4F499BD6}.Debug|x64.ActiveCfg = Debug|x64
{632BBE62-5421-49EA-835A-7FFA4F499BD6}.Debug|x64.Build.0 = Debug|x64
{632BBE62-5421-49EA-835A-7FFA4F499BD6}.Release|x64.ActiveCfg = Release|x64
@@ -613,6 +644,11 @@ Global
{03276A39-D4E9-417C-8FFD-200B0EE5E871} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{B81FB7B6-D30E-428F-908A-41422EFC1172} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{0F85E674-34AE-443D-954C-8321EB8B93B1} = {C3081D9A-1586-441A-B5F4-ED815B3719C1}
{05BE6150-D5B3-48F0-AEB9-C44096950C6D} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD} = {05BE6150-D5B3-48F0-AEB9-C44096950C6D}
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193} = {05BE6150-D5B3-48F0-AEB9-C44096950C6D}
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A} = {05BE6150-D5B3-48F0-AEB9-C44096950C6D}
{3098C6BF-E96E-4793-A70E-FB09B741580A} = {05BE6150-D5B3-48F0-AEB9-C44096950C6D}
{632BBE62-5421-49EA-835A-7FFA4F499BD6} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution

View File

@@ -74,6 +74,10 @@ modify --installpath "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\%targetFo
--add Microsoft.VisualStudio.Component.VC.ATL.Spectre
```
**Optional step:**<br/>
4. to build the Video Conference module, install the [WDK version 1903](https://docs.microsoft.com/en-us/windows-hardware/drivers/other-wdk-downloads) ([direct download link](https://go.microsoft.com/fwlink/?linkid=2085767))<br />
During the installation, make sure that, when prompted, the `Install Windows Driver Kit Visual Studio extension` option is checked.
### Compiling Source Code
- Open `powertoys.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.

View File

@@ -2,8 +2,9 @@
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureNuGetPackageBuildImports" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\WiX.3.11.2\build\wix.props" Condition="Exists('..\packages\WiX.3.11.2\build\wix.props')" />
<Import Project="..\..\src\Version.props" />
<Import Project="..\..\src\hasWDK.props" />
<PropertyGroup>
<DefineConstants>Version=$(Version);</DefineConstants>
<DefineConstants>Version=$(Version);HasWDK=$(HasWDK)</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>

View File

@@ -9,6 +9,7 @@
<?define PowerRenameProjectName="PowerRename"?>
<?define ShortcutGuideProjectName="ShortcutGuide"?>
<?define ColorPickerProjectName="ColorPicker"?>
<?define VideoConferenceProjectName="VideoConference"?>
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
<?define BinX64Dir="$(var.RepoDir)x64\$(var.Configuration)\" ?>
@@ -87,6 +88,26 @@
<Custom Action="RegisterPowerToysSchTask" After="InstallFiles">
NOT Installed and CREATESCHEDULEDTASK = 1
</Custom>
<?if $(var.HasWDK)="true" ?>
<Custom Action="SetCertifyVirtualCameraDriverParam" After="RegisterPowerToysSchTask" >
WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="CertifyVirtualCameraDriver" After="SetCertifyVirtualCameraDriverParam" >
NOT Installed and WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="SetInstallVirtualCameraDriverParam" After="CertifyVirtualCameraDriver" >
WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="InstallVirtualCameraDriver" After="SetInstallVirtualCameraDriverParam" >
NOT Installed and WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="SetUninstallVirtualCameraDriverParam" Before="UninstallVirtualCameraDriver" >
WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="UninstallVirtualCameraDriver" Before="RemoveFiles">
Installed and WINDOWSBUILDNUMBER >= 18362
</Custom>
<?endif?>
<Custom Action="WixCloseApplications" Before="RemoveFiles" />
<Custom Action="RemovePowerToysSchTasks" After="RemoveFiles" />
<Custom Action="TelemetryLogInstallSuccess" After="InstallFinalize">
@@ -204,6 +225,44 @@
DllEntry="DetectPrevInstallPathCA"
/>
<?if $(var.HasWDK)="true" ?>
<CustomAction Id="SetCertifyVirtualCameraDriverParam"
Property="CertifyVirtualCameraDriver"
Value="[#VideoConferenceVirtualDriver.cer]" />
<CustomAction Id="CertifyVirtualCameraDriver"
Return="ignore"
Impersonate="no"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="CertifyVirtualCameraDriverCA"
/>
<CustomAction Id="SetInstallVirtualCameraDriverParam"
Property="InstallVirtualCameraDriver"
Value="[#VideoConferenceVirtualDriver.inf]" />
<CustomAction Id="InstallVirtualCameraDriver"
Return="ignore"
Impersonate="no"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="InstallVirtualCameraDriverCA"
/>
<CustomAction Id="SetUninstallVirtualCameraDriverParam"
Property="UninstallVirtualCameraDriver"
Value="[#VideoConferenceVirtualDriver.inf]" />
<CustomAction Id="UninstallVirtualCameraDriver"
Return="ignore"
Impersonate="no"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="UninstallVirtualCameraDriverCA"
/>
<?endif?>
<!-- Close 'PowerToys.exe' before uninstall-->
<Property Id="MSIRESTARTMANAGERCONTROL" Value="Disable" />
<util:CloseApplication CloseMessage="yes" Target="PowerToys.exe" ElevatedCloseMessage="yes" RebootPrompt="no" TerminateProcess="0" />
@@ -240,6 +299,11 @@
</Directory>
<Directory Id="PowerRenameInstallFolder" Name="$(var.PowerRenameProjectName)"/>
<Directory Id="ShortcutGuideInstallFolder" Name="$(var.ShortcutGuideProjectName)"/>
<?if $(var.HasWDK)="true" ?>
<Directory Id="VideoConferenceInstallFolder" Name="$(var.VideoConferenceProjectName)">
<Directory Id="VideoConferenceIconsFolder" Name="Icons" />
</Directory>
<?endif?>
<Directory Id="FileExplorerPreviewInstallFolder" Name="FileExplorerPreview" />
<Directory Id="FancyZonesInstallFolder" Name="$(var.FancyZonesProjectName)" />
<Directory Id="KeyboardManagerInstallFolder" Name="$(var.KeyboardManagerProjectName)" />
@@ -612,6 +676,38 @@
<File Source="$(var.BinX64Dir)modules\$(var.ShortcutGuideProjectName)\$(var.ShortcutGuideProjectName).dll" KeyPath="yes" />
</Component>
</DirectoryRef>
<?if $(var.HasWDK)="true" ?>
<DirectoryRef Id="VideoConferenceInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\">
<Component Id="Module_VideoConference" Guid="5996527a-40fc-432e-b3ac-abc0b4bd3887" Win64="yes">
<Condition>WINDOWSBUILDNUMBER >= 18362</Condition>
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceModule.dll" KeyPath="yes" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\black.bmp" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver\videoconferencevirtualdriver.cat" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver\VideoConferenceCustomMediaSource.dll" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.dll" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.inf" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver.cer" />
</Component>
</DirectoryRef>
<DirectoryRef Id="VideoConferenceIconsFolder" FileSource="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons">
<Component Id="Module_VideoConferenceIcons" Guid="5996527a-40fc-432e-b34c-abc0b4bd3887" Win64="yes">
<Condition>WINDOWSBUILDNUMBER >= 18362</Condition>
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-NotInUse Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-NotInUse Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-Off Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-Off Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-On Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-On Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-NotInUse Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-NotInUse Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-Off Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-Off Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-On Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-On Light.png" />
</Component>
</DirectoryRef>
<?endif?>
<DirectoryRef Id="KeyboardManagerInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.KeyboardManagerProjectName)\">
<Component Id="Module_KeyboardManager" Guid="9279BD82-786F-4F0B-8E49-DB484EE34C9B" Win64="yes">
@@ -666,7 +762,7 @@
<File Source="$(var.BinX64Dir)SettingsUIRunner\Microsoft.PowerToys.Settings.UI.Runner.exe"/>
<File Source="$(var.BinX64Dir)SettingsUIRunner\Microsoft.PowerToys.Settings.UI.exe"/>
<!-- dll -->
<?foreach File in concrt140_app.dll;Microsoft.Bcl.AsyncInterfaces.dll;Microsoft.PowerToys.Settings.UI.Lib.dll;Microsoft.PowerToys.Settings.UI.Runner.dll;Microsoft.Toolkit.dll;Microsoft.Toolkit.Uwp.dll;Microsoft.Toolkit.Uwp.UI.dll;Microsoft.Toolkit.Win32.UI.XamlHost.dll;Microsoft.Toolkit.Win32.UI.XamlHost.Managed.dll;Microsoft.Toolkit.Wpf.UI.Controls.dll;Microsoft.Toolkit.Wpf.UI.XamlHost.dll;Microsoft.UI.Xaml.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;msvcp140_1_app.dll;msvcp140_2_app.dll;msvcp140_app.dll;Newtonsoft.Json.dll;PowerToysInterop.dll;System.Runtime.CompilerServices.Unsafe.dll;System.Text.Encodings.Web.dll;System.Text.Json.dll;vcamp140_app.dll;vccorlib140_app.dll;vcomp140_app.dll;vcruntime140_1_app.dll;vcruntime140_app.dll;Telemetry.dll;ManagedCommon.dll?>
<?foreach File in concrt140_app.dll;Microsoft.Bcl.AsyncInterfaces.dll;Microsoft.PowerToys.Settings.UI.Lib.dll;Microsoft.PowerToys.Settings.UI.Runner.dll;Microsoft.Toolkit.dll;Microsoft.Toolkit.Uwp.dll;Microsoft.Toolkit.Uwp.UI.dll;Microsoft.Toolkit.Win32.UI.XamlHost.dll;Microsoft.Toolkit.Win32.UI.XamlHost.Managed.dll;Microsoft.Toolkit.Wpf.UI.Controls.dll;Microsoft.Toolkit.Wpf.UI.XamlHost.dll;Microsoft.UI.Xaml.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;msvcp140_1_app.dll;msvcp140_2_app.dll;msvcp140_app.dll;Newtonsoft.Json.dll;PowerToysInterop.dll;System.Runtime.CompilerServices.Unsafe.dll;System.Text.Encodings.Web.dll;System.Text.Json.dll;vcamp140_app.dll;vccorlib140_app.dll;vcomp140_app.dll;vcruntime140_1_app.dll;vcruntime140_app.dll;Telemetry.dll;ManagedCommon.dll;System.Deployment.dll;System.Windows.Forms.dll;System.Runtime.Serialization.Formatters.Soap.dll?>
<File Id="SettingsV2_$(var.File)" Source="$(var.BinX64Dir)SettingsUIRunner\$(var.File)" />
<?endforeach?>
<!-- json -->
@@ -706,7 +802,7 @@
</DirectoryRef>
<DirectoryRef Id="SettingsV2AssetsModulesInstallFolder" FileSource="$(var.BinX64Dir)SettingsUIRunner\Assets\Modules">
<Component Id="SettingsV2AssetsModules" Guid="A0B961A9-77D0-4223-88A9-E3B41BD9C329" Win64="yes">
<?foreach File in ColorPicker.png;FancyZones.png;ImageResizer.png;KBM.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ShortcutGuide.png?>
<?foreach File in ColorPicker.png;FancyZones.png;ImageResizer.png;KBM.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ShortcutGuide.png;VideoConference.png?>
<File Source="$(var.BinX64Dir)SettingsUIRunner\Assets\Modules\$(var.File)" />
<?endforeach?>
</Component>
@@ -793,6 +889,8 @@
<ComponentRef Id="vcredist_dlls" />
<ComponentRef Id="PowerToysSvgs" />
<ComponentRef Id="Module_ShortcutGuide" />
<ComponentRef Id="Module_VideoConference" />
<ComponentRef Id="Module_VideoConferenceIcons" />
<ComponentRef Id="Module_FancyZones" />
<ComponentRef Id="DesktopShortcut" />
<ComponentRef Id="Module_PowerRename" />

View File

@@ -31,7 +31,6 @@ echo ^</Project^> >> !settingsPublishProfile!
rem In case of Release we should not use Debug CRT in VCRT forwarders
msbuild !PTRoot!\src\core\Microsoft.PowerToys.Settings.UI.Runner\Microsoft.PowerToys.Settings.UI.Runner.csproj -t:Publish -p:Configuration="Release" -p:Platform="x64" -p:AppxBundle=Never -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=!settingsProfileFileName!
rem Publish Launcher
SET launcherProfileFolderName=!PTRoot!\src\modules\launcher\PowerLauncher\Properties\PublishProfiles\

View File

@@ -3,6 +3,11 @@
#include <ProjectTelemetry.h>
#include "../../src/common/updating/updating.h"
#include <newdev.h>
#include <Windows.h>
#pragma comment (lib, "crypt32.lib")
using namespace std;
@@ -16,6 +21,7 @@ TRACELOGGING_DEFINE_PROVIDER(
const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
// Creates a Scheduled Task to run at logon for the current user.
@@ -564,6 +570,8 @@ LExit:
return WcaFinalize(er);
}
UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
@@ -662,6 +670,165 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
return WcaFinalize(er);
}
UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall)
{
#ifdef CIBuild // On pipeline we are using microsoft certification
WcaInitialize(hInstall, "CertifyVirtualCameraDriverCA");
return WcaFinalize(ERROR_SUCCESS);
#else
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
LPWSTR certificatePath = NULL;
HCERTSTORE hCertStore = NULL;
HANDLE hfile = NULL;
DWORD size = INVALID_FILE_SIZE;
hr = WcaInitialize(hInstall, "CertifyVirtualCameraDriverCA");
ExitOnFailure(hr, "Failed to initialize", hr);
hr = WcaGetProperty(L"CustomActionData", &certificatePath);
ExitOnFailure(hr, "Failed to get install preperty", hr);
hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"AuthRoot");
if (!hCertStore)
{
hr = GetLastError();
ExitOnFailure(hr, "Cannot put principal run level: %x", hr);
}
hfile = CreateFile(certificatePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hfile == INVALID_HANDLE_VALUE)
{
hr = GetLastError();
ExitOnFailure(hr, "Certificate file open failed", hr);
}
size = GetFileSize(hfile, NULL);
if (size == INVALID_FILE_SIZE)
{
hr = GetLastError();
ExitOnFailure(hr, "Certificate file size not valid", hr);
}
char* pFileContent = (char*)malloc(size);
DWORD sizeread;
if (!ReadFile(hfile, pFileContent, size, &sizeread, NULL))
{
hr = GetLastError();
ExitOnFailure(hr, "Certificate file read failed", hr);
}
if (!CertAddEncodedCertificateToStore(hCertStore,
X509_ASN_ENCODING,
(const BYTE*)pFileContent,
size,
CERT_STORE_ADD_ALWAYS,
NULL))
{
hr = GetLastError();
ExitOnFailure(hr, "Adding certificate failed", hr);
}
free(pFileContent);
LExit:
ReleaseStr(certificatePath);
if (hCertStore)
{
CertCloseStore(hCertStore, 0);
}
if (hfile)
{
CloseHandle(hfile);
}
if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to add certificate to store"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
#endif
}
UINT __stdcall InstallVirtualCameraDriverCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "InstallVirtualCameraDriverCA");
ExitOnFailure(hr, "Failed to initialize");
LPWSTR driverPath = NULL;
hr = WcaGetProperty(L"CustomActionData", &driverPath);
ExitOnFailure(hr, "Failed to get install preperty");
BOOL requiresReboot;
DiInstallDriverW(GetConsoleWindow(), driverPath, DIIRFLAG_FORCE_INF, &requiresReboot);
hr = GetLastError();
ExitOnFailure(hr, "Failed to install driver");
LExit:
if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to install virtual camera driver"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall UninstallVirtualCameraDriverCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "UninstallVirtualCameraDriverCA");
ExitOnFailure(hr, "Failed to initialize");
LPWSTR driverPath = NULL;
hr = WcaGetProperty(L"CustomActionData", &driverPath);
ExitOnFailure(hr, "Failed to get uninstall preperty");
BOOL requiresReboot;
DiUninstallDriverW(GetConsoleWindow(), driverPath, 0, &requiresReboot);
switch (GetLastError())
{
case ERROR_ACCESS_DENIED:
case ERROR_FILE_NOT_FOUND:
case ERROR_INVALID_FLAGS:
case ERROR_IN_WOW64:
{
hr = GetLastError();
ExitOnFailure(hr, "Failed to uninstall driver");
break;
}
}
LExit:
if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Filed to iminstall virtual camera driver"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
// DllMain - Initialize and cleanup WiX custom action utils.
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)

View File

@@ -12,4 +12,7 @@ EXPORTS
TelemetryLogUninstallFailCA
TelemetryLogRepairCancelCA
TelemetryLogRepairFailCA
TerminateProcessesCA
TerminateProcessesCA
CertifyVirtualCameraDriverCA
InstallVirtualCameraDriverCA
UninstallVirtualCameraDriverCA

View File

@@ -60,7 +60,7 @@
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;Newdev.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -81,8 +81,11 @@
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<ClCompile Condition="'$(CIBuild)'=='true'">
<AdditionalOptions >/DCIBuild</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;Newdev.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Version>0.23.2</Version>
<Version>0.24.0</Version>
</PropertyGroup>
</Project>
</Project>

View File

@@ -0,0 +1,142 @@
#include "pch.h"
#include "MicrophoneDevice.h"
#include <Functiondiscoverykeys_devpkey.h>
MicrophoneDevice::MicrophoneDevice(wil::com_ptr_nothrow<IMMDevice> device, wil::com_ptr_nothrow<IAudioEndpointVolume> endpoint) :
_device{ std::move(device) },
_endpoint{ std::move(endpoint) }
{
if (!_device || !_endpoint)
{
throw std::logic_error("MicrophoneDevice was initialized with null objects!");
}
_device->GetId(&_id);
wil::com_ptr_nothrow<IPropertyStore> props;
_device->OpenPropertyStore(
STGM_READ, &props);
if (props)
{
props->GetValue(PKEY_Device_FriendlyName, &_friendly_name);
}
}
MicrophoneDevice::~MicrophoneDevice()
{
if (_notifier)
{
_endpoint->UnregisterControlChangeNotify(_notifier.get());
}
}
bool MicrophoneDevice::active() const noexcept
{
DWORD state = 0;
_device->GetState(&state);
return state == DEVICE_STATE_ACTIVE;
}
void MicrophoneDevice::set_muted(const bool muted) noexcept
{
_endpoint->SetMute(muted, nullptr);
}
bool MicrophoneDevice::muted() const noexcept
{
BOOL muted = FALSE;
_endpoint->GetMute(&muted);
return muted;
}
void MicrophoneDevice::toggle_muted() noexcept
{
set_muted(!muted());
}
std::wstring_view MicrophoneDevice::id() const noexcept
{
return _id ? _id.get() : FALLBACK_ID;
}
std::wstring_view MicrophoneDevice::name() const noexcept
{
return _friendly_name.pwszVal ? _friendly_name.pwszVal : FALLBACK_NAME;
}
void MicrophoneDevice::set_mute_changed_callback(mute_changed_cb_t callback) noexcept
{
_mute_changed_callback = std::move(callback);
_notifier = winrt::make<VolumeNotifier>(this);
_endpoint->RegisterControlChangeNotify(_notifier.get());
}
std::optional<MicrophoneDevice> MicrophoneDevice::getDefault()
{
auto deviceEnumerator = wil::CoCreateInstanceNoThrow<MMDeviceEnumerator, IMMDeviceEnumerator>();
if (!deviceEnumerator)
{
return std::nullopt;
}
wil::com_ptr_nothrow<IMMDevice> captureDevice;
deviceEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &captureDevice);
if (!captureDevice)
{
return std::nullopt;
}
wil::com_ptr_nothrow<IAudioEndpointVolume> microphoneEndpoint;
captureDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, reinterpret_cast<LPVOID*>(&microphoneEndpoint));
if (!microphoneEndpoint)
{
return std::nullopt;
}
return std::make_optional<MicrophoneDevice>(std::move(captureDevice), std::move(microphoneEndpoint));
}
std::vector<MicrophoneDevice> MicrophoneDevice::getAllActive()
{
std::vector<MicrophoneDevice> microphoneDevices;
auto deviceEnumerator = wil::CoCreateInstanceNoThrow<MMDeviceEnumerator, IMMDeviceEnumerator>();
if (!deviceEnumerator)
{
return microphoneDevices;
}
wil::com_ptr_nothrow<IMMDeviceCollection> captureDevices;
deviceEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &captureDevices);
if (!captureDevices)
{
return microphoneDevices;
}
UINT nDevices = 0;
captureDevices->GetCount(&nDevices);
microphoneDevices.reserve(nDevices);
for (UINT i = 0; i < nDevices; ++i)
{
wil::com_ptr_nothrow<IMMDevice> device;
captureDevices->Item(i, &device);
if (!device)
{
continue;
}
wil::com_ptr_nothrow<IAudioEndpointVolume> microphoneEndpoint;
device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, reinterpret_cast<LPVOID*>(&microphoneEndpoint));
if (!microphoneEndpoint)
{
continue;
}
microphoneDevices.emplace_back(std::move(device), std::move(microphoneEndpoint));
}
return microphoneDevices;
}
MicrophoneDevice::VolumeNotifier::VolumeNotifier(MicrophoneDevice* subscribedDevice) :
_subscribedDevice{ subscribedDevice }
{
}
HRESULT __stdcall MicrophoneDevice::VolumeNotifier::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA data)
{
_subscribedDevice->_mute_changed_callback(data->bMuted);
return S_OK;
}

View File

@@ -0,0 +1,60 @@
#pragma once
#include <winrt/base.h>
#include <wil/resource.h>
#include <wil/com.h>
#include <Windows.h>
#include <Unknwn.h>
#include <string_view>
#include <optional>
#include <vector>
#include <functional>
#include <Mmdeviceapi.h>
#include <Endpointvolume.h>
class MicrophoneDevice
{
public:
using mute_changed_cb_t = std::function<void(bool muted)>;
private:
friend struct VolumeNotifier;
struct VolumeNotifier : winrt::implements<VolumeNotifier, IAudioEndpointVolumeCallback>
{
MicrophoneDevice* _subscribedDevice = nullptr;
VolumeNotifier(MicrophoneDevice* subscribedDevice);
virtual HRESULT __stdcall OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA data) override;
};
wil::unique_cotaskmem_string _id;
wil::unique_prop_variant _friendly_name;
mute_changed_cb_t _mute_changed_callback;
winrt::com_ptr<IAudioEndpointVolumeCallback> _notifier;
wil::com_ptr_nothrow<IAudioEndpointVolume> _endpoint;
wil::com_ptr_nothrow<IMMDevice> _device;
constexpr static inline std::wstring_view FALLBACK_NAME = L"Unknown device";
constexpr static inline std::wstring_view FALLBACK_ID = L"UNKNOWN_ID";
public:
MicrophoneDevice(MicrophoneDevice&&) noexcept = default;
MicrophoneDevice(wil::com_ptr_nothrow<IMMDevice> device, wil::com_ptr_nothrow<IAudioEndpointVolume> endpoint);
~MicrophoneDevice();
bool active() const noexcept;
void set_muted(const bool muted) noexcept;
bool muted() const noexcept;
void toggle_muted() noexcept;
std::wstring_view id() const noexcept;
std::wstring_view name() const noexcept;
void set_mute_changed_callback(mute_changed_cb_t callback) noexcept;
static std::optional<MicrophoneDevice> getDefault();
static std::vector<MicrophoneDevice> getAllActive();
};

View File

@@ -0,0 +1,88 @@
#include "pch.h"
#include "VideoCaptureDeviceList.h"
#include <mfapi.h>
#include <Mfidl.h>
void VideoCaptureDeviceList::Clear()
{
for (UINT32 i = 0; i < m_numberDevices; i++)
{
CoTaskMemFree(m_deviceFriendlyNames[i]);
if (m_ppDevices[i])
{
m_ppDevices[i]->Release();
}
}
CoTaskMemFree(m_ppDevices);
m_ppDevices = nullptr;
if (m_deviceFriendlyNames)
{
delete[] m_deviceFriendlyNames;
}
m_deviceFriendlyNames = nullptr;
m_numberDevices = 0;
}
HRESULT VideoCaptureDeviceList::EnumerateDevices()
{
HRESULT hr = S_OK;
wil::com_ptr<IMFAttributes> pAttributes;
Clear();
// Initialize an attribute store. We will use this to
// specify the enumeration parameters.
hr = MFCreateAttributes(&pAttributes, 1);
// Ask for source type = video capture devices
if (SUCCEEDED(hr))
{
hr = pAttributes->SetGUID(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
}
// Enumerate devices.
if (SUCCEEDED(hr))
{
hr = MFEnumDeviceSources(pAttributes.get(), &m_ppDevices, &m_numberDevices);
}
if (FAILED(hr))
{
return hr;
}
m_deviceFriendlyNames = new (std::nothrow) wchar_t*[m_numberDevices];
for (UINT32 i = 0; i < m_numberDevices; i++)
{
UINT32 nameLength = 0;
m_ppDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &m_deviceFriendlyNames[i], &nameLength);
}
return hr;
}
HRESULT VideoCaptureDeviceList::GetDevice(UINT32 index, IMFActivate** ppActivate)
{
if (index >= Count())
{
return E_INVALIDARG;
}
*ppActivate = m_ppDevices[index];
(*ppActivate)->AddRef();
return S_OK;
}
std::wstring_view VideoCaptureDeviceList::GetDeviceName(UINT32 index)
{
if (index >= Count())
{
return {};
}
return m_deviceFriendlyNames[index];
}

View File

@@ -0,0 +1,33 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <mfobjects.h>
#include <string_view>
class VideoCaptureDeviceList
{
UINT32 m_numberDevices;
// TODO: use wil
IMFActivate** m_ppDevices = nullptr;
wchar_t** m_deviceFriendlyNames = nullptr;
public:
VideoCaptureDeviceList() :
m_ppDevices(NULL), m_numberDevices(0)
{
}
~VideoCaptureDeviceList()
{
Clear();
}
UINT32 Count() const { return m_numberDevices; }
void Clear();
HRESULT EnumerateDevices();
HRESULT GetDevice(UINT32 index, IMFActivate** ppActivate);
std::wstring_view GetDeviceName(UINT32 index);
};

View File

@@ -95,19 +95,23 @@
<ClInclude Include="..\common.h" />
<ClInclude Include="..\keyboard_layout.h" />
<ClInclude Include="..\keyboard_layout_impl.h" />
<ClInclude Include="..\MicrophoneDevice.h" />
<ClInclude Include="..\os-detect.h" />
<ClInclude Include="..\pch.h" />
<ClInclude Include="..\two_way_pipe_message_ipc.h" />
<ClInclude Include="..\two_way_pipe_message_ipc_impl.h" />
<ClInclude Include="..\VideoCaptureDeviceList.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\common.cpp" />
<ClCompile Include="..\keyboard_layout.cpp" />
<ClCompile Include="..\MicrophoneDevice.cpp" />
<ClCompile Include="..\os-detect.cpp" />
<ClCompile Include="..\pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\two_way_pipe_message_ipc.cpp" />
<ClCompile Include="..\VideoCaptureDeviceList.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -36,6 +36,12 @@
<ClInclude Include="..\os-detect.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\MicrophoneDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\VideoCaptureDeviceList.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\keyboard_layout.cpp">
@@ -53,6 +59,12 @@
<ClCompile Include="..\os-detect.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\MicrophoneDevice.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\VideoCaptureDeviceList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -49,7 +49,7 @@ bool is_system_window(HWND hwnd, const char* class_name)
int run_message_loop(const bool until_idle, const std::optional<uint32_t> timeout_seconds)
{
MSG msg;
MSG msg{};
bool stop = false;
UINT_PTR timerId = 0;
if (timeout_seconds.has_value())

View File

@@ -91,6 +91,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</MultiProcessorCompilation>
<ConformanceMode Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ConformanceMode>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -106,6 +107,7 @@
<AdditionalIncludeDirectories>inc;telemetry;..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/await %(AdditionalOptions)</AdditionalOptions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -132,6 +134,8 @@
<ClInclude Include="keyboard_layout.h" />
<ClInclude Include="keyboard_layout_impl.h" />
<ClInclude Include="LowlevelKeyboardEvent.h" />
<ClInclude Include="MicrophoneDevice.h" />
<ClInclude Include="naming.h" />
<ClInclude Include="notifications.h" />
<ClInclude Include="processApi.h" />
<ClInclude Include="RcResource.h" />
@@ -141,7 +145,9 @@
<ClInclude Include="string_utils.h" />
<ClInclude Include="timeutil.h" />
<ClInclude Include="two_way_pipe_message_ipc.h" />
<ClInclude Include="user.h" />
<ClInclude Include="VersionHelper.h" />
<ClInclude Include="VideoCaptureDeviceList.h" />
<ClInclude Include="window_helpers.h" />
<ClInclude Include="icon_helpers.h" />
<ClInclude Include="json.h" />
@@ -170,7 +176,9 @@
<ClCompile Include="dpi_aware.cpp" />
<ClCompile Include="json.cpp" />
<ClCompile Include="keyboard_layout.cpp" />
<ClCompile Include="MicrophoneDevice.cpp" />
<ClCompile Include="monitors.cpp" />
<ClCompile Include="naming.cpp" />
<ClCompile Include="notifications.cpp" />
<ClCompile Include="on_thread_executor.cpp" />
<ClCompile Include="os-detect.cpp" />
@@ -185,9 +193,11 @@
<ClCompile Include="start_visible.cpp" />
<ClCompile Include="tasklist_positions.cpp" />
<ClCompile Include="common.cpp" />
<ClCompile Include="user.cpp" />
<ClCompile Include="version.cpp" />
<ClCompile Include="two_way_pipe_message_ipc.cpp" />
<ClCompile Include="VersionHelper.cpp" />
<ClCompile Include="VideoCaptureDeviceList.cpp" />
<ClCompile Include="windows_colors.cpp" />
<ClCompile Include="window_helpers.cpp" />
<ClCompile Include="winstore.cpp" />

View File

@@ -141,6 +141,18 @@
<ClInclude Include="string_utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="naming.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="user.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MicrophoneDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VideoCaptureDeviceList.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="d2d_svg.cpp">
@@ -222,6 +234,18 @@
<ClCompile Include="comUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="naming.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="user.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="MicrophoneDevice.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VideoCaptureDeviceList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -1,4 +1,167 @@
#include "pch.h"
#include "interop.h"
#include <msclr\marshal.h>
#include <msclr\marshal_cppstd.h>
#include <functional>
#include "..\keyboard_layout.h"
#include "..\two_way_pipe_message_ipc.h"
#include "..\common.h"
#include "..\shared_constants.h"
#include "..\os-detect.h"
#pragma warning(push)
#pragma warning(disable : 4793) // some functions must be compiled as native
#include "..\MicrophoneDevice.h"
#include "..\VideoCaptureDeviceList.h"
#pragma warning(pop)
using namespace System;
using namespace System::Runtime::InteropServices;
using System::Collections::Generic::List;
//https://docs.microsoft.com/en-us/cpp/dotnet/how-to-wrap-native-class-for-use-by-csharp?view=vs-2019
namespace interop
{
public
ref class LayoutMapManaged
{
public:
LayoutMapManaged() :
_map(new LayoutMap) {}
~LayoutMapManaged()
{
delete _map;
}
String ^ GetKeyName(DWORD key) {
return gcnew String(_map->GetKeyName(key).c_str());
}
void Updatelayout()
{
_map->UpdateLayout();
}
protected:
!LayoutMapManaged()
{
delete _map;
}
private:
LayoutMap* _map;
};
public
ref class TwoWayPipeMessageIPCManaged
{
public:
delegate void ReadCallback(String ^ message);
TwoWayPipeMessageIPCManaged(String ^ inputPipeName, String ^ outputPipeName, ReadCallback ^ callback)
{
_wrapperCallback = gcnew InternalReadCallback(this, &TwoWayPipeMessageIPCManaged::ReadCallbackHelper);
_callback = callback;
TwoWayPipeMessageIPC::callback_function cb = nullptr;
if (callback != nullptr)
{
cb = (TwoWayPipeMessageIPC::callback_function)(void*)Marshal::GetFunctionPointerForDelegate(_wrapperCallback);
}
_pipe = new TwoWayPipeMessageIPC(
msclr::interop::marshal_as<std::wstring>(inputPipeName),
msclr::interop::marshal_as<std::wstring>(outputPipeName),
cb);
}
~TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
void Send(String ^ msg)
{
_pipe->send(msclr::interop::marshal_as<std::wstring>(msg));
}
void Start()
{
_pipe->start(nullptr);
}
void End()
{
_pipe->end();
}
protected:
!TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
private:
delegate void InternalReadCallback(const std::wstring& msg);
TwoWayPipeMessageIPC* _pipe;
ReadCallback ^ _callback;
InternalReadCallback ^ _wrapperCallback;
void ReadCallbackHelper(const std::wstring& msg)
{
_callback(gcnew String(msg.c_str()));
}
};
public
ref class CommonManaged
{
public:
static String ^ GetProductVersion() {
return gcnew String(get_product_version().c_str());
}
static bool ShouldNewSettingsBeUsed()
{
return UseNewSettings();
}
static List<String ^> ^ GetAllActiveMicrophoneDeviceNames() {
auto names = gcnew List<String ^>();
for (const auto& device : MicrophoneDevice::getAllActive())
{
names->Add(gcnew String(device.name().data()));
}
return names;
}
static List<String ^> ^
GetAllVideoCaptureDeviceNames() {
auto names = gcnew List<String ^>();
VideoCaptureDeviceList vcdl;
vcdl.EnumerateDevices();
for (UINT32 i = 0; i < vcdl.Count(); ++i)
{
auto name = gcnew String(vcdl.GetDeviceName(i).data());
if (name != L"PowerToys VideoConference")
{
names->Add(name);
}
}
return names;
}
};
public
ref class Constants
{
public:
literal int VK_WIN_BOTH = CommonSharedConstants::VK_WIN_BOTH;
static String ^ PowerLauncherSharedEvent() {
return gcnew String(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT);
}
};
}

View File

@@ -1,136 +0,0 @@
#pragma once
#include <msclr\marshal.h>
#include <msclr\marshal_cppstd.h>
#include <functional>
#include "..\keyboard_layout.h"
#include "..\two_way_pipe_message_ipc.h"
#include "..\common.h"
#include "..\shared_constants.h"
#include "..\os-detect.h"
using namespace System;
using namespace System::Runtime::InteropServices;
//https://docs.microsoft.com/en-us/cpp/dotnet/how-to-wrap-native-class-for-use-by-csharp?view=vs-2019
namespace interop
{
public
ref class LayoutMapManaged
{
public:
LayoutMapManaged() :
_map(new LayoutMap) {}
~LayoutMapManaged()
{
delete _map;
}
String ^ GetKeyName(DWORD key) {
return gcnew String(_map->GetKeyName(key).c_str());
}
void Updatelayout()
{
_map->UpdateLayout();
}
protected:
!LayoutMapManaged()
{
delete _map;
}
private:
LayoutMap* _map;
};
public
ref class TwoWayPipeMessageIPCManaged
{
public:
delegate void ReadCallback(String ^ message);
TwoWayPipeMessageIPCManaged(String ^ inputPipeName, String ^ outputPipeName, ReadCallback ^ callback)
{
_wrapperCallback = gcnew InternalReadCallback(this, &TwoWayPipeMessageIPCManaged::ReadCallbackHelper);
_callback = callback;
TwoWayPipeMessageIPC::callback_function cb = nullptr;
if (callback != nullptr)
{
cb = (TwoWayPipeMessageIPC::callback_function)(void*)Marshal::GetFunctionPointerForDelegate(_wrapperCallback);
}
_pipe = new TwoWayPipeMessageIPC(
msclr::interop::marshal_as<std::wstring>(inputPipeName),
msclr::interop::marshal_as<std::wstring>(outputPipeName),
cb);
}
~TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
void Send(String ^ msg)
{
_pipe->send(msclr::interop::marshal_as<std::wstring>(msg));
}
void Start()
{
_pipe->start(nullptr);
}
void End()
{
_pipe->end();
}
protected:
!TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
private:
delegate void InternalReadCallback(const std::wstring& msg);
TwoWayPipeMessageIPC* _pipe;
ReadCallback ^ _callback;
InternalReadCallback ^ _wrapperCallback;
void ReadCallbackHelper(const std::wstring& msg)
{
_callback(gcnew String(msg.c_str()));
}
};
public
ref class CommonManaged
{
public:
static String^ GetProductVersion()
{
return gcnew String(get_product_version().c_str());
}
static bool ShouldNewSettingsBeUsed()
{
return UseNewSettings();
}
};
public
ref class Constants
{
public:
literal int VK_WIN_BOTH = CommonSharedConstants::VK_WIN_BOTH;
static String^ PowerLauncherSharedEvent()
{
return gcnew String(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT);
}
};
}

View File

@@ -23,6 +23,7 @@
<Keyword>ManagedCProj</Keyword>
<RootNamespace>interop</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
<ProjectName>PowerToysInterop</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
@@ -73,7 +74,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>WindowsApp.lib;Mf.lib;%(AdditionalDependencies)</AdditionalDependencies>
<DelayLoadDLLs>
</DelayLoadDLLs>
</Link>
@@ -87,7 +88,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>WindowsApp.lib;Mf.lib;%(AdditionalDependencies)</AdditionalDependencies>
<DelayLoadDLLs>
</DelayLoadDLLs>
</Link>
@@ -127,7 +128,6 @@
</Target>
<ItemGroup>
<ClInclude Include="HotkeyManager.h" />
<ClInclude Include="interop.h" />
<ClInclude Include="KeyboardHook.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
@@ -149,6 +149,9 @@
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common-md-flag\common-md-flag.vcxproj">
<Project>{985b3f2f-ceed-4c0a-a249-69257e719145}</Project>
@@ -156,5 +159,12 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<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')" />
</ImportGroup>
<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'))" />
</Target>
</Project>

View File

@@ -15,9 +15,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="interop.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>

View File

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

View File

@@ -72,3 +72,23 @@ MonitorInfo MonitorInfo::GetPrimaryMonitor()
EnumDisplayMonitors(NULL, NULL, GetPrimaryDisplayEnumCb, reinterpret_cast<LPARAM>(&primary));
return primary;
}
MonitorInfo MonitorInfo::GetFromWindow(HWND hwnd)
{
auto monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
return GetFromHandle(monitor);
}
MonitorInfo MonitorInfo::GetFromPoint(POINT p)
{
auto monitor = MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST);
return GetFromHandle(monitor);
}
MonitorInfo MonitorInfo::GetFromHandle(HMONITOR monitor)
{
MONITORINFOEX monitor_info;
monitor_info.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(monitor, &monitor_info);
return MonitorInfo(monitor, monitor_info.rcWork);
}

View File

@@ -33,6 +33,9 @@ struct MonitorInfo : ScreenSize
// Returns monitor rects ordered from left to right
static std::vector<MonitorInfo> GetMonitors(bool includeNonWorkingArea);
static MonitorInfo GetPrimaryMonitor();
static MonitorInfo GetFromWindow(HWND hwnd);
static MonitorInfo GetFromPoint(POINT p);
static MonitorInfo GetFromHandle(HMONITOR monitor);
};
bool operator==(const ScreenSize& lhs, const ScreenSize& rhs);

20
src/common/naming.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include "pch.h"
#include "naming.h"
#include "user.h"
std::wstring ObtainStableGlobalNameForKernelObject(const std::wstring_view name, const bool restricted)
{
static const std::optional<std::wstring> username = ObtainActiveUserName();
std::wstring result = L"Global\\";
if (restricted)
{
result += L"Restricted\\";
}
if (username)
{
result += *username;
}
result += name;
return result;
}

5
src/common/naming.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#include <string_view>
#include <string>
std::wstring ObtainStableGlobalNameForKernelObject(const std::wstring_view name, const bool restricted);

View File

@@ -1,7 +1,12 @@
#define NOMINMAX
#include <Windows.h>
#include <Unknwn.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <Windows.h>
#include <wil/resource.h>
#include <wil/com.h>
#include <dxgi1_3.h>
#include <d3d11_2.h>
#include <d2d1_3.h>

View File

@@ -7,6 +7,7 @@
namespace PTSettingsHelper
{
std::wstring get_module_save_folder_location(std::wstring_view powertoy_name);
std::wstring get_module_save_file_location(std::wstring_view powertoy_name);
std::wstring get_root_save_folder_location();
void save_module_settings(std::wstring_view powertoy_name, json::JsonObject& settings);

View File

@@ -203,7 +203,7 @@ namespace PowerToysSettings
if (output_bytes == 1 && output[0] >= 'a' && output[0] <= 'z')
{
// Make Latin letters keys capital, as it looks better
output[0] = toupper(output[0]);
output[0] = static_cast<wchar_t>(toupper(output[0]));
}
return output.data();
}

View File

@@ -26,6 +26,13 @@ namespace timeutil
return std::nullopt;
}
}
inline time_t from_filetime(const FILETIME& ft)
{
ULARGE_INTEGER ull;
ull.LowPart = ft.dwLowDateTime;
ull.HighPart = ft.dwHighDateTime;
return ull.QuadPart / 10000000ULL - 11644473600ULL;
}
inline std::time_t now()
{

View File

@@ -50,7 +50,7 @@ namespace updating
{
auto current_version_to_next_version = VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
current_version_to_next_version += L" -> ";
current_version_to_next_version += info.version_string;
current_version_to_next_version += info.version.toWstring();
return current_version_to_next_version;
}
@@ -70,14 +70,14 @@ namespace updating
::notifications::show_toast_with_activations(std::move(contents),
TOAST_TITLE,
{},
{ ::notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://download_and_install_update/" }, ::notifications::link_button{ GITHUB_NEW_VERSION_MORE_INFO, info.release_page_uri.ToString().c_str() } },
{ ::notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://download_and_install_update/" }, ::notifications::link_button{ GITHUB_NEW_VERSION_MORE_INFO, info.release_page_url.ToString().c_str() } },
std::move(toast_params));
}
void show_download_start(const updating::new_version_download_info& info)
{
::notifications::progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string };
std::wstring progress_title{ info.version.toWstring() };
progress_title += L' ';
progress_title += localized_strings::DOWNLOAD_IN_PROGRESS;
@@ -99,7 +99,7 @@ namespace updating
::notifications::show_toast_with_activations(std::move(contents),
TOAST_TITLE,
{},
{ ::notifications::link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } },
{ ::notifications::link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_url.ToString().c_str() } },
std::move(toast_params));
}
@@ -111,7 +111,7 @@ namespace updating
::notifications::show_toast_with_activations(std::move(contents),
TOAST_TITLE,
{},
{ ::notifications::link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } },
{ ::notifications::link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_url.ToString().c_str() } },
std::move(toast_params));
}
@@ -139,7 +139,7 @@ namespace updating
{
::notifications::progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string };
std::wstring progress_title{ info.version.toWstring() };
progress_title += L' ';
progress_title += progress < 1 ? localized_strings::DOWNLOAD_IN_PROGRESS : localized_strings::DOWNLOAD_COMPLETE;
progress_bar_params.progress_title = progress_title;

View File

@@ -25,6 +25,7 @@ namespace
const wchar_t POWERTOYS_EXE_COMPONENT[] = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
const wchar_t DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH[] = L"delete_previous_powertoys_confirm";
const wchar_t LATEST_RELEASE_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
const wchar_t ALL_RELEASES_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases";
const wchar_t MSIX_PACKAGE_NAME[] = L"Microsoft.PowerToys";
const wchar_t MSIX_PACKAGE_PUBLISHER[] = L"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US";
@@ -100,55 +101,107 @@ namespace updating
return false;
}
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async()
using winrt::Windows::Foundation::Uri;
std::optional<std::pair<Uri, std::wstring>> extract_installer_asset_download_info(const json::JsonObject& release_object)
{
try
{
http::HttpClient client;
const auto body = co_await client.request(winrt::Windows::Foundation::Uri{ LATEST_RELEASE_ENDPOINT });
auto json_body = json::JsonValue::Parse(body).GetObjectW();
auto new_version = json_body.GetNamedString(L"tag_name");
winrt::Windows::Foundation::Uri release_page_uri{ json_body.GetNamedString(L"html_url") };
VersionHelper github_version(winrt::to_string(new_version));
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
if (github_version > current_version)
const std::wstring_view required_architecture = get_architecture_string(get_current_architecture());
constexpr const std::wstring_view required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN;
// Desc-sorted by its priority
const std::array<std::wstring_view, 2> asset_extensions = { L".exe", L".msi" };
for (const auto asset_extension : asset_extensions)
{
const std::wstring_view required_architecture = get_architecture_string(get_current_architecture());
constexpr const std::wstring_view required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN;
// Desc-sorted by its priority
const std::array<std::wstring_view, 2> asset_extensions = { L".exe", L".msi" };
for (const auto asset_extension : asset_extensions)
for (auto asset_elem : release_object.GetNamedArray(L"assets"))
{
for (auto asset_elem : json_body.GetNamedArray(L"assets"))
{
auto asset{ asset_elem.GetObjectW() };
std::wstring filename_lower = asset.GetNamedString(L"name", {}).c_str();
std::transform(begin(filename_lower), end(filename_lower), begin(filename_lower), ::towlower);
auto asset{ asset_elem.GetObjectW() };
std::wstring filename_lower = asset.GetNamedString(L"name", {}).c_str();
std::transform(begin(filename_lower), end(filename_lower), begin(filename_lower), ::towlower);
const bool extension_matched = filename_lower.ends_with(asset_extension);
const bool architecture_matched = filename_lower.find(required_architecture) != std::wstring::npos;
const bool filename_matched = filename_lower.find(required_filename_pattern) != std::wstring::npos;
if (extension_matched && architecture_matched && filename_matched)
{
winrt::Windows::Foundation::Uri msi_download_url{ asset.GetNamedString(L"browser_download_url") };
co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str(), std::move(msi_download_url), std::move(filename_lower) };
}
const bool extension_matched = filename_lower.ends_with(asset_extension);
const bool architecture_matched = filename_lower.find(required_architecture) != std::wstring::npos;
const bool filename_matched = filename_lower.find(required_filename_pattern) != std::wstring::npos;
const bool asset_matched = extension_matched && architecture_matched && filename_matched;
if (extension_matched && architecture_matched && filename_matched)
{
return std::make_pair(Uri{ asset.GetNamedString(L"browser_download_url") }, std::move(filename_lower));
}
}
}
else
{
co_return std::nullopt;
}
}
catch (...)
{
co_return std::nullopt;
}
return std::nullopt;
}
std::optional<VersionHelper> extract_version_from_relase_object(const json::JsonObject& release_object)
{
try
{
return VersionHelper{ winrt::to_string(release_object.GetNamedString(L"tag_name")) };
}
catch (...)
{
}
return std::nullopt;
}
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async(const bool prerelease)
{
try
{
http::HttpClient client;
json::JsonObject release_object;
const VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
VersionHelper github_version = current_version;
if (prerelease)
{
const auto body = co_await client.request(Uri{ ALL_RELEASES_ENDPOINT });
for (const auto& json : json::JsonValue::Parse(body).GetArray())
{
auto potential_release_object = json.GetObjectW();
const bool is_prerelease = potential_release_object.GetNamedBoolean(L"prerelease", false);
auto extracted_version = extract_version_from_relase_object(potential_release_object);
if (!is_prerelease || !extracted_version || *extracted_version <= github_version)
{
continue;
}
github_version = std::move(*extracted_version);
release_object = std::move(potential_release_object);
}
}
else
{
const auto body = co_await client.request(Uri{ LATEST_RELEASE_ENDPOINT });
release_object = json::JsonValue::Parse(body).GetObjectW();
auto extracted_version = extract_version_from_relase_object(release_object);
if (!extracted_version)
{
co_return std::nullopt;
}
github_version = std::move(*extracted_version);
}
if (github_version > current_version)
{
Uri release_page_url{ release_object.GetNamedString(L"html_url") };
auto installer_download_url = extract_installer_asset_download_info(release_object);
if (installer_download_url.has_value())
{
co_return new_version_download_info{ std::move(release_page_url),
std::move(github_version),
std::move(installer_download_url->first),
std::move(installer_download_url->second) };
}
}
}
catch (...)
{
}
co_return std::nullopt;
}
std::future<bool> uninstall_previous_msix_version_async()
{
winrt::Windows::Management::Deployment::PackageManager package_manager;
@@ -237,19 +290,6 @@ namespace updating
}
}
std::future<std::wstring> check_new_version_available()
{
const auto new_version = co_await get_new_github_version_info_async();
if (!new_version)
{
updating::notifications::show_unavailable();
co_return VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
}
updating::notifications::show_available(new_version.value());
co_return new_version->version_string;
}
std::future<std::wstring> download_update()
{
const auto new_version = co_await get_new_github_version_info_async();

View File

@@ -20,17 +20,17 @@ namespace updating
struct new_version_download_info
{
winrt::Windows::Foundation::Uri release_page_uri;
std::wstring version_string;
winrt::Windows::Foundation::Uri release_page_url;
VersionHelper version;
winrt::Windows::Foundation::Uri installer_download_url;
std::wstring installer_filename;
};
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async();
// TODO(yuyoyuppe): !! when merging to master, we must set the default value to false !!
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async(const bool prerelease = true);
std::future<void> try_autoupdate(const bool download_updates_automatically);
std::filesystem::path get_pending_updates_path();
std::future<std::wstring> check_new_version_available();
std::future<std::wstring> download_update();
// non-localized

20
src/common/user.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include "pch.h"
#include "user.h"
#include <wtsapi32.h>
std::optional<std::wstring> ObtainActiveUserName()
{
const DWORD sessionId = WTSGetActiveConsoleSessionId();
WCHAR* pUserName;
DWORD _ = 0;
if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, &pUserName, &_))
{
return std::nullopt;
}
WTSGetActiveConsoleSessionId();
std::wstring result{ pUserName };
WTSFreeMemory(pUserName);
return result;
}

9
src/common/user.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include <optional>
#include <string>
#include <string_view>
std::optional<std::wstring> ObtainActiveUserName();
std::wstring ObtainStableGlobalNameForKernelObject(const std::wstring_view name, const bool restricted);

View File

@@ -80,6 +80,22 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
}
}
private bool videoConference = true;
[JsonPropertyName("Video Conference")]
public bool VideoConference
{
get => this.videoConference;
set
{
if (this.videoConference != value)
{
LogTelemetryEvent(value);
this.videoConference = value;
}
}
}
private bool powerRename = true;
public bool PowerRename

View File

@@ -48,4 +48,10 @@
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\Telemetry.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Forms.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
{
public class SndVideoConferenceSettings
{
[JsonPropertyName("Video Conference")]
public VideoConferenceSettings VideoConference { get; set; }
public SndVideoConferenceSettings(VideoConferenceSettings settings)
{
VideoConference = settings;
}
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
}
}
}

View File

@@ -29,5 +29,10 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
{
return JsonSerializer.Serialize(this);
}
public static implicit operator StringProperty(string v)
{
return new StringProperty(v);
}
}
}

View File

@@ -0,0 +1,86 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
{
public class VideoConferenceConfigProperties
{
public VideoConferenceConfigProperties()
{
this.MuteCameraAndMicrophoneHotkey = new KeyboardKeysProperty(
new HotkeySettings()
{
Win = true,
Ctrl = false,
Alt = false,
Shift = false,
Key = "N",
Code = 78,
});
this.MuteMicrophoneHotkey = new KeyboardKeysProperty(
new HotkeySettings()
{
Win = true,
Ctrl = false,
Alt = false,
Shift = true,
Key = "A",
Code = 65,
});
this.MuteCameraHotkey = new KeyboardKeysProperty(
new HotkeySettings()
{
Win = true,
Ctrl = false,
Alt = false,
Shift = true,
Key = "O",
Code = 79,
});
this.HideToolbarWhenUnmuted = new BoolProperty(true);
}
[JsonPropertyName("mute_camera_and_microphone_hotkey")]
public KeyboardKeysProperty MuteCameraAndMicrophoneHotkey { get; set; }
[JsonPropertyName("mute_microphone_hotkey")]
public KeyboardKeysProperty MuteMicrophoneHotkey { get; set; }
[JsonPropertyName("mute_camera_hotkey")]
public KeyboardKeysProperty MuteCameraHotkey { get; set; }
[JsonPropertyName("selected_camera")]
public StringProperty SelectedCamera { get; set; } = string.Empty;
[JsonPropertyName("selected_mic")]
public StringProperty SelectedMicrophone { get; set; } = string.Empty;
[JsonPropertyName("toolbar_position")]
public StringProperty ToolbarPosition { get; set; } = "Top right corner";
[JsonPropertyName("toolbar_monitor")]
public StringProperty ToolbarMonitor { get; set; } = "Main monitor";
[JsonPropertyName("camera_overlay_image_path")]
public StringProperty CameraOverlayImagePath { get; set; } = string.Empty;
[JsonPropertyName("theme")]
public StringProperty Theme { get; set; }
[JsonPropertyName("hide_toolbar_when_unmuted")]
public BoolProperty HideToolbarWhenUnmuted { get; set; }
// converts the current to a json string.
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
}
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
namespace Microsoft.PowerToys.Settings.UI.Lib
{
public class VideoConferenceSettings : BasePTModuleSettings, ISettingsConfig
{
public VideoConferenceSettings()
{
Version = "1";
Name = "Video Conference";
Properties = new VideoConferenceConfigProperties();
}
[JsonPropertyName("properties")]
public VideoConferenceConfigProperties Properties { get; set; }
public string GetModuleName()
{
return Name;
}
bool ISettingsConfig.UpgradeSettingsConfiguration()
{
return false;
}
}
}

View File

@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Lib
{
public class VideoConferenceSettingsIPCMessage
{
[JsonPropertyName("powertoys")]
public SndVideoConferenceSettings Powertoys { get; set; }
public VideoConferenceSettingsIPCMessage()
{
}
public VideoConferenceSettingsIPCMessage(SndVideoConferenceSettings settings)
{
this.Powertoys = settings;
}
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
}
}
}

View File

@@ -0,0 +1,414 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.PowerToys.Settings.UI.Lib;
using Microsoft.PowerToys.Settings.UI.Lib.Helpers;
using Microsoft.PowerToys.Settings.UI.Lib.Interface;
using Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands;
namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
public class VideoConferenceViewModel : Observable
{
private readonly ISettingsUtils _settingsUtils;
private VideoConferenceSettings Settings { get; set; }
private GeneralSettings GeneralSettingsConfig { get; set; }
private const string ModuleName = "Video Conference";
private Func<string, int> SendConfigMSG { get; }
private string _settingsConfigFileFolder = string.Empty;
public VideoConferenceViewModel(ISettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc, string configFileSubfolder = "")
{
GeneralSettingsConfig = settingsRepository.SettingsConfig;
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
SendConfigMSG = ipcMSGCallBackFunc;
_settingsConfigFileFolder = configFileSubfolder;
try
{
Settings = _settingsUtils.GetSettings<VideoConferenceSettings>(GetSettingsSubPath());
}
catch
{
Settings = new VideoConferenceSettings();
_settingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath());
}
CameraNames = interop.CommonManaged.GetAllVideoCaptureDeviceNames();
MicrophoneNames = interop.CommonManaged.GetAllActiveMicrophoneDeviceNames();
MicrophoneNames.Insert(0, "[All]");
var shouldSaveSettings = false;
if (Settings.Properties.SelectedCamera.Value == string.Empty && CameraNames.Count != 0)
{
_selectedCameraIndex = 0;
Settings.Properties.SelectedCamera.Value = CameraNames[0];
shouldSaveSettings = true;
}
else
{
_selectedCameraIndex = CameraNames.FindIndex(name => name == Settings.Properties.SelectedCamera.Value);
}
if (Settings.Properties.SelectedMicrophone.Value == string.Empty)
{
_selectedMicrophoneIndex = 0;
Settings.Properties.SelectedMicrophone.Value = MicrophoneNames[0];
shouldSaveSettings = true;
}
else
{
_selectedMicrophoneIndex = MicrophoneNames.FindIndex(name => name == Settings.Properties.SelectedMicrophone.Value);
}
_isEnabled = GeneralSettingsConfig.Enabled.VideoConference;
_cameraAndMicrophoneMuteHotkey = Settings.Properties.MuteCameraAndMicrophoneHotkey.Value;
_mirophoneMuteHotkey = Settings.Properties.MuteMicrophoneHotkey.Value;
_cameraMuteHotkey = Settings.Properties.MuteCameraHotkey.Value;
CameraImageOverlayPath = Settings.Properties.CameraOverlayImagePath.Value;
SelectOverlayImage = new ButtonClickCommand(SelectOverlayImageAction);
ClearOverlayImage = new ButtonClickCommand(ClearOverlayImageAction);
_hideToolbarWhenUnmuted = Settings.Properties.HideToolbarWhenUnmuted.Value;
switch (Settings.Properties.ToolbarPosition.Value)
{
case "Top left corner":
_toolbarPositionIndex = 0;
break;
case "Top center":
_toolbarPositionIndex = 1;
break;
case "Top right corner":
_toolbarPositionIndex = 2;
break;
case "Bottom left corner":
_toolbarPositionIndex = 3;
break;
case "Bottom center":
_toolbarPositionIndex = 4;
break;
case "Bottom right corner":
_toolbarPositionIndex = 5;
break;
}
switch (Settings.Properties.ToolbarMonitor.Value)
{
case "Main monitor":
_toolbarMonitorIndex = 0;
break;
case "All monitors":
_toolbarMonitorIndex = 1;
break;
}
if (shouldSaveSettings)
{
_settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName);
}
}
private bool _isEnabled = false;
private int _toolbarPositionIndex;
private int _toolbarMonitorIndex;
private HotkeySettings _cameraAndMicrophoneMuteHotkey;
private HotkeySettings _mirophoneMuteHotkey;
private HotkeySettings _cameraMuteHotkey;
private int _selectedCameraIndex = -1;
private int _selectedMicrophoneIndex = 0;
private bool _hideToolbarWhenUnmuted;
public List<string> CameraNames { get; }
public List<string> MicrophoneNames { get; }
public string CameraImageOverlayPath { get; set; }
public ButtonClickCommand SelectOverlayImage { get; set; }
public ButtonClickCommand ClearOverlayImage { get; set; }
private void ClearOverlayImageAction()
{
CameraImageOverlayPath = string.Empty;
Settings.Properties.CameraOverlayImagePath = string.Empty;
RaisePropertyChanged("CameraImageOverlayPath");
}
private void SelectOverlayImageAction()
{
try
{
string pickedImage = null;
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "Image Files (*.jpeg;*.jpg;*.png)|*.jpeg;*.jpg;*.png";
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
pickedImage = openFileDialog.FileName;
}
}
if (pickedImage != null)
{
CameraImageOverlayPath = pickedImage;
Settings.Properties.CameraOverlayImagePath = pickedImage;
RaisePropertyChanged("CameraImageOverlayPath");
}
}
catch
{
}
}
public int SelectedCameraIndex
{
get
{
return _selectedCameraIndex;
}
set
{
if (_selectedCameraIndex != value)
{
_selectedCameraIndex = value;
if (_selectedCameraIndex >= 0 && _selectedCameraIndex < CameraNames.Count())
{
Settings.Properties.SelectedCamera.Value = CameraNames[_selectedCameraIndex];
RaisePropertyChanged();
}
}
}
}
public int SelectedMicrophoneIndex
{
get
{
return _selectedMicrophoneIndex;
}
set
{
if (_selectedMicrophoneIndex != value)
{
_selectedMicrophoneIndex = value;
if (_selectedMicrophoneIndex >= 0 && _selectedMicrophoneIndex < MicrophoneNames.Count())
{
Settings.Properties.SelectedMicrophone.Value = MicrophoneNames[_selectedMicrophoneIndex];
RaisePropertyChanged();
}
}
}
}
public bool IsEnabled
{
get
{
return _isEnabled;
}
set
{
if (value != _isEnabled)
{
_isEnabled = value;
GeneralSettingsConfig.Enabled.VideoConference = value;
OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig);
SendConfigMSG(snd.ToString());
OnPropertyChanged("IsEnabled");
}
}
}
public HotkeySettings CameraAndMicrophoneMuteHotkey
{
get
{
return _cameraAndMicrophoneMuteHotkey;
}
set
{
if (value != _cameraAndMicrophoneMuteHotkey)
{
_cameraAndMicrophoneMuteHotkey = value;
Settings.Properties.MuteCameraAndMicrophoneHotkey.Value = value;
RaisePropertyChanged("CameraAndMicrophoneMuteHotkey");
}
}
}
public HotkeySettings MicrophoneMuteHotkey
{
get
{
return _mirophoneMuteHotkey;
}
set
{
if (value != _mirophoneMuteHotkey)
{
_mirophoneMuteHotkey = value;
Settings.Properties.MuteMicrophoneHotkey.Value = value;
RaisePropertyChanged("MicrophoneMuteHotkey");
}
}
}
public HotkeySettings CameraMuteHotkey
{
get
{
return _cameraMuteHotkey;
}
set
{
if (value != _cameraMuteHotkey)
{
_cameraMuteHotkey = value;
Settings.Properties.MuteCameraHotkey.Value = value;
RaisePropertyChanged("CameraMuteHotkey");
}
}
}
public int ToolbarPostionIndex
{
get
{
return _toolbarPositionIndex;
}
set
{
if (_toolbarPositionIndex != value)
{
_toolbarPositionIndex = value;
switch (_toolbarPositionIndex)
{
case 0:
Settings.Properties.ToolbarPosition.Value = "Top left corner";
break;
case 1:
Settings.Properties.ToolbarPosition.Value = "Top center";
break;
case 2:
Settings.Properties.ToolbarPosition.Value = "Top right corner";
break;
case 3:
Settings.Properties.ToolbarPosition.Value = "Bottom left corner";
break;
case 4:
Settings.Properties.ToolbarPosition.Value = "Bottom center";
break;
case 5:
Settings.Properties.ToolbarPosition.Value = "Bottom right corner";
break;
}
RaisePropertyChanged("ToolbarPostionIndex");
}
}
}
public int ToolbarMonitorIndex
{
get
{
return _toolbarMonitorIndex;
}
set
{
if (_toolbarMonitorIndex != value)
{
_toolbarMonitorIndex = value;
switch (_toolbarMonitorIndex)
{
case 0:
Settings.Properties.ToolbarMonitor.Value = "Main monitor";
break;
case 1:
Settings.Properties.ToolbarMonitor.Value = "All monitors";
break;
}
RaisePropertyChanged("ToolbarMonitorIndex");
}
}
}
public bool HideToolbarWhenUnmuted
{
get
{
return _hideToolbarWhenUnmuted;
}
set
{
if (value != _hideToolbarWhenUnmuted)
{
_hideToolbarWhenUnmuted = value;
Settings.Properties.HideToolbarWhenUnmuted.Value = value;
RaisePropertyChanged("HideToolbarWhenUnmuted");
}
}
}
public string GetSettingsSubPath()
{
return _settingsConfigFileFolder + "\\" + ModuleName;
}
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(propertyName);
SndVideoConferenceSettings outsettings = new SndVideoConferenceSettings(Settings);
SndModuleSettings<SndVideoConferenceSettings> ipcMessage = new SndModuleSettings<SndVideoConferenceSettings>(outsettings);
SendConfigMSG(ipcMessage.ToJsonString());
}
}
[ComImport]
[Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IInitializeWithWindow
{
void Initialize(IntPtr hwnd);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -141,6 +141,9 @@
<Compile Include="Views\ShortcutGuidePage.xaml.cs">
<DependentUpon>ShortcutGuidePage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\VideoConference.xaml.cs">
<DependentUpon>VideoConference.xaml</DependentUpon>
</Compile>
<Compile Include="Views\VisibleIfNotEmpty.cs" />
</ItemGroup>
<ItemGroup>
@@ -159,6 +162,7 @@
<Content Include="Assets\Modules\PowerRename.png" />
<Content Include="Assets\Modules\PT.png" />
<Content Include="Assets\Modules\ShortcutGuide.png" />
<Content Include="Assets\Modules\VideoConference.png" />
<Content Include="Assets\SplashScreen.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
@@ -275,6 +279,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\VideoConference.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\codeAnalysis\StyleCop.json">
@@ -298,12 +306,6 @@
<EnableTypeInfoReflection>false</EnableTypeInfoReflection>
<EnableXBindDiagnostics>false</EnableXBindDiagnostics>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|Win32'">
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|Win32'">
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -141,6 +141,10 @@
<value>PowerRename</value>
<comment>Navigation view item name for PowerRename</comment>
</data>
<data name="Shell_VideoConference.Content" xml:space="preserve">
<value>Video Conference Mute</value>
<comment>Navigation view item name for Video Conference</comment>
</data>
<data name="Shell_ShortcutGuide.Content" xml:space="preserve">
<value>Shortcut Guide</value>
<comment>Navigation view item name for Shortcut Guide</comment>
@@ -492,6 +496,70 @@
<data name="ShortcutGuide_Theme.Text" xml:space="preserve">
<value>Choose Shortcut Guide overlay color</value>
</data>
<data name="VideoConference_Enable.Header" xml:space="preserve">
<value>Enable Video Conference</value>
</data>
<data name="VideoConference_Description.Text" xml:space="preserve">
<value>Video Conference Mute is a quick and easy way to do an global "mute" of both your microphone and webcam.
Disabling this module or closing PowerToys will unmute the microphone and camera.</value>
</data>
<data name="VideoConference_CameraAndMicrophoneMuteHotkeyControl_Header.Header" xml:space="preserve">
<value>Mute camera and microphone</value>
</data>
<data name="VideoConference_MicrophoneMuteHotkeyControl_Header.Header" xml:space="preserve">
<value>Mute microphone</value>
</data>
<data name="VideoConference_CameraMuteHotkeyControl_Header.Header" xml:space="preserve">
<value>Mute camera</value>
</data>
<data name="VideoConference_SelectedCamera.Header" xml:space="preserve">
<value>Selected camera</value>
</data>
<data name="VideoConference_SelectedMicrophone.Header" xml:space="preserve">
<value>Selected microphone</value>
</data>
<data name="VideoConference_CameraOverlayImagePathHeader.Text" xml:space="preserve">
<value>Camera overlay image</value>
</data>
<data name="VideoConference_ToolbarPosition.Header" xml:space="preserve">
<value>Toolbar postion</value>
</data>
<data name="VideoConference_ToolbarPosition_TopCenter.Content" xml:space="preserve">
<value>Top center</value>
</data>
<data name="VideoConference_ToolbarPosition_TopLeftCorner.Content" xml:space="preserve">
<value>Top left corner</value>
</data>
<data name="VideoConference_ToolbarPosition_TopRightCorner.Content" xml:space="preserve">
<value>Top right corner</value>
</data>
<data name="VideoConference_ToolbarPosition_BottomLeftCorner.Content" xml:space="preserve">
<value>Bottom left corner</value>
</data>
<data name="VideoConference_ToolbarPosition_BottomCenter.Content" xml:space="preserve">
<value>Bottom center</value>
</data>
<data name="VideoConference_ToolbarPosition_BottomRightCorner.Content" xml:space="preserve">
<value>Bottom right corner</value>
</data>
<data name="VideoConference_ToolbarMonitor.Header" xml:space="preserve">
<value>Show toolbar on</value>
</data>
<data name="VideoConference_ToolbarMonitor_Main.Content" xml:space="preserve">
<value>Main monitor</value>
</data>
<data name="VideoConference_ToolbarMonitor_UnderCursor.Content" xml:space="preserve">
<value>Monitor under cursor</value>
</data>
<data name="VideoConference_ToolbarMonitor_ActiveWindow.Content" xml:space="preserve">
<value>Active window monitor</value>
</data>
<data name="VideoConference_ToolbarMonitor_All.Content" xml:space="preserve">
<value>All monitors</value>
</data>
<data name="VideoConference_HideToolbarWhenUnmuted.Content" xml:space="preserve">
<value>Hide toolbar when both camera and microphone are unmuted</value>
</data>
<data name="ImageResizer_CustomSizes.Text" xml:space="preserve">
<value>Image sizes</value>
</data>
@@ -675,6 +743,9 @@
<data name="Shortcut_Guide_Image.AutomationProperties.Name" xml:space="preserve">
<value>Shortcut Guide</value>
</data>
<data name="About_VideoConference.Text" xml:space="preserve">
<value>About Video Conference</value>
</data>
<data name="General_Repository.Text" xml:space="preserve">
<value>GitHub repository</value>
</data>
@@ -747,6 +818,18 @@
<data name="FancyZones_MoveWindowsBasedOnPositionCheckBoxControl.Content" xml:space="preserve">
<value>Move windows based on their position</value>
</data>
<data name="VideoConference_Camera.Text" xml:space="preserve">
<value>Camera</value>
</data>
<data name="VideoConference_Microphone.Text" xml:space="preserve">
<value>Microphone</value>
</data>
<data name="VideoConference_Toolbar.Text" xml:space="preserve">
<value>Toolbar</value>
</data>
<data name="VideoConference_Shortcuts.Text" xml:space="preserve">
<value>Shortcuts</value>
</data>
<data name="GeneralSettings_NewVersionIsAvailable" xml:space="preserve">
<value>New update available</value>
</data>
@@ -759,4 +842,13 @@
<data name="FileExplorerPreview_PreviewPane_GroupSettings.Text" xml:space="preserve">
<value>Preview Pane</value>
</data>
<data name="VideoConference_CameraOverlayImageAlt.AutomationProperties.Name" xml:space="preserve">
<value>Camera overlay image preview</value>
</data>
<data name="VideoConference_CameraOverlayImageBrowse.Content" xml:space="preserve">
<value>Browse</value>
</data>
<data name="VideoConference_CameraOverlayImageClear.Content" xml:space="preserve">
<value>Clear</value>
</data>
</root>

View File

@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
@@ -36,6 +37,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
set { Set(ref isBackEnabled, value); }
}
public bool IsVideoConferenceBuild
{
get
{
return File.Exists("modules/VideoConference/VideoConferenceModule.dll");
}
}
public WinUI.NavigationViewItem Selected
{
get { return selected; }

View File

@@ -88,6 +88,13 @@
<FontIcon Glyph="&#xEDA7;"/>
</winui:NavigationViewItem.Icon>
</winui:NavigationViewItem>
<winui:NavigationViewItem x:Uid="Shell_VideoConference" helpers:NavHelper.NavigateTo="views:VideoConferencePage" IsEnabled="{x:Bind ViewModel.IsVideoConferenceBuild, Mode=OneWay}">
<winui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE714;"/>
</winui:NavigationViewItem.Icon>
</winui:NavigationViewItem>
</winui:NavigationView.MenuItems>
<i:Interaction.Behaviors>
<behaviors:NavigationViewHeaderBehavior
@@ -116,4 +123,4 @@
</ScrollViewer>
</winui:NavigationView>
</Grid>
</UserControl>
</UserControl>

View File

@@ -0,0 +1,207 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.Views.VideoConferencePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:CustomControls="using:Microsoft.PowerToys.Settings.UI.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid RowSpacing="{StaticResource DefaultRowSpacing}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="LayoutVisualStates">
<VisualState x:Name="WideLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource WideLayoutMinWidth}" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="SmallLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource SmallLayoutMinWidth}" />
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SidePanel.(Grid.Column)" Value="0"/>
<Setter Target="SidePanel.Width" Value="Auto"/>
<Setter Target="VideoConferenceView.(Grid.Row)" Value="1" />
<Setter Target="LinksPanel.(RelativePanel.RightOf)" Value="AboutImage"/>
<Setter Target="LinksPanel.(RelativePanel.AlignTopWith)" Value="AboutImage"/>
<Setter Target="AboutImage.Margin" Value="0,12,12,0"/>
<Setter Target="AboutTitle.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" x:Name="VideoConferenceView">
<ToggleSwitch x:Uid="VideoConference_Enable"
IsOn="{ Binding Mode=TwoWay, Path=IsEnabled}"
/>
<TextBlock x:Uid="VideoConference_Shortcuts"
Style="{StaticResource SettingsGroupTitleStyle}"
Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/>
<CustomControls:HotkeySettingsControl
x:Uid="VideoConference_CameraAndMicrophoneMuteHotkeyControl_Header"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
Keys="Win, Ctrl, Alt, Shift"
HotkeySettings="{x:Bind Path=ViewModel.CameraAndMicrophoneMuteHotkey, Mode=TwoWay}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"
/>
<CustomControls:HotkeySettingsControl
x:Uid="VideoConference_MicrophoneMuteHotkeyControl_Header"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
Keys="Win, Ctrl, Alt, Shift"
HotkeySettings="{x:Bind Path=ViewModel.MicrophoneMuteHotkey, Mode=TwoWay}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"
/>
<CustomControls:HotkeySettingsControl
x:Uid="VideoConference_CameraMuteHotkeyControl_Header"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
Keys="Win, Ctrl, Alt, Shift"
HotkeySettings="{x:Bind Path=ViewModel.CameraMuteHotkey, Mode=TwoWay}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"
/>
<TextBlock x:Uid="VideoConference_Microphone"
Style="{StaticResource SettingsGroupTitleStyle}"
Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/>
<ComboBox x:Uid="VideoConference_SelectedMicrophone"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
SelectedIndex="{Binding Path=SelectedMicrophoneIndex, Mode=TwoWay}"
ItemsSource="{Binding MicrophoneNames, Mode=OneTime}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"/>
<TextBlock x:Uid="VideoConference_Camera"
Style="{StaticResource SettingsGroupTitleStyle}"
Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/>
<ComboBox x:Uid="VideoConference_SelectedCamera"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
SelectedIndex="{Binding Path=SelectedCameraIndex, Mode=TwoWay}"
ItemsSource="{Binding CameraNames, Mode=OneTime}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"/>
<TextBlock x:Uid="VideoConference_CameraOverlayImagePathHeader"
Margin="{StaticResource SmallTopMargin}"/>
<Border CornerRadius="4"
HorizontalAlignment="Left"
Margin="{StaticResource XXSmallTopMargin}">
<Image Width="240"
x:Uid="VideoConference_CameraOverlayImageAlt"
ToolTipService.ToolTip="{Binding Mode=OneWay, Path=CameraImageOverlayPath}"
Source="{Binding Mode=OneWay, Path=CameraImageOverlayPath}"/>
</Border>
<StackPanel Orientation="Horizontal"
Padding="0"
Spacing="8"
Margin="{StaticResource SmallTopMargin}">
<Button Height="32"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"
x:Uid="VideoConference_CameraOverlayImageBrowse"
Command="{Binding Mode=OneWay, Path=SelectOverlayImage}"
HorizontalContentAlignment="Left"
HorizontalAlignment="Left" />
<Button IsEnabled="{Binding Mode=TwoWay, Path=IsEnabled}"
Height="32"
x:Uid="VideoConference_CameraOverlayImageClear"
Command="{Binding Mode=OneWay, Path=ClearOverlayImage}"
HorizontalAlignment="Left" />
</StackPanel>
<TextBlock x:Uid="VideoConference_Toolbar"
Style="{StaticResource SettingsGroupTitleStyle}"
Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/>
<ComboBox x:Uid="VideoConference_ToolbarPosition"
MinWidth="240"
Margin="{StaticResource SmallTopMargin}"
SelectedIndex="{ Binding Mode=TwoWay, Path=ToolbarPostionIndex}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}">
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_TopLeftCorner"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_TopCenter"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_TopRightCorner"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_BottomLeftCorner"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_BottomCenter"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_BottomRightCorner"/>
</ComboBox>
<ComboBox x:Uid="VideoConference_ToolbarMonitor"
MinWidth="240"
Margin="{StaticResource SmallTopMargin}"
SelectedIndex="{ Binding Mode=TwoWay, Path=ToolbarMonitorIndex}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}">
<ComboBoxItem x:Uid="VideoConference_ToolbarMonitor_Main"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarMonitor_All"/>
</ComboBox>
<CheckBox x:Uid="VideoConference_HideToolbarWhenUnmuted"
IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.HideToolbarWhenUnmuted}"
Margin="{StaticResource SmallTopMargin}"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}"
/>
</StackPanel>
<RelativePanel x:Name="SidePanel"
HorizontalAlignment="Left"
Width="{StaticResource SidePanelWidth}"
Grid.Column="1">
<StackPanel x:Name="DescriptionPanel">
<TextBlock x:Uid="About_VideoConference"
x:Name="AboutTitle"
Grid.ColumnSpan="2"
Style="{StaticResource SettingsGroupTitleStyle}"
Margin="{StaticResource XSmallBottomMargin}"/>
<TextBlock x:Uid="VideoConference_Description"
TextWrapping="Wrap"
Grid.Row="1" />
</StackPanel>
<Border x:Name="AboutImage"
CornerRadius="4"
Grid.Row="2"
MaxWidth="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopBottomMargin}"
RelativePanel.Below="DescriptionPanel">
<Image Source="ms-appx:///Assets/Modules/VideoConference.png" />
</Border>
<StackPanel x:Name="LinksPanel"
Margin="0,1,0,0"
RelativePanel.Below="AboutImage"
Orientation="Vertical" >
<HyperlinkButton NavigateUri="https://aka.ms/PowerToysOverview_VideoConference">
<TextBlock x:Uid="Module_overview" />
</HyperlinkButton>
<HyperlinkButton NavigateUri="https://aka.ms/powerToysGiveFeedback">
<TextBlock x:Uid="Give_Feedback" />
</HyperlinkButton>
</StackPanel>
</RelativePanel>
</Grid>
</Page>

View File

@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.Lib;
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using Microsoft.PowerToys.Settings.UI.ViewModels;
using Windows.UI.Xaml.Controls;
namespace Microsoft.PowerToys.Settings.UI.Views
{
public sealed partial class VideoConferencePage : Page
{
private VideoConferenceViewModel ViewModel { get; set; }
public VideoConferencePage()
{
var settingsUtils = new SettingsUtils(new SystemIOProvider());
ViewModel = new VideoConferenceViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel;
InitializeComponent();
}
}
}

15
src/hasWDK.props Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Choose>
<When Condition="Exists('$(VCTargetsPath)\Microsoft.Cpp.WDK.props')">
<PropertyGroup>
<HasWDK>true</HasWDK>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<HasWDK>false</HasWDK>
</PropertyGroup>
</Otherwise>
</Choose>
</Project>

View File

@@ -127,8 +127,6 @@ private:
size_t m_keyCycle{};
static const UINT m_showAnimationDuration = 200; // ms
static const UINT m_flashDuration = 700; // ms
ULONG_PTR gdiplusToken;
};
ZoneWindow::ZoneWindow(HINSTANCE hinstance)
@@ -140,14 +138,10 @@ ZoneWindow::ZoneWindow(HINSTANCE hinstance)
wcex.lpszClassName = NonLocalizable::ToolWindowClassName;
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
RegisterClassExW(&wcex);
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
}
ZoneWindow::~ZoneWindow()
{
Gdiplus::GdiplusShutdown(gdiplusToken);
}
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId, bool flashZones)

View File

@@ -1,4 +1,4 @@
#include <pch.h>
#include "pch.h"
#include "registry_wrapper.h"
namespace PowerPreviewSettings

View File

@@ -1,5 +1,4 @@
#pragma once
#include <pch.h>
#include <string>
#include "Generated Files/resource.h"
#include <settings_objects.h>

View File

@@ -0,0 +1,220 @@
#include <dxgiformat.h>
#include <assert.h>
#include <winrt/base.h>
#pragma warning(push)
#pragma warning(disable : 4005)
#include <wincodec.h>
#pragma warning(pop)
#include <memory>
#include <mfapi.h>
#include <shcore.h>
#include <algorithm>
#include <wil/resource.h>
#include "ImageLoader.h"
#include "Logging.h"
IWICImagingFactory* _GetWIC() noexcept
{
static IWICImagingFactory* s_Factory = nullptr;
if (s_Factory)
return s_Factory;
HRESULT hr = CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
(LPVOID*)&s_Factory);
if (FAILED(hr))
{
LogToFile(std::string("_GetWIC() failed with code: " + hr));
s_Factory = nullptr;
return nullptr;
}
return s_Factory;
}
using Microsoft::WRL::ComPtr;
ComPtr<IMFSample> LoadImageAsSample(ComPtr<IStream> imageStream, IMFMediaType* sampleMediaType) noexcept
{
HRESULT hr = S_OK;
// Get target sample frame dimensions
UINT targetWidth = 0;
UINT targetHeight = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFGetAttributeSize(sampleMediaType, MF_MT_FRAME_SIZE, &targetWidth, &targetHeight));
IWICImagingFactory* pWIC = _GetWIC();
if (!pWIC)
{
LogToFile("Failed to create IWICImagingFactory");
return nullptr;
}
// Initialize image bitmap decoder from filename and get the image frame
ComPtr<IWICBitmapDecoder> bitmapDecoder;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(pWIC->CreateDecoderFromStream(imageStream.Get(), nullptr, WICDecodeMetadataCacheOnLoad, &bitmapDecoder));
ComPtr<IWICBitmapFrameDecode> decodedFrame;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(bitmapDecoder->GetFrame(0, &decodedFrame));
UINT imageWidth = 0, imageHeight = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(decodedFrame->GetSize(&imageWidth, &imageHeight));
// Scale the image if required
ComPtr<IWICBitmapSource> sourceImageFrame;
if (targetWidth != imageWidth || targetHeight != imageHeight)
{
ComPtr<IWICBitmapScaler> scaler;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(pWIC->CreateBitmapScaler(&scaler));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(scaler->Initialize(decodedFrame.Get(), targetWidth, targetHeight, WICBitmapInterpolationModeHighQualityCubic));
sourceImageFrame.Attach(scaler.Detach());
}
else
{
sourceImageFrame.Attach(decodedFrame.Detach());
}
MFT_REGISTER_TYPE_INFO outputFilter = { MFMediaType_Video, {} };
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(sampleMediaType->GetGUID(MF_MT_SUBTYPE, &outputFilter.guidSubtype));
ComPtr<IWICBitmapEncoder> jpgEncoder;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(pWIC->CreateEncoder(
outputFilter.guidSubtype == MFVideoFormat_RGB24 ? GUID_ContainerFormatBmp : GUID_ContainerFormatJpeg, nullptr, &jpgEncoder));
// Prepare the encoder output memory stream and encoding params
ComPtr<IStream> jpgStream;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(CreateStreamOnHGlobal(nullptr, true, &jpgStream));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgEncoder->Initialize(jpgStream.Get(), WICBitmapEncoderNoCache));
ComPtr<IWICBitmapFrameEncode> jpgFrame;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgEncoder->CreateNewFrame(&jpgFrame, nullptr));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->Initialize(nullptr));
WICPixelFormatGUID jpgFormat = GUID_WICPixelFormat24bppBGR;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->SetPixelFormat(&jpgFormat));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->SetSize(targetWidth, targetHeight));
// Commit the image encoding
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->WriteSource(sourceImageFrame.Get(), nullptr));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->Commit());
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgEncoder->Commit());
// Obtain stream size and lock its memory pointer
STATSTG jpgStreamStat{};
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgStream->Stat(&jpgStreamStat, STATFLAG_NONAME));
const size_t jpgStreamSize = jpgStreamStat.cbSize.QuadPart;
HGLOBAL streamMemoryHandle{};
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(GetHGlobalFromStream(jpgStream.Get(), &streamMemoryHandle));
auto jpgStreamMemory = static_cast<uint8_t*>(GlobalLock(streamMemoryHandle));
auto unlockJpgStreamMemory = wil::scope_exit([jpgStreamMemory] { GlobalUnlock(jpgStreamMemory); });
// Create a sample from the input image buffer
ComPtr<IMFSample> inputSample;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateSample(&inputSample));
IMFMediaBuffer* inputMediaBuffer = nullptr;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateAlignedMemoryBuffer(static_cast<DWORD>(jpgStreamSize), MF_64_BYTE_ALIGNMENT, &inputMediaBuffer));
BYTE* inputBuf = nullptr;
DWORD max_length = 0, current_length = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(inputMediaBuffer->Lock(&inputBuf, &max_length, &current_length));
if (max_length < jpgStreamSize)
{
LogToFile("max_length < jpgStreamSize");
return nullptr;
}
std::copy(jpgStreamMemory, jpgStreamMemory + jpgStreamSize, inputBuf);
unlockJpgStreamMemory.reset();
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(inputMediaBuffer->Unlock());
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(inputMediaBuffer->SetCurrentLength(static_cast<DWORD>(jpgStreamSize)));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(inputSample->AddBuffer(inputMediaBuffer));
// Now we are ready to convert it to the requested media type, so we need to find a suitable jpg encoder
MFT_REGISTER_TYPE_INFO inputFilter = { MFMediaType_Video, outputFilter.guidSubtype == MFVideoFormat_RGB24 ? MFVideoFormat_RGB24 : MFVideoFormat_MJPG };
// But if no conversion is needed, just return the input sample
if (!memcmp(&inputFilter.guidSubtype, &outputFilter.guidSubtype, sizeof(GUID)))
{
return inputSample;
}
IMFActivate** ppVDActivate = nullptr;
UINT32 count = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, MFT_ENUM_FLAG_SYNCMFT, &inputFilter, &outputFilter, &ppVDActivate, &count));
ComPtr<IMFTransform> videoDecoder;
bool videoDecoderActivated = false;
for (UINT32 i = 0; i < count; ++i)
{
if (!videoDecoderActivated && !FAILED(ppVDActivate[i]->ActivateObject(IID_PPV_ARGS(&videoDecoder))))
{
videoDecoderActivated = true;
}
ppVDActivate[i]->Release();
}
if (count)
{
CoTaskMemFree(ppVDActivate);
}
if (!videoDecoderActivated)
{
LogToFile("No converter avialable for the selected format");
return nullptr;
}
auto shutdownVideoDecoder = wil::scope_exit([&videoDecoder] { MFShutdownObject(videoDecoder.Get()); });
// Set input/output types for the decoder
ComPtr<IMFMediaType> jpgFrameMediaType;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateMediaType(&jpgFrameMediaType));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrameMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrameMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_MJPG));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrameMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrameMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFSetAttributeSize(jpgFrameMediaType.Get(), MF_MT_FRAME_SIZE, targetWidth, targetHeight));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFSetAttributeRatio(jpgFrameMediaType.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->SetInputType(0, jpgFrameMediaType.Get(), 0));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->SetOutputType(0, sampleMediaType, 0));
// Process the input sample
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->ProcessInput(0, inputSample.Get(), 0));
// Check whether we need to allocate output sample and buffer ourselves
MFT_OUTPUT_STREAM_INFO outputStreamInfo{};
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->GetOutputStreamInfo(0, &outputStreamInfo));
const bool onlyProvidesSamples = outputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES;
const bool canProvideSamples = outputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES;
const bool mustAllocateSample = (!onlyProvidesSamples && !canProvideSamples) || (!onlyProvidesSamples && (outputStreamInfo.dwFlags & MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER));
MFT_OUTPUT_DATA_BUFFER outputSamples{};
IMFSample* outputSample = nullptr;
// If so, do the allocation
if (mustAllocateSample)
{
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateSample(&outputSample));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(outputSample->SetSampleDuration(333333));
IMFMediaBuffer* outputMediaBuffer = nullptr;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateAlignedMemoryBuffer(outputStreamInfo.cbSize, outputStreamInfo.cbAlignment - 1, &outputMediaBuffer));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(outputMediaBuffer->SetCurrentLength(outputStreamInfo.cbSize));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(outputSample->AddBuffer(outputMediaBuffer));
outputSamples.pSample = outputSample;
}
// Finally, produce the output sample
DWORD processStatus = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->ProcessOutput(0, 1, &outputSamples, &processStatus));
if (outputSamples.pEvents)
{
outputSamples.pEvents->Release();
}
return outputSamples.pSample;
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <memory>
#include <optional>
#include <wrl/client.h>
#include <Mfidl.h>
#pragma warning(push)
#pragma warning(disable : 4005)
#include <stdint.h>
#pragma warning(pop)
Microsoft::WRL::ComPtr<IMFSample> LoadImageAsSample(Microsoft::WRL::ComPtr<IStream> imageStream, IMFMediaType* outputSampleMediaType) noexcept;

View File

@@ -0,0 +1,524 @@
#include "stdafx.h"
#include "SimpleMediaSource.h"
#include "SimpleMediaStream.h"
#include "Logging.h"
HRESULT
SimpleMediaSource::RuntimeClassInitialize()
{
VERBOSE_LOG;
HRESULT hr = S_OK;
RETURN_IF_FAILED_WITH_LOGGING(MFCreateAttributes(&_spAttributes, 10));
RETURN_IF_FAILED_WITH_LOGGING(MFCreateEventQueue(&_spEventQueue));
RETURN_IF_FAILED_WITH_LOGGING(MakeAndInitialize<SimpleMediaStream>(&_stream, this));
{
ComPtr<IMFStreamDescriptor> streamDescriptor(_stream.Get()->_spStreamDesc.Get());
RETURN_IF_FAILED_WITH_LOGGING(MFCreatePresentationDescriptor(1, streamDescriptor.GetAddressOf(), &_spPresentationDescriptor));
}
_wasStreamPreviouslySelected = false;
_sourceState = SourceState::Stopped;
return hr;
}
// IMFMediaEventGenerator methods.
IFACEMETHODIMP
SimpleMediaSource::BeginGetEvent(
_In_ IMFAsyncCallback* pCallback,
_In_ IUnknown* punkState)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->BeginGetEvent(pCallback, punkState));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::EndGetEvent(
_In_ IMFAsyncResult* pResult,
_COM_Outptr_ IMFMediaEvent** ppEvent)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->EndGetEvent(pResult, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::GetEvent(
DWORD dwFlags,
_COM_Outptr_ IMFMediaEvent** ppEvent)
{
// NOTE:
// GetEvent can block indefinitely, so we don't hold the lock.
// This requires some juggling with the event queue pointer.
HRESULT hr = S_OK;
ComPtr<IMFMediaEventQueue> spQueue;
{
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
spQueue = _spEventQueue;
}
// Now get the event.
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->GetEvent(dwFlags, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::QueueEvent(
MediaEventType eventType,
REFGUID guidExtendedType,
HRESULT hrStatus,
_In_opt_ PROPVARIANT const* pvValue)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamVar(eventType, guidExtendedType, hrStatus, pvValue));
return hr;
}
// IMFMediaSource methods
IFACEMETHODIMP
SimpleMediaSource::CreatePresentationDescriptor(
_COM_Outptr_ IMFPresentationDescriptor** ppPresentationDescriptor)
{
VERBOSE_LOG;
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (ppPresentationDescriptor == nullptr)
{
return E_POINTER;
}
*ppPresentationDescriptor = nullptr;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spPresentationDescriptor->Clone(ppPresentationDescriptor));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::GetCharacteristics(
_Out_ DWORD* pdwCharacteristics)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (nullptr == pdwCharacteristics)
{
return E_POINTER;
}
*pdwCharacteristics = 0;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
*pdwCharacteristics = MFMEDIASOURCE_IS_LIVE;
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::Pause()
{
// Pause() not required/needed
return MF_E_INVALID_STATE_TRANSITION;
}
IFACEMETHODIMP
SimpleMediaSource::Shutdown()
{
VERBOSE_LOG;
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
_sourceState = SourceState::Shutdown;
_spAttributes.Reset();
_spPresentationDescriptor.Reset();
if (_spEventQueue != nullptr)
{
_spEventQueue->Shutdown();
_spEventQueue.Reset();
}
if (_stream != nullptr)
{
_stream.Get()->Shutdown();
_stream.Reset();
}
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::Start(
_In_ IMFPresentationDescriptor* pPresentationDescriptor,
_In_opt_ const GUID* pguidTimeFormat,
_In_ const PROPVARIANT* pvarStartPos)
{
VERBOSE_LOG;
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
DWORD count = 0;
PROPVARIANT startTime;
BOOL selected = false;
ComPtr<IMFStreamDescriptor> streamDesc;
DWORD streamIndex = 0;
if (pPresentationDescriptor == nullptr || pvarStartPos == nullptr)
{
return E_INVALIDARG;
}
else if (pguidTimeFormat != nullptr && *pguidTimeFormat != GUID_NULL)
{
return MF_E_UNSUPPORTED_TIME_FORMAT;
}
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
if (!(_sourceState != SourceState::Stopped || _sourceState != SourceState::Shutdown))
{
return MF_E_INVALID_STATE_TRANSITION;
}
_sourceState = SourceState::Started;
// This checks the passed in PresentationDescriptor matches the member of streams we
// have defined internally and that at least one stream is selected
RETURN_IF_FAILED_WITH_LOGGING(_ValidatePresentationDescriptor(pPresentationDescriptor));
RETURN_IF_FAILED_WITH_LOGGING(pPresentationDescriptor->GetStreamDescriptorCount(&count));
RETURN_IF_FAILED_WITH_LOGGING(InitPropVariantFromInt64(MFGetSystemTime(), &startTime));
// Send event that the source started. Include error code in case it failed.
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamVar(MESourceStarted,
GUID_NULL,
hr,
&startTime));
// We're hardcoding this to the first descriptor
// since this sample is a single stream sample. For
// multiple streams, we need to walk the list of streams
// and for each selected stream, send the MEUpdatedStream
// or MENewStream event along with the MEStreamStarted
// event.
RETURN_IF_FAILED_WITH_LOGGING(pPresentationDescriptor->GetStreamDescriptorByIndex(0,
&selected,
&streamDesc));
RETURN_IF_FAILED_WITH_LOGGING(streamDesc->GetStreamIdentifier(&streamIndex));
if (streamIndex >= 1)
{
return MF_E_INVALIDSTREAMNUMBER;
}
if (selected)
{
ComPtr<IUnknown> spunkStream;
MediaEventType met = (_wasStreamPreviouslySelected ? MEUpdatedStream : MENewStream);
// Update our internal PresentationDescriptor
RETURN_IF_FAILED_WITH_LOGGING(_spPresentationDescriptor->SelectStream(streamIndex));
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->SetStreamState(MF_STREAM_STATE_RUNNING));
RETURN_IF_FAILED_WITH_LOGGING(_stream.As(&spunkStream));
// Send the MEUpdatedStream/MENewStream to our source event
// queue.
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamUnk(met,
GUID_NULL,
S_OK,
spunkStream.Get()));
// But for our stream started (MEStreamStarted), we post to our
// stream event queue.
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->QueueEvent(MEStreamStarted,
GUID_NULL,
S_OK,
&startTime));
}
_wasStreamPreviouslySelected = selected;
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::Stop()
{
VERBOSE_LOG;
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
PROPVARIANT stopTime;
DWORD count = 0;
MF_STREAM_STATE state;
if (_sourceState != SourceState::Started)
{
return MF_E_INVALID_STATE_TRANSITION;
}
_sourceState = SourceState::Stopped;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(InitPropVariantFromInt64(MFGetSystemTime(), &stopTime));
RETURN_IF_FAILED_WITH_LOGGING(_spPresentationDescriptor->GetStreamDescriptorCount(&count));
// Deselect the streams and send the stream stopped events.
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->GetStreamState(&state));
_wasStreamPreviouslySelected = (state == MF_STREAM_STATE_RUNNING);
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->SetStreamState(MF_STREAM_STATE_STOPPED));
_spPresentationDescriptor->DeselectStream(0);
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->QueueEvent(MEStreamStopped, GUID_NULL, hr, &stopTime));
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamVar(MESourceStopped, GUID_NULL, hr, &stopTime));
return hr;
}
// IMFMediaSourceEx
IFACEMETHODIMP
SimpleMediaSource::GetSourceAttributes(
_COM_Outptr_ IMFAttributes** sourceAttributes)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (nullptr == sourceAttributes)
{
return E_POINTER;
}
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
*sourceAttributes = nullptr;
if (_spAttributes.Get() == nullptr)
{
ComPtr<IMFSensorProfileCollection> profileCollection;
ComPtr<IMFSensorProfile> profile;
// Create our source attribute store.
RETURN_IF_FAILED_WITH_LOGGING(MFCreateAttributes(_spAttributes.GetAddressOf(), 1));
// Create an empty profile collection...
RETURN_IF_FAILED_WITH_LOGGING(MFCreateSensorProfileCollection(&profileCollection));
// In this example since we have just one stream, we only have one
// pin to add: Pin0.
// Legacy profile is mandatory. This is to ensure non-profile
// aware applications can still function, but with degraded
// feature sets.
RETURN_IF_FAILED_WITH_LOGGING(MFCreateSensorProfile(KSCAMERAPROFILE_Legacy, 0, nullptr, profile.ReleaseAndGetAddressOf()));
RETURN_IF_FAILED_WITH_LOGGING(profile->AddProfileFilter(0, L"((RES==;FRT<=30,1;SUT==))"));
RETURN_IF_FAILED_WITH_LOGGING(profileCollection->AddProfile(profile.Get()));
// High Frame Rate profile will only allow >=60fps.
RETURN_IF_FAILED_WITH_LOGGING(MFCreateSensorProfile(KSCAMERAPROFILE_HighFrameRate, 0, nullptr, profile.ReleaseAndGetAddressOf()));
RETURN_IF_FAILED_WITH_LOGGING(profile->AddProfileFilter(0, L"((RES==;FRT>=60,1;SUT==))"));
RETURN_IF_FAILED_WITH_LOGGING(profileCollection->AddProfile(profile.Get()));
// Se the profile collection to the attribute store of the IMFTransform.
RETURN_IF_FAILED_WITH_LOGGING(_spAttributes->SetUnknown(MF_DEVICEMFT_SENSORPROFILE_COLLECTION,
profileCollection.Get()));
}
return _spAttributes.CopyTo(sourceAttributes);
}
IFACEMETHODIMP
SimpleMediaSource::GetStreamAttributes(
DWORD dwStreamIdentifier,
_COM_Outptr_ IMFAttributes** ppAttributes)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (ppAttributes == nullptr)
{
return E_POINTER;
}
*ppAttributes = nullptr;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
if (dwStreamIdentifier >= 1)
{
return MF_E_INVALIDSTREAMNUMBER;
}
else
{
*ppAttributes = _stream.Get()->_spAttributes.Get();
(*ppAttributes)->AddRef();
}
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::SetD3DManager(
_In_opt_ IUnknown* /*pManager*/
)
{
// Return code is ignored by the frame work, this is a
// best effort attempt to inform the media source of the
// DXGI manager to use if DX surface support is available.
return E_NOTIMPL;
}
struct __declspec(uuid("a1f58958-a5aa-412f-af20-1b7f1242dba0")) IMFDeviceController;
// IMFGetService methods
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::GetService(
_In_ REFGUID,
_In_ REFIID iid,
_Out_ LPVOID* ppvObject)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
const bool wantsIMFDeviceController = IsEqualIID(iid, __uuidof(IMFDeviceController));
auto stream = _stream.Get();
const bool goingToRestart = wantsIMFDeviceController && _wasStreamPreviouslySelected && stream && stream->_isSelected && _sourceState == SourceState::Started;
if (goingToRestart)
{
// GetService w /IMFDeviceController is called when we're already started -> stopping to prepare for restart
stream->SetStreamState(MF_STREAM_STATE_STOPPED);
return E_POINTER;
}
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
if (!ppvObject)
{
return E_POINTER;
}
*ppvObject = nullptr;
// We have no supported service, just return
// MF_E_UNSUPPORTED_SERVICE for all calls.
return MF_E_UNSUPPORTED_SERVICE;
}
// IKsControl methods
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::KsProperty(
_In_reads_bytes_(ulPropertyLength) PKSPROPERTY,
_In_ ULONG,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID,
_In_ ULONG,
_Out_ ULONG*)
{
// ERROR_SET_NOT_FOUND is the standard error code returned
// by the AV Stream driver framework when a miniport
// driver does not register a handler for a KS operation.
// We want to mimic the driver behavior here if we don't
// support controls.
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::KsMethod(
_In_reads_bytes_(ulMethodLength) PKSMETHOD,
_In_ ULONG,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID,
_In_ ULONG,
_Out_ ULONG*)
{
Shutdown();
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::KsEvent(
_In_reads_bytes_opt_(ulEventLength) PKSEVENT,
_In_ ULONG,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID,
_In_ ULONG,
_Out_opt_ ULONG*)
{
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
/// Internal methods.
HRESULT
SimpleMediaSource::_CheckShutdownRequiresLock()
{
if (_sourceState == SourceState::Shutdown)
{
return MF_E_SHUTDOWN;
}
if (_spEventQueue == nullptr || _stream == nullptr)
{
return E_UNEXPECTED;
}
return S_OK;
}
HRESULT
SimpleMediaSource::_ValidatePresentationDescriptor(
_In_ IMFPresentationDescriptor* pPD)
{
HRESULT hr = S_OK;
DWORD cStreams = 0;
bool anySelected = false;
if (pPD == nullptr)
{
return E_INVALIDARG;
}
// The caller's PD must have the same number of streams as ours.
RETURN_IF_FAILED_WITH_LOGGING(pPD->GetStreamDescriptorCount(&cStreams));
if (SUCCEEDED(hr) && (cStreams != 1))
{
return E_INVALIDARG;
}
// The caller must select at least one stream.
for (UINT32 i = 0; i < cStreams; ++i)
{
ComPtr<IMFStreamDescriptor> spSD;
BOOL fSelected = FALSE;
DWORD dwId = 0;
RETURN_IF_FAILED_WITH_LOGGING(pPD->GetStreamDescriptorByIndex(i, &fSelected, &spSD));
anySelected |= !!fSelected;
RETURN_IF_FAILED_WITH_LOGGING(spSD->GetStreamIdentifier(&dwId));
if (dwId >= 1)
{
return E_INVALIDARG;
}
}
if (!anySelected)
{
return E_INVALIDARG;
}
return hr;
}

View File

@@ -0,0 +1,92 @@
#pragma once
#include "stdafx.h"
class SimpleMediaStream;
class __declspec(uuid("{8a6954dc-7baa-486b-a7a3-a3cc09246487}"))
SimpleMediaSource : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMFMediaEventGenerator, IMFMediaSource, IMFMediaSourceEx, IMFGetService, IKsControl>
{
enum class SourceState
{
Invalid,
Stopped,
Started,
Shutdown
};
public:
// IMFMediaEventGenerator
IFACEMETHOD(BeginGetEvent)
(_In_ IMFAsyncCallback* pCallback, _In_ IUnknown* punkState);
IFACEMETHOD(EndGetEvent)
(_In_ IMFAsyncResult* pResult, _Out_ IMFMediaEvent** ppEvent);
IFACEMETHOD(GetEvent)
(DWORD dwFlags, _Out_ IMFMediaEvent** ppEvent);
IFACEMETHOD(QueueEvent)
(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, _In_ const PROPVARIANT* pvValue);
// IMFMediaSource
IFACEMETHOD(CreatePresentationDescriptor)
(_Out_ IMFPresentationDescriptor** ppPresentationDescriptor);
IFACEMETHOD(GetCharacteristics)
(_Out_ DWORD* pdwCharacteristics);
IFACEMETHOD(Pause)
();
IFACEMETHOD(Shutdown)
();
IFACEMETHOD(Start)
(_In_ IMFPresentationDescriptor* pPresentationDescriptor, _In_ const GUID* pguidTimeFormat, _In_ const PROPVARIANT* pvarStartPosition);
IFACEMETHOD(Stop)
();
// IMFMediaSourceEx
IFACEMETHOD(GetSourceAttributes)
(_COM_Outptr_ IMFAttributes** ppAttributes);
IFACEMETHOD(GetStreamAttributes)
(DWORD dwStreamIdentifier, _COM_Outptr_ IMFAttributes** ppAttributes);
IFACEMETHOD(SetD3DManager)
(_In_opt_ IUnknown* pManager);
// IMFGetService
IFACEMETHOD(GetService)
(_In_ REFGUID guidService, _In_ REFIID riid, _Out_ LPVOID* ppvObject);
// IKsControl
IFACEMETHOD(KsProperty)
(_In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty,
_In_ ULONG ulPropertyLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pPropertyData,
_In_ ULONG ulDataLength,
_Out_ ULONG* pBytesReturned);
IFACEMETHOD(KsMethod)
(_In_reads_bytes_(ulMethodLength) PKSMETHOD pMethod,
_In_ ULONG ulMethodLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pMethodData,
_In_ ULONG ulDataLength,
_Out_ ULONG* pBytesReturned);
IFACEMETHOD(KsEvent)
(_In_reads_bytes_opt_(ulEventLength) PKSEVENT pEvent,
_In_ ULONG ulEventLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pEventData,
_In_ ULONG ulDataLength,
_Out_opt_ ULONG* pBytesReturned);
public:
HRESULT RuntimeClassInitialize();
private:
HRESULT _CheckShutdownRequiresLock();
HRESULT _ValidatePresentationDescriptor(_In_ IMFPresentationDescriptor* pPresentationDescriptor);
CriticalSection _critSec;
SourceState _sourceState{ SourceState::Invalid };
ComPtr<IMFMediaEventQueue> _spEventQueue;
ComPtr<IMFPresentationDescriptor> _spPresentationDescriptor;
ComPtr<IMFAttributes> _spAttributes;
bool _wasStreamPreviouslySelected; // maybe makes more sense as a property of the stream
ComPtr<SimpleMediaStream> _stream;
};
CoCreatableClass(SimpleMediaSource);

View File

@@ -0,0 +1,670 @@
#include "stdafx.h"
#include "ImageLoader.h"
#include <vector>
#include <algorithm>
#include <thread>
#include "SimpleMediaSource.h"
#include "SimpleMediaStream.h"
#include <common\user.h>
#include "Logging.h"
HRESULT CopyAttribute(IMFAttributes* pSrc, IMFAttributes* pDest, const GUID& key);
const static std::wstring_view MODULE_NAME = L"Video Conference";
const static std::wstring_view VIRTUAL_CAMERA_NAME = L"PowerToys VideoConference";
namespace
{
constexpr std::array<unsigned char, 3> blackColor = { 0, 0, 0 };
// clang-format off
unsigned char bmpPixelData[58] = {
0x42, 0x4D, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, blackColor[0], blackColor[1], blackColor[2], 0x00
};
// clang-format on
}
//-------------------------------------------------------------------
// CopyAttribute
//
// Copy an attribute value from one attribute store to another.
//-------------------------------------------------------------------
HRESULT CopyAttribute(IMFAttributes* pSrc, IMFAttributes* pDest, const GUID& key)
{
PROPVARIANT var;
PropVariantInit(&var);
HRESULT hr = S_OK;
hr = pSrc->GetItem(key, &var);
if (SUCCEEDED(hr))
{
hr = pDest->SetItem(key, var);
}
PropVariantClear(&var);
return hr;
}
bool areSame(double lhs, double rhs)
{
const double EPSILON = 0.00000001;
return (fabs(lhs - rhs) < EPSILON);
}
ComPtr<IMFMediaType> SelectBestMediaType(IMFSourceReader* reader)
{
VERBOSE_LOG;
std::vector<ComPtr<IMFMediaType>> supportedMediaTypes;
auto typeFramerate = [](IMFMediaType* type) {
UINT32 framerateNum = 0, framerateDenum = 1;
MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &framerateNum, &framerateDenum);
const float framerate = static_cast<float>(framerateNum) / framerateDenum;
return framerate;
};
bool is16by9RatioAvailable = false;
for (DWORD tyIdx = 0;; ++tyIdx)
{
IMFMediaType* nextType = nullptr;
HRESULT hr = reader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, tyIdx, &nextType);
if (!nextType)
{
break;
}
UINT32 width = 0;
UINT32 height = 0;
MFGetAttributeSize(nextType, MF_MT_FRAME_SIZE, &width, &height);
double aspectRatio = static_cast<double>(width) / height;
GUID subtype{};
nextType->GetGUID(MF_MT_SUBTYPE, &subtype);
//LogToFile(std::string("Available format: ") +
// toMediaTypeString(subtype) +
// std::string(", width=") +
// std::to_string(width) +
// std::string(", height=") +
// std::to_string(height) +
// std::string(", aspect ratio=") +
// std::to_string(aspectRatio));
if (subtype != MFVideoFormat_YUY2 &&
subtype != MFVideoFormat_RGB24 &&
subtype != MFVideoFormat_MJPG &&
subtype != MFVideoFormat_NV12)
{
continue;
}
if (areSame(aspectRatio, 16. / 9.))
{
is16by9RatioAvailable = true;
}
constexpr float minimalAcceptableFramerate = 15.f;
// Skip low frame types
if (typeFramerate(nextType) < minimalAcceptableFramerate)
{
continue;
}
supportedMediaTypes.emplace_back(nextType);
if (hr == MF_E_NO_MORE_TYPES || FAILED(hr))
{
break;
}
}
if (is16by9RatioAvailable)
{
// Remove all types with non 16 by 9 ratio
supportedMediaTypes.erase(std::remove_if(begin(supportedMediaTypes), end(supportedMediaTypes), [](ComPtr<IMFMediaType>& ptr) {
UINT32 width = 0, height = 0;
MFGetAttributeSize(ptr.Get(), MF_MT_FRAME_SIZE, &width, &height);
double ratio = static_cast<double>(width) / height;
return !areSame(ratio, 16. / 9.);
}),
end(supportedMediaTypes));
}
UINT64 maxResolution = 0;
for (auto& type : supportedMediaTypes)
{
UINT32 width = 0;
UINT32 height = 0;
MFGetAttributeSize(type.Get(), MF_MT_FRAME_SIZE, &width, &height);
const UINT64 curResolutionMult = static_cast<UINT64>(width) * height;
if (curResolutionMult >= maxResolution)
{
maxResolution = curResolutionMult;
}
}
// Remove all types with non-optimal resolution
supportedMediaTypes.erase(std::remove_if(begin(supportedMediaTypes), end(supportedMediaTypes), [maxResolution](ComPtr<IMFMediaType>& ptr) {
UINT32 w = 0, h = 0;
MFGetAttributeSize(ptr.Get(), MF_MT_FRAME_SIZE, &w, &h);
const UINT64 curResolutionMult = static_cast<UINT64>(w) * h;
return curResolutionMult != maxResolution;
}),
end(supportedMediaTypes));
// Desc-sort by frame_rate
std::sort(begin(supportedMediaTypes), end(supportedMediaTypes), [typeFramerate](ComPtr<IMFMediaType>& lhs, ComPtr<IMFMediaType>& rhs) {
return typeFramerate(lhs.Get()) > typeFramerate(rhs.Get());
});
return std::move(supportedMediaTypes[0]);
}
HRESULT
SimpleMediaStream::RuntimeClassInitialize(
_In_ SimpleMediaSource* pSource)
{
VERBOSE_LOG;
HRESULT hr = S_OK;
if (nullptr == pSource)
{
return E_INVALIDARG;
}
RETURN_IF_FAILED_WITH_LOGGING(pSource->QueryInterface(IID_PPV_ARGS(&_parent)));
const auto newSettings = SyncCurrentSettings();
UpdateSourceCamera(newSettings.newCameraName);
ComPtr<IStream> blackBMPImage = SHCreateMemStream(bmpPixelData, sizeof(bmpPixelData));
if (!blackBMPImage || !_spMediaType)
{
return E_FAIL;
}
if (_spMediaType)
{
if (newSettings.overlayImage)
{
_overlayImage = LoadImageAsSample(newSettings.overlayImage, _spMediaType.Get());
}
_blackImage = LoadImageAsSample(blackBMPImage, _spMediaType.Get());
}
return S_OK;
}
// IMFMediaEventGenerator
IFACEMETHODIMP
SimpleMediaStream::BeginGetEvent(
_In_ IMFAsyncCallback* pCallback,
_In_ IUnknown* punkState)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->BeginGetEvent(pCallback, punkState));
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::EndGetEvent(
_In_ IMFAsyncResult* pResult,
_COM_Outptr_ IMFMediaEvent** ppEvent)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->EndGetEvent(pResult, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::GetEvent(
DWORD dwFlags,
_COM_Outptr_ IMFMediaEvent** ppEvent)
{
// NOTE:
// GetEvent can block indefinitely, so we don't hold the lock.
// This requires some juggling with the event queue pointer.
HRESULT hr = S_OK;
ComPtr<IMFMediaEventQueue> spQueue;
{
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
spQueue = _spEventQueue;
}
// Now get the event.
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->GetEvent(dwFlags, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::QueueEvent(
MediaEventType eventType,
REFGUID guidExtendedType,
HRESULT hrStatus,
_In_opt_ PROPVARIANT const* pvValue)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamVar(eventType, guidExtendedType, hrStatus, pvValue));
return hr;
}
// IMFMediaStream
IFACEMETHODIMP
SimpleMediaStream::GetMediaSource(
_COM_Outptr_ IMFMediaSource** ppMediaSource)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (ppMediaSource == nullptr)
{
return E_POINTER;
}
*ppMediaSource = nullptr;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
*ppMediaSource = _parent.Get();
(*ppMediaSource)->AddRef();
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::GetStreamDescriptor(
_COM_Outptr_ IMFStreamDescriptor** ppStreamDescriptor)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (ppStreamDescriptor == nullptr)
{
return E_POINTER;
}
*ppStreamDescriptor = nullptr;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
if (_spStreamDesc != nullptr)
{
*ppStreamDescriptor = _spStreamDesc.Get();
(*ppStreamDescriptor)->AddRef();
}
else
{
return E_UNEXPECTED;
}
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::RequestSample(
_In_ IUnknown* pToken)
{
auto lock = _critSec.Lock();
HRESULT hr{};
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
const auto syncedSettings = SyncCurrentSettings();
// Source camera is updated, we must shutdown ourselves, since we can't modify presentation descriptor while running
if (!syncedSettings.newCameraName.empty())
{
std::thread{ [this] {
auto lock = _critSec.Lock();
SetStreamState(MF_STREAM_STATE_STOPPED);
} }.detach();
}
else if (syncedSettings.overlayImage)
{
_overlayImage = LoadImageAsSample(syncedSettings.overlayImage, _spMediaType.Get());
}
// Request the first video frame.
ComPtr<IMFSample> sample;
DWORD streamFlags = 0;
const auto readSampleResult = _sourceCamera->ReadSample(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
nullptr,
&streamFlags,
nullptr,
&sample);
IMFSample* outputSample = syncedSettings.webcamDisabled ? _overlayImage.Get() : sample.Get();
const bool noSampleAvailable = !outputSample;
// use black image instead, it should be always available
if (noSampleAvailable)
{
outputSample = _blackImage.Get();
}
RETURN_IF_FAILED_WITH_LOGGING(outputSample->SetSampleTime(MFGetSystemTime()));
RETURN_IF_FAILED_WITH_LOGGING(outputSample->SetSampleDuration(333333));
if (pToken != nullptr)
{
RETURN_IF_FAILED_WITH_LOGGING(outputSample->SetUnknown(MFSampleExtension_Token, pToken));
}
if (FAILED(readSampleResult) && syncedSettings.newCameraName.empty())
{
// Try to reinit webcamera, since it could've been unavailable due to concurrent access from 3rd-party apps
UpdateSourceCamera(_currentSourceCameraName ? *_currentSourceCameraName : L"");
}
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamUnk(MEMediaSample,
GUID_NULL,
S_OK,
outputSample));
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////////////////
// IMFMediaStream2
IFACEMETHODIMP
SimpleMediaStream::SetStreamState(
MF_STREAM_STATE state)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
bool runningState = false;
RETURN_IF_FAILED(_CheckShutdownRequiresLock());
switch (state)
{
case MF_STREAM_STATE_PAUSED:
goto done; // because not supported
case MF_STREAM_STATE_RUNNING:
runningState = true;
break;
case MF_STREAM_STATE_STOPPED:
runningState = false;
_parent->Shutdown();
break;
default:
hr = MF_E_INVALID_STATE_TRANSITION;
break;
}
_isSelected = runningState;
done:
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::GetStreamState(
_Out_ MF_STREAM_STATE* pState)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
*pState = (_isSelected ? MF_STREAM_STATE_RUNNING : MF_STREAM_STATE_STOPPED);
return hr;
}
HRESULT
SimpleMediaStream::Shutdown()
{
VERBOSE_LOG;
HRESULT hr = S_OK;
if (_settingsUpdateChannel.has_value())
{
_settingsUpdateChannel->access([this](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
settings->cameraInUse = false;
});
}
auto lock = _critSec.Lock();
_isShutdown = true;
_parent.Reset();
if (_spEventQueue != nullptr)
{
hr = _spEventQueue->Shutdown();
_spEventQueue.Reset();
}
_spAttributes.Reset();
_spMediaType.Reset();
_spStreamDesc.Reset();
_sourceCamera.Reset();
_currentSourceCameraName.reset();
_settingsUpdateChannel.reset();
_overlayImage.Reset();
_isSelected = false;
return hr;
}
HRESULT SimpleMediaStream::UpdateSourceCamera(std::wstring_view newCameraName)
{
VERBOSE_LOG;
HRESULT hr = S_OK;
_cameraList.Clear();
RETURN_IF_FAILED_WITH_LOGGING(_cameraList.EnumerateDevices());
bool webcamIsChosen = false;
ComPtr<IMFActivate> webcamSourceActivator;
for (UINT32 i = 0; i < _cameraList.Count(); ++i)
{
if (_cameraList.GetDeviceName(i) == newCameraName)
{
_cameraList.GetDevice(i, &webcamSourceActivator);
webcamIsChosen = true;
_currentSourceCameraName.emplace(newCameraName);
break;
}
}
// Try selecting the first camera which isn't us, since at this point we can only guess the user's preferrence
if (!webcamIsChosen)
{
for (UINT32 i = 0; i < _cameraList.Count(); ++i)
{
const auto camName = _cameraList.GetDeviceName(i);
const bool differentCamera = _currentSourceCameraName.has_value() && camName != *_currentSourceCameraName;
if (camName != VIRTUAL_CAMERA_NAME && (differentCamera || !_currentSourceCameraName.has_value()))
{
RETURN_IF_FAILED_WITH_LOGGING(_cameraList.GetDevice(i, &webcamSourceActivator));
webcamIsChosen = true;
_currentSourceCameraName.emplace(camName);
break;
}
}
}
if (!webcamIsChosen)
{
return E_ABORT;
}
ComPtr<IMFMediaSource> realSource;
RETURN_IF_FAILED_WITH_LOGGING(webcamSourceActivator->ActivateObject(
__uuidof(IMFMediaSource),
(void**)realSource.GetAddressOf()));
ComPtr<IMFAttributes> pAttributes;
hr = MFCreateAttributes(&pAttributes, 2);
if (SUCCEEDED(hr))
{
ComPtr<IMFMediaTypeHandler> spTypeHandler;
hr = MFCreateSourceReaderFromMediaSource(
realSource.Get(),
pAttributes.Get(),
&_sourceCamera);
_spAttributes.Reset();
_spMediaType = SelectBestMediaType(_sourceCamera.Get());
RETURN_IF_FAILED_WITH_LOGGING(MFCreateAttributes(&_spAttributes, 10));
RETURN_IF_FAILED_WITH_LOGGING(_SetStreamAttributes(_spAttributes.Get()));
if (_spEventQueue)
{
_spEventQueue->Shutdown();
_spEventQueue.Reset();
}
RETURN_IF_FAILED_WITH_LOGGING(MFCreateEventQueue(&_spEventQueue));
_spStreamDesc.Reset();
// Initialize stream descriptors
RETURN_IF_FAILED_WITH_LOGGING(MFCreateStreamDescriptor(0, 1, _spMediaType.GetAddressOf(), &_spStreamDesc));
RETURN_IF_FAILED_WITH_LOGGING(_spStreamDesc->GetMediaTypeHandler(&spTypeHandler));
RETURN_IF_FAILED_WITH_LOGGING(spTypeHandler->SetCurrentMediaType(_spMediaType.Get()));
RETURN_IF_FAILED_WITH_LOGGING(_SetStreamDescriptorAttributes(_spStreamDesc.Get()));
RETURN_IF_FAILED_WITH_LOGGING(_sourceCamera->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr, _spMediaType.Get()));
}
return hr;
}
SimpleMediaStream::SyncedSettings SimpleMediaStream::SyncCurrentSettings()
{
SyncedSettings result;
if (!_settingsUpdateChannel.has_value())
{
_settingsUpdateChannel = SerializedSharedMemory::open(CameraSettingsUpdateChannel::endpoint(), sizeof(CameraSettingsUpdateChannel), false);
}
if (!_settingsUpdateChannel)
{
return result;
}
_settingsUpdateChannel->access([this, &result](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
bool cameraNameUpdated = false;
result.webcamDisabled = settings->useOverlayImage;
settings->cameraInUse = true;
if (settings->sourceCameraName.has_value())
{
std::wstring_view newCameraNameView{ settings->sourceCameraName->data() };
if (!_currentSourceCameraName.has_value() || *_currentSourceCameraName != newCameraNameView)
{
cameraNameUpdated = true;
result.newCameraName = newCameraNameView;
}
}
if (!settings->overlayImageSize.has_value())
{
return;
}
if (settings->newOverlayImagePosted || !_overlayImage)
{
auto imageChannel =
SerializedSharedMemory::open(CameraOverlayImageChannel::endpoint(), *settings->overlayImageSize, true);
if (!imageChannel)
{
return;
}
imageChannel->access([this, settings, &result](auto imageMemory) {
result.overlayImage = SHCreateMemStream(imageMemory._data, static_cast<UINT>(imageMemory._size));
if (!result.overlayImage)
{
return;
}
settings->newOverlayImagePosted = false;
});
}
});
return result;
}
HRESULT
SimpleMediaStream::_CheckShutdownRequiresLock()
{
if (_isShutdown)
{
return MF_E_SHUTDOWN;
}
if (_spEventQueue == nullptr)
{
return E_UNEXPECTED;
}
return S_OK;
}
HRESULT
SimpleMediaStream::_SetStreamAttributes(
_In_ IMFAttributes* pAttributeStore)
{
HRESULT hr = S_OK;
if (nullptr == pAttributeStore)
{
return E_INVALIDARG;
}
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, PINNAME_VIDEO_CAPTURE));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_STREAM_ID, 0));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_FRAMESERVER_SHARED, 1));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES, _MFFrameSourceTypes::MFFrameSourceTypes_Color));
return hr;
}
HRESULT
SimpleMediaStream::_SetStreamDescriptorAttributes(
_In_ IMFAttributes* pAttributeStore)
{
HRESULT hr = S_OK;
if (nullptr == pAttributeStore)
{
return E_INVALIDARG;
}
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, PINNAME_VIDEO_CAPTURE));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_STREAM_ID, 0));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_FRAMESERVER_SHARED, 1));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES, _MFFrameSourceTypes::MFFrameSourceTypes_Color));
return hr;
}

View File

@@ -0,0 +1,78 @@
#pragma once
#include "stdafx.h"
#include <SerializedSharedMemory.h>
#include <CameraStateUpdateChannels.h>
#include <common/VideoCaptureDeviceList.h>
class SimpleMediaSource;
class SimpleMediaStream : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IMFMediaEventGenerator, IMFMediaStream, IMFMediaStream2>
{
friend class SimpleMediaSource;
public:
// IMFMediaEventGenerator
IFACEMETHOD(BeginGetEvent)
(IMFAsyncCallback* pCallback, IUnknown* punkState);
IFACEMETHOD(EndGetEvent)
(IMFAsyncResult* pResult, IMFMediaEvent** ppEvent);
IFACEMETHOD(GetEvent)
(DWORD dwFlags, IMFMediaEvent** ppEvent);
IFACEMETHOD(QueueEvent)
(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT* pvValue);
// IMFMediaStream
IFACEMETHOD(GetMediaSource)
(IMFMediaSource** ppMediaSource);
IFACEMETHOD(GetStreamDescriptor)
(IMFStreamDescriptor** ppStreamDescriptor);
IFACEMETHOD(RequestSample)
(IUnknown* pToken);
// IMFMediaStream2
IFACEMETHOD(SetStreamState)
(MF_STREAM_STATE state);
IFACEMETHOD(GetStreamState)
(_Out_ MF_STREAM_STATE* pState);
// Non-interface methods.
HRESULT RuntimeClassInitialize(_In_ SimpleMediaSource* pSource);
HRESULT Shutdown();
protected:
struct SyncedSettings
{
bool webcamDisabled = false;
std::wstring newCameraName;
ComPtr<IStream> overlayImage;
};
HRESULT UpdateSourceCamera(std::wstring_view newCameraName);
SyncedSettings SyncCurrentSettings();
HRESULT _CheckShutdownRequiresLock();
HRESULT _SetStreamAttributes(IMFAttributes* pAttributeStore);
HRESULT _SetStreamDescriptorAttributes(IMFAttributes* pAttributeStore);
CriticalSection _critSec;
ComPtr<IMFMediaSource> _parent;
ComPtr<IMFMediaEventQueue> _spEventQueue;
ComPtr<IMFAttributes> _spAttributes;
ComPtr<IMFMediaType> _spMediaType;
ComPtr<IMFStreamDescriptor> _spStreamDesc;
bool _isShutdown = false;
bool _isSelected = false;
VideoCaptureDeviceList _cameraList;
ComPtr<IMFSourceReader> _sourceCamera;
ComPtr<IMFSample> _overlayImage;
ComPtr<IMFSample> _blackImage;
std::optional<SerializedSharedMemory> _settingsUpdateChannel;
std::optional<std::wstring> _currentSourceCameraName;
};

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\hasWDK.props" />
<Import Project="WDK_VideoConferenceCustomMediaSource.vcxproj" Condition="$(HasWDK)==true" />
<Import Project="..\..\..\wdk_skip.vcxproj" Condition="$(HasWDK)==false" />
<PropertyGroup Label="Globals">
<IntDirSharingDetected>None</IntDirSharingDetected>
<ProjectGuid>{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}</ProjectGuid>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}</ProjectGuid>
<RootNamespace>VideoConference</RootNamespace>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<ProjectName>VideoConferenceCustomMediaSource</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
<Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
<Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</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)'=='Debug|x64'">
<CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>false</RunCodeAnalysis>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>false</RunCodeAnalysis>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<EnablePREfast>false</EnablePREfast>
<ExceptionHandling>false</ExceptionHandling>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;$(IntDir);..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>$(OutDir)..\..\common.lib;$(OutDir)VideoConferenceShared.lib;mfplat.lib;Mfsensorgroup.lib;%(AdditionalDependencies);OneCoreUAP.lib</AdditionalDependencies>
<ModuleDefinitionFile>$(ProjectDir)/module.def</ModuleDefinitionFile>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<EnablePREfast>false</EnablePREfast>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<ExceptionHandling>Sync</ExceptionHandling>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;$(IntDir);..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>$(OutDir)..\..\common.lib;$(OutDir)VideoConferenceShared.lib;mfplat.lib;Mfsensorgroup.lib;%(AdditionalDependencies);OneCoreUAP.lib</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<ModuleDefinitionFile>$(ProjectDir)/module.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<None Include="module.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllMain.cpp" />
<ClCompile Include="SimpleMediaSource.cpp" />
<ClCompile Include="SimpleMediaStream.cpp" />
<ClCompile Include="stdafx.cpp" />
<ClCompile Include="ImageLoader.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="SimpleMediaSource.h" />
<ClInclude Include="SimpleMediaStream.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="ImageLoader.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<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')" />
</ImportGroup>
<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'))" />
</Target>
</Project>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="module.def" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="SimpleMediaSource.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SimpleMediaStream.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dllMain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ImageLoader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SimpleMediaSource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SimpleMediaStream.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ImageLoader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,35 @@
/// <copyright file="dllmain.cpp" company="Microsoft">
/// Copyright (c) Microsoft Corporation. All rights reserved.
/// </copyright>
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <wrl\module.h>
#if !defined(__WRL_CLASSIC_COM__)
STDAPI DllGetActivationFactory(_In_ HSTRING activatibleClassId, _COM_Outptr_ IActivationFactory** factory)
{
return Module<InProc>::GetModule().GetActivationFactory(activatibleClassId, factory);
}
#endif
#if !defined(__WRL_WINRT_STRICT__)
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv)
{
return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
}
#endif
STDAPI DllCanUnloadNow()
{
return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
}
STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinst);
}
return TRUE;
}

View File

@@ -0,0 +1,6 @@
LIBRARY
EXPORTS
DllGetActivationFactory PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE

View File

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

View File

@@ -0,0 +1,8 @@
/// <copyright file="stdafx.cpp" company="Microsoft">
/// Copyright (c) Microsoft Corporation. All rights reserved.
/// </copyright>
// stdafx.cpp : source file that includes just the standard includes
// KinectMFMediaSource.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"

View File

@@ -0,0 +1,75 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#ifndef UNICODE
#define UNICODE
#endif
// Windows Header Files:
#include <windows.h>
#include <propvarutil.h>
//#include <mfstd.h> // Must be included before <initguid.h>, or else DirectDraw GUIDs will be defined twice. See the comment in <uuids.h>.
#include <ole2.h>
#include <initguid.h>
#include <ks.h>
#include <ksmedia.h>
#include <mfapi.h>
#include <mferror.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <nserror.h>
#include <winmeta.h>
#include <wrl.h>
#include <d3d9types.h>
#include <new>
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <Wmcodecdsp.h>
#include <assert.h>
#include <Dbt.h>
#include <shlwapi.h>
#include <string_view>
#include <optional>
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
#if !defined(_IKsControl_)
#define _IKsControl_
interface DECLSPEC_UUID("28F54685-06FD-11D2-B27A-00A0C9223196") IKsControl;
#undef INTERFACE
#define INTERFACE IKsControl
DECLARE_INTERFACE_(IKsControl, IUnknown)
{
STDMETHOD(KsProperty)
(
THIS_
IN PKSPROPERTY Property,
IN ULONG PropertyLength,
IN OUT LPVOID PropertyData,
IN ULONG DataLength,
OUT ULONG * BytesReturned) PURE;
STDMETHOD(KsMethod)
(
THIS_
IN PKSMETHOD Method,
IN ULONG MethodLength,
IN OUT LPVOID MethodData,
IN ULONG DataLength,
OUT ULONG * BytesReturned) PURE;
STDMETHOD(KsEvent)
(
THIS_
IN PKSEVENT Event OPTIONAL,
IN ULONG EventLength,
IN OUT LPVOID EventData,
IN ULONG DataLength,
OUT ULONG * BytesReturned) PURE;
};
#endif // _IKsControl_

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,26 @@
# Windows Key Shortcut Guide
# Introduction
The Windows Key Shortcut Guide shows common keyboard shortcuts that use the Windows key.
# Usage
Press and hold the keyboard Windows key for about 1 second, an overlay appears showing keyboard shortcuts that use the Windows Key:
- Shortcuts for changing the position of the active window.
- Common Windows shortcuts.
- Taskbar shortcuts.
Releasing the Windows key will make the overlay disappear. If the shortcut guide was visible for less than a second, the start menu will appear after the shortcut guide is dismissed.
![Image of the Overlay](/doc/images/shortcut_guide/usage.png)
Windows key keyboard shortcuts can be used while the guide is being shown and the result of those shortcuts (active window moved, arrow shortcut behavior changes, etc) will be displayed in the guide.
# Options
These configurations can be edited from the PowerToys Settings screen:
- "How long to press the Windows key before showing the Shortcut Guide (ms)" - How many milliseconds to press the Windows key before the Shortcut Guide is shown.
- "Opacity of the Shortcut Guide's overlay background (%)" - Changing this setting controls the opacity of the Shortcut Guide's overlay background, occluding the work environment beneath the Shortcut Guide to different degrees.
![Image of the Options](/doc/images/shortcut_guide/settings.png)
# Backlog
The backlog for the utility can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/ShortcutGuideBacklog.md) and the source code is [here](https://github.com/Microsoft/PowerToys/tree/master/src/modules/shortcut_guide).

View File

@@ -0,0 +1,307 @@
#include "pch.h"
#include "Toolbar.h"
#include <windowsx.h>
#include "common/windows_colors.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");
}
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:
{
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;
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);
}
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,55 @@
#pragma once
#include <Windows.h>
#include <gdiplus.h>
#include "common/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 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;
UINT_PTR nTimerId;
};

Some files were not shown because too many files have changed in this diff Show More