diff --git a/src/Essentials/src/DeviceInfo/DeviceInfo.uwp.cs b/src/Essentials/src/DeviceInfo/DeviceInfo.uwp.cs index c94c5940d078..5b1034d10fbf 100644 --- a/src/Essentials/src/DeviceInfo/DeviceInfo.uwp.cs +++ b/src/Essentials/src/DeviceInfo/DeviceInfo.uwp.cs @@ -4,7 +4,6 @@ using Microsoft.Maui.ApplicationModel; using Windows.Security.ExchangeActiveSyncProvisioning; using Windows.System.Profile; -using Windows.UI.ViewManagement; namespace Microsoft.Maui.Devices { @@ -114,17 +113,90 @@ public DeviceType DeviceType } } - static readonly int SM_CONVERTIBLESLATEMODE = 0x2003; - static readonly int SM_TABLETPC = 0x56; + /// + /// Whether or not the device is in "tablet mode" or not. This + /// has to be implemented by the device manufacturer. + /// + const int SM_CONVERTIBLESLATEMODE = 0x2003; + + /// + /// How many fingers (aka touches) are supported for touch control + /// + const int SM_MAXIMUMTOUCHES = 95; + + /// + /// Whether a physical keyboard is attached or not. + /// Manufacturers have to remember to set this. + /// Defaults to not-attached. + /// + const int SM_ISDOCKED = 0x2004; + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern int GetSystemMetrics(int nIndex); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool GetAutoRotationState(ref AutoRotationState pState); + + [DllImport("Powrprof.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern PowerPlatformRole PowerDeterminePlatformRoleEx(ulong Version); + static bool GetIsInTabletMode() { - var supportsTablet = GetSystemMetrics(SM_TABLETPC) != 0; - var inTabletMode = GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0; - return inTabletMode && supportsTablet; + // Adopt Chromium's methodology for determining tablet mode + // https://source.chromium.org/chromium/chromium/src/+/main:base/win/win_util.cc;l=537;drc=ac83a5a2d3c04763d86ce16d92f3904cc9566d3a;bpv=0;bpt=1 + // Device does not have a touchscreen + if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0) + { + return false; + } + + // If the device is docked, user is treating as a PC + if (GetSystemMetrics(SM_ISDOCKED) != 0) + { + return false; + } + + // Fetch device rotation. Possible for this to fail. + AutoRotationState rotationState = AutoRotationState.AR_ENABLED; + bool success = GetAutoRotationState(ref rotationState); + + // Fetch succeeded and device does not support rotation + if (success && (rotationState & (AutoRotationState.AR_NOT_SUPPORTED | AutoRotationState.AR_LAPTOP | AutoRotationState.AR_NOSENSOR)) != 0) + { + return false; + } + + // Check if power management says we are mobile (laptop) or a tablet + if ((PowerDeterminePlatformRoleEx(2) & (PowerPlatformRole.PlatformRoleMobile | PowerPlatformRole.PlatformRoleSlate)) != 0) + { + // Check if tablet mode is 0. 0 is default value. + return GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0; + } + + return false; } } + + /// + /// Represents the OEM's preferred power management profile, + /// Useful in-case OEM implements one but not the other + /// + enum PowerPlatformRole + { + PlatformRoleMobile = 2, + PlatformRoleSlate = 8, + } + + /// + /// Whether rotation is supported or not. + /// Rotation is only supported if AR_ENABLED is true + /// + enum AutoRotationState + { + AR_ENABLED = 0x0, + AR_NOT_SUPPORTED = 0x20, + AR_LAPTOP = 0x80, + AR_NOSENSOR = 0x10, + } }