Compare commits

...

8 Commits

Author SHA1 Message Date
martinchrzan
4bb149718f Hiding remove context menu when there are no colors in the history (#8482)
(cherry picked from commit 08d84cbd6a)
2020-12-08 15:33:14 +01:00
martinchrzan
df5c513603 color picker freeze hotfix into stable (#8481)
* Fixing issue when color picker was frozen because other application was locking clipboard

(cherry picked from commit d9fc6e4c9b)

* Fixed another issue where holding activation keys constantly would trigger show color picker numberous times and it would cause some events to be attached multiple times

(cherry picked from commit f5388a7c87)
2020-12-08 15:32:48 +01:00
Ivan Stošić
047b455601 [FancyZones] Reduce VRAM use (#8469) 2020-12-08 14:21:20 +01:00
Andrey Nekrasov
da384d3d65 FZ: fix incorrect snapping zoning behavior caused by calling MessageBox from LLKBHook cb (#8476) 2020-12-08 15:15:57 +03:00
Andrey Nekrasov
e6879aa97c install: update the installed dotnet version (#8456) 2020-12-08 13:58:16 +03:00
Ivan Stošić
4ced84b46d Free render target 2020-12-08 11:52:55 +01:00
Seraphima Zykova
1240598635 [FancyZones] Obtain device id (#8453) 2020-12-07 19:07:41 +03:00
Seraphima Zykova
9d39952670 handle key events (#8281) 2020-11-30 18:42:47 +01:00
14 changed files with 175 additions and 102 deletions

View File

@@ -22,7 +22,7 @@ namespace updating
bool install_dotnet(const bool silent)
{
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/3eb7efa1-96c6-4e97-bb9f-563ecf595f8a/7efd9c1cdd74df8fb0a34c288138a84f/windowsdesktop-runtime-3.1.6-win-x64.exe";
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/513acf37-8da2-497d-bdaa-84d6e33c1fee/eb7b010350df712c752f4ec4b615f89d/windowsdesktop-runtime-3.1.10-win-x64.exe";
const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime.exe";
auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME;

View File

@@ -14,6 +14,8 @@ namespace ColorPicker.Helpers
{
private readonly IColorEditorViewModel _colorEditorViewModel;
private ColorEditorWindow _colorEditorWindow;
private bool _colorPickerShown;
private object _colorPickerVisibilityLock = new object();
[ImportingConstructor]
public AppStateHandler(IColorEditorViewModel colorEditorViewModel)
@@ -30,16 +32,30 @@ namespace ColorPicker.Helpers
public void ShowColorPicker()
{
AppShown?.Invoke(this, EventArgs.Empty);
Application.Current.MainWindow.Opacity = 0;
Application.Current.MainWindow.Visibility = Visibility.Visible;
lock (_colorPickerVisibilityLock)
{
if (!_colorPickerShown)
{
AppShown?.Invoke(this, EventArgs.Empty);
Application.Current.MainWindow.Opacity = 0;
Application.Current.MainWindow.Visibility = Visibility.Visible;
_colorPickerShown = true;
}
}
}
public void HideColorPicker()
{
Application.Current.MainWindow.Opacity = 0;
Application.Current.MainWindow.Visibility = Visibility.Collapsed;
AppHidden?.Invoke(this, EventArgs.Empty);
lock (_colorPickerVisibilityLock)
{
if (_colorPickerShown)
{
Application.Current.MainWindow.Opacity = 0;
Application.Current.MainWindow.Visibility = Visibility.Collapsed;
AppHidden?.Invoke(this, EventArgs.Empty);
_colorPickerShown = false;
}
}
}
public void ShowColorPickerEditor()

View File

@@ -3,7 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using static ColorPicker.NativeMethods;
namespace ColorPicker.Helpers
{
@@ -23,15 +25,24 @@ namespace ColorPicker.Helpers
{
try
{
Clipboard.SetText(colorRepresentationToCopy);
Clipboard.SetDataObject(colorRepresentationToCopy);
break;
}
catch (COMException ex)
{
var hwnd = GetOpenClipboardWindow();
var sb = new StringBuilder(501);
_ = GetWindowText(hwnd.ToInt32(), sb, 500);
var applicationUsingClipboard = sb.ToString();
if ((uint)ex.ErrorCode != ErrorCodeClipboardCantOpen)
{
Logger.LogError("Failed to set text into clipboard", ex);
}
else
{
Logger.LogError("Failed to set text into clipboard, application that is locking clipboard - " + applicationUsingClipboard, ex);
}
}
System.Threading.Thread.Sleep(10);

View File

@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Windows.Input;
using ColorPicker.Helpers;
using ColorPicker.Settings;
@@ -21,10 +22,12 @@ namespace ColorPicker.Keyboard
{
private readonly AppStateHandler _appStateHandler;
private readonly IUserSettings _userSettings;
private List<string> _previouslyPressedKeys;
private List<string> _activationKeys = new List<string>();
private GlobalKeyboardHook _keyboardHook;
private bool disposedValue;
private bool _activationShortcutPressed;
[ImportingConstructor]
public KeyboardMonitor(AppStateHandler appStateHandler, IUserSettings userSettings)
@@ -80,26 +83,38 @@ namespace ColorPicker.Keyboard
// If the last key pressed is a modifier key, then currentlyPressedKeys cannot possibly match with _activationKeys
// because _activationKeys contains exactly 1 non-modifier key. Hence, there's no need to check if `name` is a
// modifier key or to do any additional processing on it.
// Check pressed modifier keys.
AddModifierKeys(currentlyPressedKeys);
if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown || e.KeyboardState == GlobalKeyboardHook.KeyboardState.SysKeyDown)
{
// Check pressed modifier keys.
AddModifierKeys(currentlyPressedKeys);
currentlyPressedKeys.Add(name);
}
currentlyPressedKeys.Sort();
if (currentlyPressedKeys.Count == 0 && _previouslyPressedKeys.Count != 0)
{
// no keys pressed, we can enable activation shortcut again
_activationShortcutPressed = false;
}
_previouslyPressedKeys = currentlyPressedKeys;
if (ArraysAreSame(currentlyPressedKeys, _activationKeys))
{
if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor)
// avoid triggering this action multiple times as this will be called nonstop while keys are pressed
if (!_activationShortcutPressed)
{
_appStateHandler.ShowColorPickerEditor();
}
else
{
_appStateHandler.ShowColorPicker();
_activationShortcutPressed = true;
if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor)
{
_appStateHandler.ShowColorPickerEditor();
}
else
{
_appStateHandler.ShowColorPicker();
}
}
}
}

View File

@@ -5,6 +5,7 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
namespace ColorPicker
{
@@ -161,5 +162,11 @@ namespace ColorPicker
/// </summary>
public IntPtr AdditionalInformation;
}
[DllImport("user32.dll")]
internal static extern IntPtr GetOpenClipboardWindow();
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern int GetWindowText(int hwnd, StringBuilder text, int count);
}
}

View File

@@ -28,7 +28,7 @@
SelectedIndex="{Binding SelectedColorIndex}"
ItemContainerStyle="{DynamicResource ColorHistoryListViewStyle}">
<ui:ListView.ContextMenu>
<ContextMenu>
<ContextMenu Visibility="{Binding ColorsHistory.Count, Converter={StaticResource numberToVisibilityConverter}}">
<MenuItem Header="{x:Static p:Resources.Remove}"
Command="{Binding RemoveColorCommand}">
<MenuItem.Icon>

View File

@@ -91,6 +91,22 @@ namespace FancyZonesEditor
Overlay.Show();
}
public void App_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.LeftShift || e.Key == System.Windows.Input.Key.RightShift)
{
MainWindowSettings.IsShiftKeyPressed = false;
}
}
public void App_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.LeftShift || e.Key == System.Windows.Input.Key.RightShift)
{
MainWindowSettings.IsShiftKeyPressed = true;
}
}
public static void ShowExceptionMessageBox(string message, Exception exception = null)
{
string fullMessage = FancyZonesEditor.Properties.Resources.Error_Report + PowerToysIssuesURL + " \n" + message;

View File

@@ -19,6 +19,7 @@ namespace FancyZonesEditor
InitializeComponent();
KeyUp += GridEditorWindow_KeyUp;
KeyDown += ((App)Application.Current).App_KeyDown;
_stashedModel = (GridLayoutModel)(App.Overlay.CurrentDataContext as GridLayoutModel).Clone();
}
@@ -36,6 +37,8 @@ namespace FancyZonesEditor
{
OnCancel(sender, null);
}
((App)Application.Current).App_KeyUp(sender, e);
}
private GridLayoutModel _stashedModel;

View File

@@ -5,6 +5,7 @@
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using FancyZonesEditor.Utils;
@@ -32,6 +33,9 @@ namespace FancyZonesEditor.Models
Window.Background = (Brush)properties[milliseconds % properties.Length].GetValue(null, null);
}
Window.KeyUp += ((App)Application.Current).App_KeyUp;
Window.KeyDown += ((App)Application.Current).App_KeyDown;
Window.Left = workArea.X;
Window.Top = workArea.Y;
Window.Width = workArea.Width;

View File

@@ -634,53 +634,73 @@ void FancyZones::ToggleEditor() noexcept
const bool spanZonesAcrossMonitors = m_settings->GetSettings()->spanZonesAcrossMonitors;
params += std::to_wstring(spanZonesAcrossMonitors) + divider; /* Span zones */
std::vector<std::pair<HMONITOR, MONITORINFOEX>> allMonitors;
allMonitors = FancyZonesUtils::GetAllMonitorInfo<&MONITORINFOEX::rcWork>();
// device id map
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
bool showDpiWarning = false;
int prevDpiX = -1, prevDpiY = -1;
std::wstring monitorsData;
for (auto& monitor : allMonitors)
std::wstring monitorsDataStr;
for (auto& monitorData : allMonitors)
{
auto monitorId = FancyZonesUtils::GenerateMonitorId(monitor.second, monitor.first, m_currentDesktopId);
if (monitor.first == targetMonitor)
HMONITOR monitor = monitorData.first;
auto monitorInfo = monitorData.second;
std::wstring monitorId;
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(monitorInfo.szDevice, displayDeviceIdxMap);
wil::unique_cotaskmem_string virtualDesktopId;
if (SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopId)))
{
params += *monitorId + divider; /* Monitor id where the Editor should be opened */
monitorId = FancyZonesUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
}
else
{
continue;
}
if (monitorId.has_value())
if (monitor == targetMonitor)
{
monitorsData += std::move(*monitorId) + divider; /* Monitor id */
params += monitorId + divider; /* Monitor id where the Editor should be opened */
}
UINT dpiX = 0;
UINT dpiY = 0;
if (GetDpiForMonitor(monitor.first, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
monitorsDataStr += std::move(monitorId) + divider; /* Monitor id */
UINT dpiX = 0;
UINT dpiY = 0;
if (GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
{
monitorsDataStr += std::to_wstring(dpiX) + divider; /* DPI */
if (spanZonesAcrossMonitors && prevDpiX != -1 && (prevDpiX != dpiX || prevDpiY != dpiY))
{
monitorsData += std::to_wstring(dpiX) + divider; /* DPI */
if (spanZonesAcrossMonitors && prevDpiX != -1 && (prevDpiX != dpiX || prevDpiY != dpiY))
{
showDpiWarning = true;
}
prevDpiX = dpiX;
prevDpiY = dpiY;
showDpiWarning = true;
}
monitorsData += std::to_wstring(monitor.second.rcMonitor.left) + divider;
monitorsData += std::to_wstring(monitor.second.rcMonitor.top) + divider;
prevDpiX = dpiX;
prevDpiY = dpiY;
}
monitorsDataStr += std::to_wstring(monitorInfo.rcMonitor.left) + divider;
monitorsDataStr += std::to_wstring(monitorInfo.rcMonitor.top) + divider;
}
params += std::to_wstring(allMonitors.size()) + divider; /* Monitors count */
params += monitorsData;
params += monitorsDataStr;
if (showDpiWarning)
{
MessageBoxW(NULL,
GET_RESOURCE_STRING(IDS_SPAN_ACROSS_ZONES_WARNING).c_str(),
GET_RESOURCE_STRING(IDS_POWERTOYS_FANCYZONES).c_str(),
MB_OK | MB_ICONWARNING);
// We must show the message box in a separate thread, since this code is called from a low-level
// keyboard hook callback, and launching messageboxes from it has unexpected side effects,
// like triggering EVENT_SYSTEM_MOVESIZEEND prematurely.
// TODO: understand the root cause of this, until then it's commented out.
//std::thread{ [] {
// MessageBoxW(nullptr,
// GET_RESOURCE_STRING(IDS_SPAN_ACROSS_ZONES_WARNING).c_str(),
// GET_RESOURCE_STRING(IDS_POWERTOYS_FANCYZONES).c_str(),
// MB_OK | MB_ICONWARNING);
//} }.detach();
}
const auto& fancyZonesData = FancyZonesDataInstance();
@@ -951,30 +971,8 @@ void FancyZones::UpdateZoneWindows() noexcept
auto& displayDeviceIdxMap = *(params->displayDeviceIdx);
FancyZones* fancyZones = params->fancyZones;
DISPLAY_DEVICE displayDevice{ .cb = sizeof(displayDevice) };
std::wstring deviceId;
while (EnumDisplayDevicesW(mi.szDevice, displayDeviceIdxMap[mi.szDevice], &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
{
++displayDeviceIdxMap[mi.szDevice];
// Only take active monitors (presented as being "on" by the respective GDI view) and monitors that don't
// represent a pseudo device used to mirror application drawing.
if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_ACTIVE) &&
WI_IsFlagClear(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER))
{
deviceId = displayDevice.DeviceID;
fancyZones->AddZoneWindow(monitor, deviceId);
break;
}
}
if (deviceId.empty())
{
deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
L"\\\\?\\DISPLAY#LOCALDISPLAY#";
fancyZones->AddZoneWindow(monitor, deviceId);
}
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(mi.szDevice, displayDeviceIdxMap);
fancyZones->AddZoneWindow(monitor, deviceId);
}
return TRUE;
};

View File

@@ -120,8 +120,7 @@ bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monit
{
return false;
}
const UINT dpi = GetDpiForMonitor(m_monitor);
workAreaRect = Rect(mi.rcWork, dpi);
workAreaRect = Rect(mi.rcWork);
}
else
{

View File

@@ -315,4 +315,9 @@ ZoneWindowDrawing::~ZoneWindowDrawing()
}
m_cv.notify_all();
m_renderThread.join();
if (m_renderTarget)
{
m_renderTarget->Release();
}
}

View File

@@ -155,6 +155,34 @@ namespace FancyZonesUtils
}
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
std::wstring GetDisplayDeviceId(const std::wstring& device, std::unordered_map<std::wstring, DWORD>& displayDeviceIdxMap)
{
DISPLAY_DEVICE displayDevice{ .cb = sizeof(displayDevice) };
std::wstring deviceId;
while (EnumDisplayDevicesW(device.c_str(), displayDeviceIdxMap[device], &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
{
++displayDeviceIdxMap[device];
// Only take active monitors (presented as being "on" by the respective GDI view) and monitors that don't
// represent a pseudo device used to mirror application drawing.
if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_ACTIVE) &&
WI_IsFlagClear(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER))
{
deviceId = displayDevice.DeviceID;
break;
}
}
if (deviceId.empty())
{
deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
L"\\\\?\\DISPLAY#LOCALDISPLAY#";
}
return deviceId;
}
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
{
UINT dpi{};
@@ -582,36 +610,6 @@ namespace FancyZonesUtils
return result;
}
std::optional<std::wstring> GenerateMonitorId(MONITORINFOEX mi, HMONITOR monitor, const GUID& virtualDesktopId)
{
DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) };
PCWSTR deviceId = nullptr;
bool validMonitor = true;
if (EnumDisplayDevices(mi.szDevice, 0, &displayDevice, 1))
{
if (displayDevice.DeviceID[0] != L'\0')
{
deviceId = displayDevice.DeviceID;
}
}
if (!deviceId)
{
deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
L"\\\\?\\DISPLAY#LOCALDISPLAY#";
}
wil::unique_cotaskmem_string vdId;
if (SUCCEEDED(StringFromCLSID(virtualDesktopId, &vdId)))
{
return GenerateUniqueId(monitor, deviceId, vdId.get());
}
return std::nullopt;
}
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept
{
using complex = std::complex<double>;

View File

@@ -184,6 +184,8 @@ namespace FancyZonesUtils
return result;
}
std::wstring GetDisplayDeviceId(const std::wstring& device, std::unordered_map<std::wstring, DWORD>& displayDeviceIdxMap);
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
void SizeWindowToRect(HWND window, RECT rect) noexcept;
@@ -202,7 +204,6 @@ namespace FancyZonesUtils
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& devideId, const std::wstring& virtualDesktopId);
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId);
std::optional<std::wstring> GenerateMonitorId(MONITORINFOEX mi, HMONITOR monitor, const GUID& virtualDesktopId);
std::wstring TrimDeviceId(const std::wstring& deviceId);
std::optional<FancyZonesDataTypes::DeviceIdData> ParseDeviceId(const std::wstring& deviceId);