-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f64a2a3
commit bf5606b
Showing
16 changed files
with
645 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
/.vs | ||
/obj | ||
/bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<Application x:Class="MonitorControl.App" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:local="clr-namespace:MonitorControl" | ||
StartupUri="MainWindow.xaml"> | ||
<Application.Resources> | ||
|
||
</Application.Resources> | ||
</Application> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using System.Runtime.InteropServices; | ||
using System.Windows; | ||
|
||
namespace MonitorControl | ||
{ | ||
/// <summary> | ||
/// Interaction logic for App.xaml | ||
/// </summary> | ||
public partial class App : Application | ||
{ | ||
public App() | ||
{ | ||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
{ | ||
MessageBox.Show("This application runs only on Windows", "Monitor Control", MessageBoxButton.OK, MessageBoxImage.Exclamation); | ||
Application.Current.Shutdown(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<Window x:Class="MonitorControl.MainWindow" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:local="clr-namespace:MonitorControl" | ||
mc:Ignorable="d" | ||
Title="Monitor Control" | ||
MinHeight="200" MinWidth="200" SizeToContent="WidthAndHeight" ResizeMode="NoResize" | ||
MaxWidth="650" | ||
Loaded="Window_Loaded" | ||
> | ||
|
||
|
||
<ListBox x:Name="MonitorList" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Single"> | ||
<ListBox.ItemContainerStyle> | ||
<Style TargetType="ListBoxItem"> | ||
<Setter Property="Template"> | ||
<Setter.Value> | ||
<ControlTemplate TargetType="ListBoxItem"> | ||
<ContentPresenter/> | ||
</ControlTemplate> | ||
</Setter.Value> | ||
</Setter> | ||
</Style> | ||
</ListBox.ItemContainerStyle> | ||
|
||
<ListBox.ItemsPanel> | ||
<ItemsPanelTemplate> | ||
<WrapPanel IsItemsHost="True" Orientation="Horizontal" /> | ||
</ItemsPanelTemplate> | ||
</ListBox.ItemsPanel> | ||
<ListBox.ItemTemplate> | ||
<DataTemplate> | ||
<Grid Height="160" Width="180" Margin="5" > | ||
<Grid.RowDefinitions> | ||
<RowDefinition Height="100"/> | ||
<RowDefinition /> | ||
</Grid.RowDefinitions> | ||
<Border BorderThickness="1" CornerRadius="3" Grid.RowSpan="2" BorderBrush="{DynamicResource {x:Static SystemColors.ScrollBarBrushKey}}" /> | ||
<Grid Grid.Row="0"> | ||
<TextBlock Text="{Binding DeviceName}" TextWrapping="Wrap" Grid.RowSpan="2" HorizontalAlignment="Center" Margin="0,10,0,0" > | ||
</TextBlock> | ||
<Rectangle HorizontalAlignment="Center" Margin="0,35,0,0" VerticalAlignment="Top" | ||
Width="{Binding DisplayWidth}" Height="53"> | ||
<Rectangle.Stroke> | ||
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.HighlightColorKey}}"/> | ||
</Rectangle.Stroke> | ||
</Rectangle> | ||
</Grid> | ||
<Grid Grid.Row="1" IsEnabled="{Binding IsEnabled}"> | ||
<Slider HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Center" Width="116" Height="24" | ||
Maximum="100" SmallChange="1" TickPlacement="TopLeft" TickFrequency="25" IsMoveToPointEnabled="True" | ||
Value="{Binding BrightnessLevel, Mode=OneWay}" ValueChanged="Slider_ValueChanged" /> | ||
<Button HorizontalAlignment="Right" VerticalAlignment="Center" | ||
Width="32" Height="32" Margin="0,0,10,0" | ||
Content="{Binding PowerText, Mode=OneWay}" Click="Button_Click" /> | ||
</Grid> | ||
</Grid> | ||
</DataTemplate> | ||
</ListBox.ItemTemplate> | ||
</ListBox> | ||
|
||
</Window> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
using System.Collections.Generic; | ||
using System.Windows; | ||
using System.Linq; | ||
using System.ComponentModel; | ||
using System; | ||
|
||
namespace MonitorControl | ||
{ | ||
/// <summary> | ||
/// Interaction logic for MainWindow.xaml | ||
/// </summary> | ||
public partial class MainWindow : Window | ||
{ | ||
public MainWindow() | ||
{ | ||
InitializeComponent(); | ||
} | ||
|
||
MonitorManager manager; | ||
|
||
public List<MonitorData> Monitors = null; | ||
|
||
private void Window_Loaded(object sender, RoutedEventArgs e) | ||
{ | ||
manager = new MonitorManager(); | ||
manager.Initialize(); | ||
|
||
Monitors = manager.Monitors | ||
.SelectMany(a => a.physicalMonitors.Select(b => new MonitorData(b, a.rect))) | ||
.OrderBy(r => r.PositionX) | ||
.ToList(); | ||
|
||
MonitorList.ItemsSource = Monitors; | ||
|
||
} | ||
|
||
private void Button_Click(object sender, RoutedEventArgs e) | ||
{ | ||
FrameworkElement fe = sender as FrameworkElement; | ||
MonitorData monitor = ((MonitorData)fe.DataContext); | ||
|
||
monitor.SwitchPower(); | ||
manager.ChangePower(monitor.Ref.hPhysicalMonitor, monitor.IsPoweredOn); | ||
|
||
|
||
} | ||
|
||
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) | ||
{ | ||
FrameworkElement fe = sender as FrameworkElement; | ||
MonitorData monitor = ((MonitorData)fe.DataContext); | ||
|
||
monitor.Ref.BrightnessLevel = (uint)e.NewValue; | ||
|
||
manager.ChangeBrightness(monitor.Ref.hPhysicalMonitor, monitor.BrightnessLevel); | ||
} | ||
} | ||
|
||
public class MonitorData : INotifyPropertyChanged | ||
{ | ||
public event PropertyChangedEventHandler PropertyChanged; | ||
|
||
public MonitorData(PhysicalMonitor physicalMonitor, Rect rect) | ||
{ | ||
this.Ref = physicalMonitor; | ||
IsLandspace = (rect.Right - rect.Left) / (float)(rect.Bottom - rect.Top) > 1.0f; | ||
PositionX = rect.Left; | ||
} | ||
|
||
public PhysicalMonitor Ref { get; private set; } | ||
public string DeviceName { get { return Ref.IsEnabled ? Ref.DeviceName : "Generic Monitor"; } } | ||
public bool IsEnabled { get { return Ref.IsEnabled; } } | ||
public bool IsPoweredOn { get { return Ref.IsEnabled ? Ref.IsPoweredOn : false; } } | ||
public uint BrightnessLevel { get { return Ref.IsEnabled ? Ref.BrightnessLevel : 0; } } | ||
|
||
public bool IsLandspace { get; private set; } | ||
public int PositionX { get; private set; } | ||
public int DisplayWidth { get { return IsLandspace ? 96 : 30; } } | ||
public string PowerText { get { return IsPoweredOn ? "OFF" : "ON"; } } | ||
|
||
internal void SwitchPower() | ||
{ | ||
this.Ref.IsPoweredOn = !this.Ref.IsPoweredOn; | ||
PropertyChanged(this, new PropertyChangedEventArgs("PowerText")); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Diagnostics; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace MonitorControl | ||
{ | ||
|
||
|
||
|
||
|
||
public class MonitorApi : IDisposable | ||
{ | ||
|
||
public void TEST() | ||
{ | ||
List<(IntPtr ptr, Rect rect, int d)> hMonitors = new List<(IntPtr, Rect, int)>(); | ||
|
||
MonitorEnumProc callback = (IntPtr hDesktop, IntPtr hdc, ref Rect prect, int d) => | ||
{ | ||
hMonitors.Add((hDesktop, prect, d)); | ||
return true; | ||
}; | ||
|
||
List<PHYSICAL_MONITOR> monitors = new List<PHYSICAL_MONITOR>(); | ||
if (EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0)) | ||
{ | ||
foreach (var m in hMonitors) | ||
{ | ||
uint mcount = 0; | ||
if (!GetNumberOfPhysicalMonitorsFromHMONITOR(m.ptr, ref mcount)) | ||
{ | ||
throw new Exception("Cannot get monitor count!"); | ||
} | ||
PHYSICAL_MONITOR[] physicalMonitors = new PHYSICAL_MONITOR[mcount]; | ||
|
||
if (!GetPhysicalMonitorsFromHMONITOR(m.ptr, mcount, physicalMonitors)) | ||
{ | ||
throw new Exception("Cannot get phisical monitor handle!"); | ||
} | ||
|
||
Debug.WriteLine($"PM:{physicalMonitors.Length}) RECT: T:{m.rect.top}/L:{m.rect.left}/R:{m.rect.right}/B:{m.rect.bottom} SCALE:{m.d}"); | ||
monitors.AddRange(physicalMonitors); | ||
} | ||
|
||
for (int i = 0; i < monitors.Count; i++) | ||
{ | ||
|
||
uint cv = 0, mv = 0; | ||
if (GetVCPFeatureAndVCPFeatureReply(monitors[i].hPhysicalMonitor, SVC_FEATURE__POWER_MODE, IntPtr.Zero, ref cv, ref mv)) | ||
{ | ||
Debug.WriteLine($"{i}) {monitors[i].hPhysicalMonitor} + {monitors[i].szPhysicalMonitorDescription} + `{cv}/{mv}`"); | ||
} | ||
else | ||
{ | ||
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; | ||
Debug.WriteLine($"{i}) ERROR: {errorMessage}"); | ||
|
||
// TODO: use HighLevel API to set brightness on these monitors | ||
// https://docs.microsoft.com/en-us/windows/win32/api/highlevelmonitorconfigurationapi/nf-highlevelmonitorconfigurationapi-setmonitorbrightness | ||
} | ||
} | ||
|
||
// call power | ||
|
||
//:: Current value> 1=On / 2=Standby / 3=Suspended / 4=Off / 5=OffByButton | ||
MonitorSwitch(monitors[2], PowerModeEnum.PowerOn); // <<<< OK but doesn't work on laptop screen | ||
//MonitorBrightness(monitors[0], 60); // <<<< OK but doesn't work on laptop screen | ||
|
||
PHYSICAL_MONITOR[] toDestroy = monitors.ToArray(); | ||
DestroyPhysicalMonitors((uint)toDestroy.Length, ref toDestroy); | ||
} | ||
else | ||
{ | ||
//error | ||
} | ||
|
||
} | ||
|
||
|
||
|
||
//Switch monitor power | ||
void MonitorSwitch(PHYSICAL_MONITOR monitor, PowerModeEnum mode) | ||
{ | ||
if (SetVCPFeature(monitor.hPhysicalMonitor, SVC_FEATURE__POWER_MODE, (uint)mode)) | ||
{ | ||
Debug.WriteLine("works!"); | ||
} | ||
else | ||
{ | ||
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; | ||
Debug.WriteLine(errorMessage); | ||
} | ||
} | ||
|
||
|
||
//Switch monitor power | ||
void MonitorBrightness(PHYSICAL_MONITOR monitor, uint pctg) | ||
{ | ||
if (SetVCPFeature(monitor.hPhysicalMonitor, SVC_FEATURE__Brightness, (uint)pctg)) | ||
{ | ||
Debug.WriteLine("works!"); | ||
} | ||
else | ||
{ | ||
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; | ||
Debug.WriteLine(errorMessage); | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 16 | ||
VisualStudioVersion = 16.0.29424.173 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonitorControlNetCore", "MonitorControlNetCore.csproj", "{656F438D-C75C-4FA4-A24A-04A7B611C1A1}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{656F438D-C75C-4FA4-A24A-04A7B611C1A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{656F438D-C75C-4FA4-A24A-04A7B611C1A1}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{656F438D-C75C-4FA4-A24A-04A7B611C1A1}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{656F438D-C75C-4FA4-A24A-04A7B611C1A1}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {5862A0AD-D119-48E7-9671-25DBDA502B9B} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> | ||
|
||
<PropertyGroup> | ||
<OutputType>WinExe</OutputType> | ||
<TargetFramework>netcoreapp3.0</TargetFramework> | ||
<UseWPF>true</UseWPF> | ||
<RootNamespace>MonitorControl</RootNamespace> | ||
<AssemblyName>MonitorControl</AssemblyName> | ||
<StartupObject>MonitorControl.App</StartupObject> | ||
<PackageId>com.ricardotejo.monitor-control</PackageId> | ||
<Authors>Ricardo Tejo</Authors> | ||
<Company>Ricardo Tejo</Company> | ||
<Product>Monitor Control</Product> | ||
<Description>Brightness and power control for multiple physical monitors</Description> | ||
<Copyright>©2019 Ricardo Tejo</Copyright> | ||
<PackageLicenseExpression>MIT</PackageLicenseExpression> | ||
<SignAssembly>false</SignAssembly> | ||
<ApplicationIcon>icon.ico</ApplicationIcon> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Remove="MonitorApi.cs" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<_LastSelectedProfileId>D:\personal\MonitorControl\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<ApplicationDefinition Update="App.xaml"> | ||
<SubType>Designer</SubType> | ||
</ApplicationDefinition> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Page Update="MainWindow.xaml"> | ||
<SubType>Designer</SubType> | ||
</Page> | ||
</ItemGroup> | ||
</Project> |
Oops, something went wrong.