/
githubmirror
/
PowerToys
Обзор
Документация
Войти
/
githubmirror
/
PowerToys
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/modules/powerdisplay/PowerDisplay.Lib/Services/DisplayRotationService.cs
173 строки
7 KB
moooyo
[PD] Clean up PowerDisplay: fix resource leaks, remove dead code, and fix some bugs (#46979)
13 апр 2026, 21:00
Не верифицирован
13 апр 2026, 21:00
ff87dce
Код
Авторство
О чём код?
// 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.Runtime.InteropServices; using ManagedCommon; using PowerDisplay.Common.Models; using static PowerDisplay.Common.Drivers.NativeConstants; using static PowerDisplay.Common.Drivers.PInvoke; using DevMode = PowerDisplay.Common.Drivers.DevMode; namespace PowerDisplay.Common.Services { /// <summary> /// Service for controlling display rotation/orientation. /// Uses ChangeDisplaySettingsEx API to change display orientation. /// </summary> public class DisplayRotationService { /// <summary> /// Set display rotation for a specific monitor. /// Uses GdiDeviceName from the Monitor object for accurate adapter targeting. /// </summary> /// <param name="monitor">Monitor object with GdiDeviceName</param> /// <param name="newOrientation">New orientation: 0=normal, 1=90°, 2=180°, 3=270°</param> /// <returns>Operation result</returns> public MonitorOperationResult SetRotation(Monitor monitor, int newOrientation) { ArgumentNullException.ThrowIfNull(monitor); if (newOrientation < 0 || newOrientation > 3) { return MonitorOperationResult.Failure($"Invalid orientation value: {newOrientation}. Must be 0-3."); } if (string.IsNullOrEmpty(monitor.GdiDeviceName)) { return MonitorOperationResult.Failure("Monitor has no GdiDeviceName"); } return SetRotationByGdiDeviceName(monitor.GdiDeviceName, newOrientation); } /// <summary> /// Set display rotation by GDI device name. /// </summary> /// <param name="gdiDeviceName">GDI device name (e.g., "\\.\DISPLAY1")</param> /// <param name="newOrientation">New orientation: 0=normal, 1=90°, 2=180°, 3=270°</param> /// <returns>Operation result</returns> public unsafe MonitorOperationResult SetRotationByGdiDeviceName(string gdiDeviceName, int newOrientation) { if (string.IsNullOrEmpty(gdiDeviceName)) { return MonitorOperationResult.Failure("GDI device name is required"); } try { // 1. Get current display settings DevMode devMode = default; devMode.DmSize = (short)sizeof(DevMode); if (!EnumDisplaySettings(gdiDeviceName, EnumCurrentSettings, &devMode)) { var error = Marshal.GetLastWin32Error(); Logger.LogError($"SetRotation: EnumDisplaySettings failed for {gdiDeviceName}, error: {error}"); return MonitorOperationResult.Failure($"Failed to get current display settings for {gdiDeviceName}", error); } int currentOrientation = devMode.DmDisplayOrientation; // If already at target orientation, return success if (currentOrientation == newOrientation) { return MonitorOperationResult.Success(); } // 2. Determine if we need to swap width and height // When switching between landscape (0°/180°) and portrait (90°/270°), swap dimensions bool currentIsLandscape = currentOrientation == DmdoDefault || currentOrientation == Dmdo180; bool newIsLandscape = newOrientation == DmdoDefault || newOrientation == Dmdo180; if (currentIsLandscape != newIsLandscape) { // Swap width and height int temp = devMode.DmPelsWidth; devMode.DmPelsWidth = devMode.DmPelsHeight; devMode.DmPelsHeight = temp; } // 3. Set new orientation devMode.DmDisplayOrientation = newOrientation; devMode.DmFields = DmDisplayOrientation | DmPelsWidth | DmPelsHeight; // 4. Test the settings first using CDS_TEST flag int testResult = ChangeDisplaySettingsEx(gdiDeviceName, &devMode, IntPtr.Zero, CdsTest, IntPtr.Zero); if (testResult != DispChangeSuccessful) { string errorMsg = GetChangeDisplaySettingsErrorMessage(testResult); Logger.LogError($"SetRotation: Test failed for {gdiDeviceName}: {errorMsg}"); return MonitorOperationResult.Failure($"Display settings test failed: {errorMsg}", testResult); } // 5. Apply the settings (without CDS_UPDATEREGISTRY to make it temporary) int result = ChangeDisplaySettingsEx(gdiDeviceName, &devMode, IntPtr.Zero, 0, IntPtr.Zero); if (result != DispChangeSuccessful) { string errorMsg = GetChangeDisplaySettingsErrorMessage(result); Logger.LogError($"SetRotation: Apply failed for {gdiDeviceName}: {errorMsg}"); return MonitorOperationResult.Failure($"Failed to apply display settings: {errorMsg}", result); } return MonitorOperationResult.Success(); } catch (Exception ex) { Logger.LogError($"SetRotation: Exception for {gdiDeviceName}: {ex.Message}"); return MonitorOperationResult.Failure($"Exception while setting rotation: {ex.Message}"); } } /// <summary> /// Get current orientation for a GDI device name. /// </summary> /// <param name="gdiDeviceName">GDI device name (e.g., "\\.\DISPLAY1")</param> /// <returns>Current orientation (0-3), or -1 if query failed</returns> public unsafe int GetCurrentOrientation(string gdiDeviceName) { if (string.IsNullOrEmpty(gdiDeviceName)) { return -1; } try { DevMode devMode = default; devMode.DmSize = (short)sizeof(DevMode); if (!EnumDisplaySettings(gdiDeviceName, EnumCurrentSettings, &devMode)) { return -1; } return devMode.DmDisplayOrientation; } catch (Exception ex) { Logger.LogWarning($"GetCurrentOrientation: Exception for {gdiDeviceName}: {ex.Message}"); return -1; } } /// <summary> /// Get human-readable error message for ChangeDisplaySettings result code. /// </summary> private static string GetChangeDisplaySettingsErrorMessage(int resultCode) { return resultCode switch { DispChangeSuccessful => "Success", DispChangeRestart => "Computer must be restarted", DispChangeFailed => "Display driver failed the specified graphics mode", DispChangeBadmode => "Graphics mode is not supported", DispChangeNotupdated => "Unable to write settings to registry", DispChangeBadflags => "Invalid flags", DispChangeBadparam => "Invalid parameter", _ => $"Unknown error code: {resultCode}", }; } } }