Skip to content

Commit

Permalink
finish programming of basic functions
Browse files Browse the repository at this point in the history
  • Loading branch information
lsylusiyao committed Jun 17, 2020
1 parent 3065489 commit 88bbd2a
Show file tree
Hide file tree
Showing 7 changed files with 502 additions and 18 deletions.
216 changes: 216 additions & 0 deletions ScreenRotateForWin10/APIWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
using System;
using System.Runtime.InteropServices;

namespace ScreenRotateForWin10
{
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183565(v=vs.85).aspx
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
internal struct DEVMODE
{
public const int CCHDEVICENAME = 32;
public const int CCHFORMNAME = 32;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
[System.Runtime.InteropServices.FieldOffset(0)]
public string dmDeviceName;
[System.Runtime.InteropServices.FieldOffset(32)]
public Int16 dmSpecVersion;
[System.Runtime.InteropServices.FieldOffset(34)]
public Int16 dmDriverVersion;
[System.Runtime.InteropServices.FieldOffset(36)]
public Int16 dmSize;
[System.Runtime.InteropServices.FieldOffset(38)]
public Int16 dmDriverExtra;
[System.Runtime.InteropServices.FieldOffset(40)]
public DM dmFields;

[System.Runtime.InteropServices.FieldOffset(44)]
Int16 dmOrientation;
[System.Runtime.InteropServices.FieldOffset(46)]
Int16 dmPaperSize;
[System.Runtime.InteropServices.FieldOffset(48)]
Int16 dmPaperLength;
[System.Runtime.InteropServices.FieldOffset(50)]
Int16 dmPaperWidth;
[System.Runtime.InteropServices.FieldOffset(52)]
Int16 dmScale;
[System.Runtime.InteropServices.FieldOffset(54)]
Int16 dmCopies;
[System.Runtime.InteropServices.FieldOffset(56)]
Int16 dmDefaultSource;
[System.Runtime.InteropServices.FieldOffset(58)]
Int16 dmPrintQuality;

[System.Runtime.InteropServices.FieldOffset(44)]
public POINTL dmPosition;
[System.Runtime.InteropServices.FieldOffset(52)]
public Int32 dmDisplayOrientation;
[System.Runtime.InteropServices.FieldOffset(56)]
public Int32 dmDisplayFixedOutput;

[System.Runtime.InteropServices.FieldOffset(60)]
public short dmColor;
[System.Runtime.InteropServices.FieldOffset(62)]
public short dmDuplex;
[System.Runtime.InteropServices.FieldOffset(64)]
public short dmYResolution;
[System.Runtime.InteropServices.FieldOffset(66)]
public short dmTTOption;
[System.Runtime.InteropServices.FieldOffset(68)]
public short dmCollate;
[System.Runtime.InteropServices.FieldOffset(72)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
public string dmFormName;
[System.Runtime.InteropServices.FieldOffset(102)]
public Int16 dmLogPixels;
[System.Runtime.InteropServices.FieldOffset(104)]
public Int32 dmBitsPerPel;
[System.Runtime.InteropServices.FieldOffset(108)]
public Int32 dmPelsWidth;
[System.Runtime.InteropServices.FieldOffset(112)]
public Int32 dmPelsHeight;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmDisplayFlags;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmNup;
[System.Runtime.InteropServices.FieldOffset(120)]
public Int32 dmDisplayFrequency;
}

// See: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183569(v=vs.85).aspx
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct DISPLAY_DEVICE
{
[MarshalAs(UnmanagedType.U4)]
public int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
[MarshalAs(UnmanagedType.U4)]
public DisplayDeviceStateFlags StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
}

// See: https://msdn.microsoft.com/de-de/library/windows/desktop/dd162807(v=vs.85).aspx
[StructLayout(LayoutKind.Sequential)]
internal struct POINTL
{
long x;
long y;
}

internal enum DISP_CHANGE : int
{
Successful = 0,
Restart = 1,
Failed = -1,
BadMode = -2,
NotUpdated = -3,
BadFlags = -4,
BadParam = -5,
BadDualView = -6
}

// http://www.pinvoke.net/default.aspx/Enums/DisplayDeviceStateFlags.html
[Flags()]
internal enum DisplayDeviceStateFlags : int
{
/// <summary>The device is part of the desktop.</summary>
AttachedToDesktop = 0x1,
MultiDriver = 0x2,
/// <summary>The device is part of the desktop.</summary>
PrimaryDevice = 0x4,
/// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
MirroringDriver = 0x8,
/// <summary>The device is VGA compatible.</summary>
VGACompatible = 0x10,
/// <summary>The device is removable; it cannot be the primary display.</summary>
Removable = 0x20,
/// <summary>The device has more display modes than its output devices support.</summary>
ModesPruned = 0x8000000,
Remote = 0x4000000,
Disconnect = 0x2000000
}

// http://www.pinvoke.net/default.aspx/user32/ChangeDisplaySettingsFlags.html
[Flags()]
internal enum DisplaySettingsFlags : int
{
CDS_NONE = 0,
CDS_UPDATEREGISTRY = 0x00000001,
CDS_TEST = 0x00000002,
CDS_FULLSCREEN = 0x00000004,
CDS_GLOBAL = 0x00000008,
CDS_SET_PRIMARY = 0x00000010,
CDS_VIDEOPARAMETERS = 0x00000020,
CDS_ENABLE_UNSAFE_MODES = 0x00000100,
CDS_DISABLE_UNSAFE_MODES = 0x00000200,
CDS_RESET = 0x40000000,
CDS_RESET_EX = 0x20000000,
CDS_NORESET = 0x10000000
}

[Flags()]
internal enum DM : int
{
Orientation = 0x00000001,
PaperSize = 0x00000002,
PaperLength = 0x00000004,
PaperWidth = 0x00000008,
Scale = 0x00000010,
Position = 0x00000020,
NUP = 0x00000040,
DisplayOrientation = 0x00000080,
Copies = 0x00000100,
DefaultSource = 0x00000200,
PrintQuality = 0x00000400,
Color = 0x00000800,
Duplex = 0x00001000,
YResolution = 0x00002000,
TTOption = 0x00004000,
Collate = 0x00008000,
FormName = 0x00010000,
LogPixels = 0x00020000,
BitsPerPixel = 0x00040000,
PelsWidth = 0x00080000,
PelsHeight = 0x00100000,
DisplayFlags = 0x00200000,
DisplayFrequency = 0x00400000,
ICMMethod = 0x00800000,
ICMIntent = 0x01000000,
MediaType = 0x02000000,
DitherType = 0x04000000,
PanningWidth = 0x08000000,
PanningHeight = 0x10000000,
DisplayFixedOutput = 0x20000000
}

internal class APIWrapper
{
[DllImport("user32.dll")]
internal static extern DISP_CHANGE ChangeDisplaySettingsEx(
string lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd,
DisplaySettingsFlags dwflags, IntPtr lParam);

[DllImport("user32.dll")]
internal static extern bool EnumDisplayDevices(
string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice,
uint dwFlags);

[DllImport("user32.dll", CharSet = CharSet.Ansi)]
internal static extern int EnumDisplaySettings(
string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);

public const int DMDO_DEFAULT = 0;
public const int DMDO_90 = 1;
public const int DMDO_180 = 2;
public const int DMDO_270 = 3;

public const int ENUM_CURRENT_SETTINGS = -1;

}
}
9 changes: 6 additions & 3 deletions ScreenRotateForWin10/App.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<runtime>
<AppContextSwitchOverrides value = "Switch.System.Windows.DoNotScaleForDpiChanges=false"/>
</runtime>
</configuration>
107 changes: 107 additions & 0 deletions ScreenRotateForWin10/Display.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Runtime.InteropServices;

namespace ScreenRotateForWin10
{
public class Display
{
/// <summary>
/// Angles for Orient
/// </summary>
public enum Orientations
{
DEGREES_CW_0 = 0,
DEGREES_CW_90 = 3,
DEGREES_CW_180 = 2,
DEGREES_CW_270 = 1
}

/// <summary>
/// Rotate the screen
/// </summary>
/// <param name="DisplayNumber">The number of display, starts from 1, same in Setting</param>
/// <param name="Orientation">Degrees</param>
/// <returns></returns>
public static bool Rotate(uint DisplayNumber, Orientations Orientation)
{
if (DisplayNumber == 0)
throw new ArgumentOutOfRangeException("DisplayNumber", DisplayNumber, "First display is 1.");

bool result = false;
DISPLAY_DEVICE d = new DISPLAY_DEVICE();
DEVMODE dm = new DEVMODE();
d.cb = Marshal.SizeOf(d);

if (!APIWrapper.EnumDisplayDevices(null, DisplayNumber - 1, ref d, 0))
throw new ArgumentOutOfRangeException("DisplayNumber", DisplayNumber, "Number is greater than connected displays.");

if (0 != APIWrapper.EnumDisplaySettings(
d.DeviceName, APIWrapper.ENUM_CURRENT_SETTINGS, ref dm))
{
if ((dm.dmDisplayOrientation + (int)Orientation) % 2 == 1) // Need to swap height and width?
{
int temp = dm.dmPelsHeight;
dm.dmPelsHeight = dm.dmPelsWidth;
dm.dmPelsWidth = temp;
}

switch (Orientation)
{
case Orientations.DEGREES_CW_90:
dm.dmDisplayOrientation = APIWrapper.DMDO_270;
break;
case Orientations.DEGREES_CW_180:
dm.dmDisplayOrientation = APIWrapper.DMDO_180;
break;
case Orientations.DEGREES_CW_270:
dm.dmDisplayOrientation = APIWrapper.DMDO_90;
break;
case Orientations.DEGREES_CW_0:
dm.dmDisplayOrientation = APIWrapper.DMDO_DEFAULT;
break;
default:
break;
}

DISP_CHANGE ret = APIWrapper.ChangeDisplaySettingsEx(
d.DeviceName, ref dm, IntPtr.Zero,
DisplaySettingsFlags.CDS_UPDATEREGISTRY, IntPtr.Zero);

result = ret == 0;
}

return result;
}

public static void ResetAllRotations()
{
try
{
uint i = 0;
while (++i <= GetScreenCount()) // make exception less
{
Rotate(i, Orientations.DEGREES_CW_0);
}
}
catch (ArgumentOutOfRangeException)
{
// Everything is fine, just reached the last display
}
}

/// <summary>
/// Get the number of screens
/// </summary>
/// <returns>screen count</returns>
public static uint GetScreenCount()
{
DISPLAY_DEVICE d = new DISPLAY_DEVICE();
d.cb = Marshal.SizeOf(d);

uint result = 0;
while (!APIWrapper.EnumDisplayDevices(null, ++result - 1, ref d, 0)) { }
return result;
}
}

}
30 changes: 27 additions & 3 deletions ScreenRotateForWin10/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,32 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ScreenRotateForWin10"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>

Title="屏幕转向控制程序" Height="410" Width="360" ResizeMode="CanMinimize">
<!--Title="ScreenRotate"-->
<Grid Margin="0,0,-0.4,0">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" Height="30" VerticalAlignment="Top" Margin="0,0,-0.4,0">
<Label Content="要旋转的屏幕:" FontSize="16" FontWeight="Bold"/>
<!--Content="Screen to Rotate"-->
<ComboBox x:Name="choiceComboBox" SelectedIndex="0" Width="100" FontSize="16" ItemsSource="{Binding ScreenNums}"/>
</StackPanel>
<UniformGrid Rows="3" Columns="3" Margin="0,35,0,0" Height="220">
<Label/>
<Button Content="旋转180度" Width="90" Height="60" FontSize="16" Click="Button_Click_2"/>
<!--Content="rotate for 180 deg"-->
<Label/>
<Button Content="顺时针90" Width="90" Height="60" HorizontalAlignment="Right" FontSize="16" Click="Button_Click_3"/>
<!--Content="90 deg for clockwise"-->
<Button Content="默认值" Width="90" Height="60" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="16" Click="Button_Click_4"/>
<!--Content="default"-->
<Button Content="逆时针90" Width="90" Height="60" HorizontalAlignment="Left" FontSize="16" Click="Button_Click_1"/>
<!--Content="90 deg for anti-clockwise"-->
<Label/>
<Button Content="横向" Width="90" Height="60" FontSize="16" Click="Button_Click"/>
<!--Content="horizon"-->
</UniformGrid>
</StackPanel>
<Button x:Name="defaultButton" Content="所有恢复默认" Width="120" VerticalAlignment="Bottom" Margin="0,0,0,30" Height="30" FontSize="14" Click="DefaultButton_Click"/>
<!--Content="All reset to default"-->
</Grid>
</Window>
Loading

0 comments on commit 88bbd2a

Please sign in to comment.