mirror of
https://github.com/nomi-san/parsec-vdd.git
synced 2026-08-07 05:20:44 +00:00
feat: add primary display feature
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
</Border.Style>
|
||||
<Grid>
|
||||
<TextBlock x:Name="xDeg" d:Text="90°" Foreground="White" Margin="8,4,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" />
|
||||
<TextBlock x:Name="xPri" d:Text="[P]" Foreground="White" Margin="0,4,8,0" HorizontalAlignment="Right" VerticalAlignment="Top" />
|
||||
<TextBlock Text="{Binding DisplayNum}" d:Text="1" FontSize="24" FontWeight="SemiBold" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,5" />
|
||||
<TextBlock Grid.Row="1" Text="{Binding DisplayMode}" d:Text="1920 x 1080 @ 60 Hz" FontSize="12" Foreground="White" TextAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,8" />
|
||||
</Grid>
|
||||
@@ -52,6 +53,7 @@
|
||||
<Separator />
|
||||
<MenuItem Click="TakeScreenshot" Header="{DynamicResource t_take_screenshot}" IsEnabled="{Binding Active}" />
|
||||
<MenuItem Click="MirrorScreen" Header="{DynamicResource t_mirror_screen}" IsEnabled="{Binding Active}" />
|
||||
<MenuItem Click="SetAsPrimary" Header="{DynamicResource t_set_primary}" IsEnabled="{Binding CanSetPrimary}" />
|
||||
<Separator />
|
||||
<MenuItem Click="RemoveDisplay" Header="{DynamicResource t_remove}" />
|
||||
</ContextMenu>
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace ParsecVDisplay.Components
|
||||
public partial class DisplayItem : UserControl
|
||||
{
|
||||
public bool Active { get; set; } = true;
|
||||
public bool CanSetPrimary { get; set; } = true;
|
||||
public string DisplayNum { get; set; } = "1";
|
||||
public string DisplayName { get; set; } = "Display [1]";
|
||||
public string DisplayPath { get; set; } = "\\\\.\\DISPLAY1";
|
||||
@@ -31,6 +32,7 @@ namespace ParsecVDisplay.Components
|
||||
{
|
||||
Display = display;
|
||||
Active = display.Active;
|
||||
CanSetPrimary = display.Active && !display.Primary;
|
||||
|
||||
DisplayNum = $"{display.Identifier}";
|
||||
DisplayName = $"Display [{display.Identifier}]";
|
||||
@@ -40,6 +42,9 @@ namespace ParsecVDisplay.Components
|
||||
? Visibility.Hidden : Visibility.Visible;
|
||||
xDeg.Text = string.Format("{0}°", ((int)display.CurrentOrientation * 90));
|
||||
|
||||
xPri.Visibility = display.Primary ? Visibility.Visible : Visibility.Hidden;
|
||||
xPri.Text = "[P]";
|
||||
|
||||
if (display.Active)
|
||||
{
|
||||
DisplayMode = display.CurrentMode.ToString();
|
||||
@@ -236,5 +241,11 @@ namespace ParsecVDisplay.Components
|
||||
{
|
||||
Tray.Instance.Invoke(() => Tray.Instance.RemoveDisplay(Display.DisplayIndex));
|
||||
}
|
||||
|
||||
private void SetAsPrimary(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Active && !Display.Primary)
|
||||
Display.SetPrimary();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,7 @@ namespace ParsecVDisplay
|
||||
}
|
||||
|
||||
public bool Active;
|
||||
public bool Primary;
|
||||
public int Identifier;
|
||||
public int CloneOf;
|
||||
public int Address;
|
||||
@@ -289,6 +290,60 @@ namespace ParsecVDisplay
|
||||
return Native.ChangeDisplaySettingsEx(null, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make this display the primary monitor. The desktop origin must land
|
||||
/// on the new primary, so we shift every active display by this one's
|
||||
/// current position before flagging it CDS_SET_PRIMARY, then commit
|
||||
/// all changes atomically. Matches the vdd.c reference implementation.
|
||||
/// </summary>
|
||||
public bool SetPrimary()
|
||||
{
|
||||
if (string.IsNullOrEmpty(DeviceName))
|
||||
return false;
|
||||
|
||||
// Capture our current position — every other display will be shifted
|
||||
// by this offset so we end up at (0,0).
|
||||
var target = new Native.DEVMODE();
|
||||
target.dmSize = (short)Marshal.SizeOf(typeof(Native.DEVMODE));
|
||||
if (!Native.EnumDisplaySettings(DeviceName, -1, ref target))
|
||||
return false;
|
||||
|
||||
int offX = target.dmPositionX;
|
||||
int offY = target.dmPositionY;
|
||||
|
||||
var dd = new Native.DISPLAY_DEVICE();
|
||||
dd.cb = Marshal.SizeOf(typeof(Native.DISPLAY_DEVICE));
|
||||
|
||||
bool targetOk = false;
|
||||
for (int i = 0; Native.EnumDisplayDevices(null, i, ref dd, 0); i++)
|
||||
{
|
||||
if ((dd.StateFlags & Native.DISPLAY_DEVICE_ACTIVE) == 0)
|
||||
continue;
|
||||
|
||||
var dm = new Native.DEVMODE();
|
||||
dm.dmSize = (short)Marshal.SizeOf(typeof(Native.DEVMODE));
|
||||
if (!Native.EnumDisplaySettings(dd.DeviceName, -1, ref dm))
|
||||
continue;
|
||||
|
||||
dm.dmPositionX -= offX;
|
||||
dm.dmPositionY -= offY;
|
||||
dm.dmFields = /*DM_POSITION*/ 0x20;
|
||||
|
||||
uint flags = /*CDS_UPDATEREGISTRY*/ 0x1 | /*CDS_NORESET*/ 0x10000000;
|
||||
bool isTarget = string.Equals(dd.DeviceName, DeviceName, StringComparison.Ordinal);
|
||||
if (isTarget)
|
||||
flags |= /*CDS_SET_PRIMARY*/ 0x10;
|
||||
|
||||
int rc = Native.ChangeDisplaySettingsEx(dd.DeviceName, ref dm, IntPtr.Zero, flags, IntPtr.Zero);
|
||||
if (isTarget && rc == 0)
|
||||
targetOk = true;
|
||||
}
|
||||
|
||||
// Commit pending positional changes + the primary flag together.
|
||||
CommitChanges();
|
||||
return targetOk;
|
||||
}
|
||||
|
||||
public void TakeScreenshot(string saveFile)
|
||||
{
|
||||
if (string.IsNullOrEmpty(DeviceName))
|
||||
@@ -367,6 +422,8 @@ namespace ParsecVDisplay
|
||||
var display = new Display
|
||||
{
|
||||
Active = (dd2.StateFlags & Native.DISPLAY_DEVICE_ACTIVE) != 0,
|
||||
// Primary is a per-adapter flag on the outer dd, not on the monitor
|
||||
Primary = (dd.StateFlags & Native.DISPLAY_DEVICE_PRIMARY_DEVICE) != 0,
|
||||
Address = ParseDisplayAddress(dd2.DeviceID),
|
||||
DeviceName = dd.DeviceName,
|
||||
DisplayName = ParseDisplayCode(dd2.DeviceID),
|
||||
@@ -507,6 +564,7 @@ namespace ParsecVDisplay
|
||||
public const uint EDD_GET_DEVICE_INTERFACE_NAME = 0x1;
|
||||
public const uint DISPLAY_DEVICE_ACTIVE = 0x1;
|
||||
public const uint DISPLAY_DEVICE_ATTACHED = 0x2;
|
||||
public const uint DISPLAY_DEVICE_PRIMARY_DEVICE = 0x4;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<system:String x:Key="t_portrait_flipped">Portrait (flipped)</system:String>
|
||||
<system:String x:Key="t_take_screenshot">Take screenshot</system:String>
|
||||
<system:String x:Key="t_mirror_screen">Mirror screen</system:String>
|
||||
<system:String x:Key="t_set_primary">Set as primary</system:String>
|
||||
<system:String x:Key="t_remove">Remove</system:String>
|
||||
|
||||
<!--Tray menu-->
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<system:String x:Key="t_portrait_flipped">Dọc (lật ngược)</system:String>
|
||||
<system:String x:Key="t_take_screenshot">Chụp màn hình</system:String>
|
||||
<system:String x:Key="t_mirror_screen">Phản chiếu màn hình</system:String>
|
||||
<system:String x:Key="t_set_primary">Đặt làm màn hình chính</system:String>
|
||||
<system:String x:Key="t_remove">Tháo bỏ</system:String>
|
||||
|
||||
<!--Tray menu-->
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<system:String x:Key="t_portrait_flipped">纵向 (翻转)</system:String>
|
||||
<system:String x:Key="t_take_screenshot">屏幕截图</system:String>
|
||||
<system:String x:Key="t_mirror_screen">镜像屏幕</system:String>
|
||||
<system:String x:Key="t_set_primary">设为主显示器</system:String>
|
||||
<system:String x:Key="t_remove">移除</system:String>
|
||||
|
||||
<!--Tray menu-->
|
||||
|
||||
Reference in New Issue
Block a user