Files
PowerToys/src/modules/videoconference/VideoConferenceModule/Toolbar.cpp

391 lines
12 KiB
C++
Raw Normal View History

#include "pch.h"
#include "Toolbar.h"
#include <windowsx.h>
#include <common/Themes/windows_colors.h>
#include <common/Display/dpi_aware.h>
#include "Logging.h"
#include "VideoConferenceModule.h"
Toolbar* toolbar = nullptr;
const int REFRESH_RATE = 100;
const int OVERLAY_SHOW_TIME = 500;
const int BORDER_OFFSET = 12;
const int TOP_RIGHT_BORDER_OFFSET = 40;
std::wstring cached_position = L"";
Toolbar::Toolbar()
{
toolbar = this;
[General]Reduce installer size by flattening application paths (#27451) * Flatten everything and succeed build * Figure out Settings assets * Remove UseCommonOutputDirectory tag * Proper settings app path * [VCM] Fix assets location * Fix some runtime paths * [RegistryPreview]Use MRTCore specific pri file * [Hosts]Use MRTCore specific pri file * [Settings]Use MRTCore specific pri file * [Peek]Use MRTCore specific pri file * [FileLocksmith]Use MRTCore specific pri file * [ScreenRuler]Use MRTCore specific pri file * [PowerRename]Use MRTCore specific pri file * [Peek]Move assets to own folder * [FileLocksmith] Use own Assets path * [Hosts]Use own assets folder * [PowerRename]Use own assets dir * [MeasureTool] Use its own assets folder * [ImageResizer]Use its own assets path * Fix spellcheck * Fix tab instead of space in project files * Normalize target frameworks and platforms * Remove WINRT_NO_MAKE_DETECTION flag. No longer needed? * Fix AOT and Hosts locations * Fix Dll version differences on dependency * Add Common.UI.csproj as refernce to fix dll versions * Update ControlzEx to normalize dll versions * Update System.Management version to 7.0.2 * Add GPOWrapper to Registry Preview to fix dll versions * [PTRun]Reference Microsoft.Extensions.Hosting to fix dll versions * Fix remaining output paths / dll version conflicts * [KeyboardManager]Executables still on their own directories * Fix Monaco paths * WinAppSDK apps get to play outside again * Enable VCM settings again * Fix KBM Editor path again * [Monaco]Set to own Assets dir * Fix installer preamble; Fix publish. remove unneeded publishes * Remove Hardlink functions * Installer builds again (still needs work to work) * Readd Monaco to spellcheck excludes * Fix spellcheck and call publish.cmd again * [Installer] Remove components that don't need own dirs * [Installer] Refactor Power Launcher * [Installer] Refactor Color Picker * [Installer] Refactor Monaco assets * [Installer]Generate File script no longer needs to remove files * [Installer]Refactor FileLocksmith * [Installer] Refactor Hosts * [Installer]Refactor Image Resizer * [Installer]Refactor MouseUtils * [Installer]Refactor MWB * [Installer]Refactor MeasureTool * [Installer]Refactor Peek * [Installer]Refactor PowerRename and registry fixes * [Installer]Refactor RegistryPreview * [Installer]Refactor ShortcutGuide * [Installer]Refactor Settings * [Installer]Clean up some unused stuff * [Installer]Clean up stuff for user install * [Installer]Fix WinUi3Apps wxs * [Installer]Fix misplaced folders * [Installer]Move x86 VCM dll to right path * Fix monaco language list location * [Installer]Fix VCM directory reference * [CI]Fix signing * [Installer] Fix resources folder for release CI * [ci]Looks like we still ship NLog on PowerToys Run * [Settings]Add dependency to avoid dll collision with Experimentation * [RegistryPreview]Move XAML files to own path * [RegistryPreview]Fix app icon * [Hosts]Move XAML files to their own path * [FileLocksmith]Move XAML files to their own path * [Peek]Move XAML files to own path * [ScreenRuler]Move XAML files to its own path * [Settings]Move XAML to its own path * [ColorPicker]Move Resources to Assets * [ShortcutGuide]Move svgs to own Assets path * [Awake]Move images to assets path * [Runner]Remove debug checks for PowerToys Run assets * [PTRun]Move images to its own assets path * [ImageResizer]Icon for context menu on own assets path * [PowerRename]Move ico to its own path * Remove unneeded intermediary directories * Remove further int dirs * Move tests to its own output path * Fix spellcheck * spellcheck: remove warnings * [CppAnalyzers]Ignore rule in a tool * [CI]Check if all deps.json files reference same versions * fix spellcheck * [ci]Fix task identation * [ci]Add script to guard against asset conflicts * [ci]Add more deps.json audit steps in the release build * Add xbf to spellcheck expects * Fix typo in asset conflict check scripts * Fix some more dependency conflicts * Downgrade CsWinRT to have the same dll version as sdk * [ci]Do a recursive check for every deps.json * Fix spellcheck error inside comment * [ci]Fix asset script error * [ci]Name deps.json verify tasks a bit better * [ci]Improve deps json verify script output * [ci]Update WinRT version to the same running in CI * Also upgrade CsWinRT in NOTICE.MD * [PowerRename]Move XAML files to own path * [Common]Fix Settings executable path * [ci]Verify there's no xbf files in app directories * [installer]Fix firewall path * [Monaco]Move new files to their proper assets path * [Monaco]Fix paths for new files after merge * [Feedback]Removed unneeded build conditions * [Feedback]Clear vcxproj direct reference to frameworks * [Feedback]RunPlugins name to hold PTRun plugins * [Feedback]Remove unneeded foreach * [Feedback]Shortcut Guide svgs with ** in project file * [Feedback]Fix spellcheck
2023-07-20 00:12:46 +01:00
darkImages.camOnMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-On Dark.png");
darkImages.camOffMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-Off Dark.png");
darkImages.camOnMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-On Dark.png");
darkImages.camOffMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-Off Dark.png");
darkImages.camUnusedMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-NotInUse Dark.png");
darkImages.camUnusedMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-NotInUse Dark.png");
lightImages.camOnMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-On Light.png");
lightImages.camOffMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-Off Light.png");
lightImages.camOnMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-On Light.png");
lightImages.camOffMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-Off Light.png");
lightImages.camUnusedMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-NotInUse Light.png");
lightImages.camUnusedMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-NotInUse Light.png");
}
void Toolbar::scheduleModuleSettingsUpdate()
{
moduleSettingsUpdateScheduled = true;
}
void Toolbar::scheduleGeneralSettingsUpdate()
{
generalSettingsUpdateScheduled = true;
}
inline POINT calculateToolbarPositioning(Box const& screenSize, std::wstring& position, const int desiredWidth, const int desiredHeight)
{
POINT p;
p.x = p.y = 0;
if (position == L"Top left corner")
{
p.x = screenSize.left() + BORDER_OFFSET;
p.y = screenSize.top() + BORDER_OFFSET;
}
else if (position == L"Top center")
{
p.x = screenSize.middle().x - desiredWidth / 2;
p.y = screenSize.top() + BORDER_OFFSET;
}
else if (position == L"Bottom left corner")
{
p.x = screenSize.left() + BORDER_OFFSET;
p.y = screenSize.bottom() - desiredHeight - BORDER_OFFSET;
}
else if (position == L"Bottom center")
{
p.x = screenSize.middle().x - desiredWidth / 2;
p.y = screenSize.bottom() - desiredHeight - BORDER_OFFSET;
}
else if (position == L"Bottom right corner")
{
p.x = screenSize.right() - desiredWidth - BORDER_OFFSET;
p.y = screenSize.bottom() - desiredHeight - BORDER_OFFSET;
}
else //"Top right corner" or non-present
{
p.x = screenSize.right() - desiredWidth - BORDER_OFFSET;
p.y = screenSize.top() + TOP_RIGHT_BORDER_OFFSET;
}
return p;
}
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);
UINT dpi = DPIAware::DEFAULT_DPI;
DPIAware::GetScreenDPIForWindow(hwnd, dpi);
if (x < 322 * static_cast<int>(dpi) / static_cast<int>(DPIAware::DEFAULT_DPI) / 2)
{
VideoConferenceModule::reverseMicrophoneMute();
}
else
{
VideoConferenceModule::reverseVirtualCameraMuteState();
}
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
case WM_DPICHANGED:
{
UINT dpi = LOWORD(wparam);
RECT* prcNewWindow = reinterpret_cast<RECT*>(lparam);
POINT suggestedPosition;
suggestedPosition.x = prcNewWindow->left;
suggestedPosition.y = prcNewWindow->top;
int desiredWidth = prcNewWindow->right - prcNewWindow->left;
int desiredHeight = prcNewWindow->bottom - prcNewWindow->top;
HMONITOR hMonitor = MonitorFromPoint(suggestedPosition, MONITOR_DEFAULTTONEAREST);
MonitorInfo info{ hMonitor };
suggestedPosition = calculateToolbarPositioning(info.GetScreenSize(false), cached_position, desiredWidth, desiredHeight);
SetWindowPos(hwnd,
NULL,
suggestedPosition.x,
suggestedPosition.y,
desiredWidth,
desiredHeight,
SWP_NOZORDER | SWP_NOACTIVATE);
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
case WM_CREATE:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc;
UINT dpi = DPIAware::DEFAULT_DPI;
DPIAware::GetScreenDPIForWindow(hwnd, dpi);
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;
}
}
// Images are scaled by 4 to support higher dpi values.
graphic.DrawImage(toolbarImage, 0, 0, toolbarImage->GetWidth() / 4 * dpi / DPIAware::DEFAULT_DPI, toolbarImage->GetHeight() / 4 * dpi / DPIAware::DEFAULT_DPI);
EndPaint(hwnd, &ps);
break;
}
case WM_TIMER:
{
if (toolbar->audioConfChangesNotifier.PullPendingNotifications())
{
instance->onMicrophoneConfigurationChanged();
}
toolbar->microphoneMuted = instance->getMicrophoneMuteState();
if (toolbar->generalSettingsUpdateScheduled)
{
instance->onGeneralSettingsChanged();
toolbar->generalSettingsUpdateScheduled = false;
}
if (toolbar->moduleSettingsUpdateScheduled)
{
instance->onModuleSettingsChanged();
toolbar->moduleSettingsUpdateScheduled = false;
}
toolbar->cameraInUse = VideoConferenceModule::getVirtualCameraInUse();
InvalidateRect(hwnd, NULL, NULL);
using namespace std::chrono;
const auto nowMillis = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
const bool showOverlayTimeout = nowMillis - toolbar->lastTimeCamOrMicMuteStateChanged > OVERLAY_SHOW_TIME;
static bool previousShow = false;
bool show = toolbar->ToolbarHide == L"Never";
const bool cameraJustStoppedInUse = toolbar->previouscameraInUse && !toolbar->cameraInUse;
bool shouldUnmuteAll = cameraJustStoppedInUse;
if (toolbar->ToolbarHide == L"When both camera and microphone are muted")
{
// We shouldn't unmute devices, since we'd like to only show the toolbar only
// when something is unmuted -> the use case is to keep everything muted by default and track it
shouldUnmuteAll = false;
show = (!toolbar->cameraMuted && toolbar->cameraInUse) || !toolbar->microphoneMuted;
}
else if (toolbar->ToolbarHide == L"When both camera and microphone are unmuted")
show = (toolbar->cameraMuted && toolbar->cameraInUse) || toolbar->microphoneMuted;
if (shouldUnmuteAll && !toolbar->moduleSettingsUpdateScheduled)
VideoConferenceModule::unmuteAll();
show = show || !showOverlayTimeout;
ShowWindow(hwnd, show ? SW_SHOW : SW_HIDE);
if (previousShow != show)
{
previousShow = show;
LOG(show ? "Toolbar visibility changed to shown" : "Toolbar visibility changed to hidden");
}
KillTimer(hwnd, toolbar->nTimerId);
toolbar->previouscameraInUse = toolbar->cameraInUse;
break;
}
default:
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
toolbar->nTimerId = SetTimer(hwnd, 101, REFRESH_RATE, nullptr);
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
void Toolbar::show(std::wstring position, std::wstring monitorString)
{
cached_position = position;
for (auto& hwnd : hwnds)
{
PostMessageW(hwnd, WM_CLOSE, 0, 0);
}
hwnds.clear();
// Images are scaled by 4 to support higher dpi values.
int overlayWidth = darkImages.camOffMicOff->GetWidth() / 4;
int overlayHeight = darkImages.camOffMicOff->GetHeight() / 4;
// 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 = reinterpret_cast<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)
{
[New PowerToy] Add Screen Ruler module for measuring screen contents (#19701) * [MeasureTool] initial commit * [chore] clean up needless WindowsTargetPlatformVersion overrides from projects * [MeasureTool] initial implementation * Fix build errors * Update vsconfig for needed Windows 10 SDK versions * fix spellchecker * another spellcheck fix * more spellcheck errors * Fix measurement being off by 1 on both ends * UI fixes * Add feet to crosses * Remove anti-aliasing, as it's creating artifacts * Use pixel tolerance from settings * Tooltip updates * Restore antialiasing to draw the tooltip * remove comment for spell check * Updated icons * Icon updates * Improve measurement accuracy and display * Fix spellchecker * Add less precise drawing on continuous warning * Add setting for turning cross feet on * Swap LMB/RMB for interaction * Uncheck active tool's RadioButton when it exits * activation hotkey toggles UI instead of just launching it * track runner process and exit when it exits * add proj ref * toolbar is interactive during measurements * always open toolbar on the main display * refactor colors * refactor edge detection & overlay ui * refactor overlay ui even more * simplify state structs * multimonitor preparation: eliminate global state * prepare for merge * spelling * proper thread termination + minor fixes * multimonitor: launch tools on all monitors * multimonitor support: track cursor position * spell * fix powertoys! * ScreenSize -> Box * add shadow effect for textbox * spell * fix debug mode * dynamic text box size based on text layout metrics * add mouse wheel to adjust pixel tolerance + per channel detection algorithm setting * spelling * fix per channel distance calculations * update installer deps + spelling * tool activation telemetry * update assets and try to fix build * use × instead of x * allow multiple measurements with bounds tool with shift-click * move #define DEBUG_OVERLAY in an appropriate space * spell-checked * update issue template + refactor text box drawing * implement custom renderer and make × semiopaque * spelling * pass dpiScale to x renderer * add sse2neon license * update OOBE * move license to NOTICE * appropriate module preview image * localization for AutomationPeer * increase default pixel tolerance from 5 to 30 * add PowerToys.MeasureToolUI.exe to bugreport * explicitly set texture dims * clarify continuous capture description * fix a real spelling error! * cleanup * clean up x2 * debug texture * fix texture access * fix saveasbitmap * improve sum of all channel diffs method score calc * optimize * ContinuousCapture is enabled by default to avoid confusion * build fix * draw captured screen in a non continuous mode * cast a spell... * merge fix * disable stroboscopic effect * split global/perScreen measure state and minor improvements * spelling * fix comment * primary monitor debug also active for the bounds tool * dpi from rt for custom renderer * add comment * fix off by 1 * make backround convertion success for non continuous mode non-essential * fix spelling * overlay window covers taskbar * fix CI * revert taskbar covering * fix CI * fix ci again * fix 2 * fix ci * CI fix * fix arm ci * cleanup cursor convertion between coordinate spaces * fix spelling * Fix signing * Fix MeasureToolUI version * Fix core version * fix race condition in system internals which happens during concurrent d3d/d2d resource creation Co-authored-by: Jaime Bernardo <jaime@janeasystems.com> Co-authored-by: Niels Laute <niels.laute@live.nl>
2022-08-27 02:17:20 +03:00
const auto screenSize = monitorInfo.GetScreenSize(false);
UINT dpi = DPIAware::DEFAULT_DPI;
DPIAware::GetScreenDPIForMonitor(monitorInfo.GetHandle(), dpi);
int scaledOverlayWidth = overlayWidth * dpi / DPIAware::DEFAULT_DPI;
int scaledOverlayHeight = overlayHeight * dpi / DPIAware::DEFAULT_DPI;
POINT p = calculateToolbarPositioning(screenSize, position, scaledOverlayWidth, scaledOverlayHeight);
HWND hwnd;
hwnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
CLASS_NAME,
CLASS_NAME,
WS_POPUP,
static_cast<int>(p.x),
static_cast<int>(p.y),
scaledOverlayWidth,
scaledOverlayHeight,
nullptr,
nullptr,
GetModuleHandleW(nullptr),
nullptr);
auto transparentColorKey = RGB(0, 0, 255);
HBRUSH brush = CreateSolidBrush(transparentColorKey);
SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, reinterpret_cast<LONG_PTR>(brush));
SetLayeredWindowAttributes(hwnd, transparentColorKey, 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::setToolbarHide(std::wstring hide)
{
ToolbarHide = hide;
}
void Toolbar::setTheme(std::wstring theme)
{
Toolbar::theme = theme;
}