diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8c6de96
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+
+/.vs
+/obj
+/bin
diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..3592d21
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..e4d14a2
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,20 @@
+using System.Runtime.InteropServices;
+using System.Windows;
+
+namespace MonitorControl
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ 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();
+ }
+ }
+ }
+}
diff --git a/MainWindow.xaml b/MainWindow.xaml
new file mode 100644
index 0000000..173586a
--- /dev/null
+++ b/MainWindow.xaml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
new file mode 100644
index 0000000..8a0cb27
--- /dev/null
+++ b/MainWindow.xaml.cs
@@ -0,0 +1,88 @@
+using System.Collections.Generic;
+using System.Windows;
+using System.Linq;
+using System.ComponentModel;
+using System;
+
+namespace MonitorControl
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ MonitorManager manager;
+
+ public List 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 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"));
+ }
+ }
+
+}
diff --git a/MonitorApi.cs b/MonitorApi.cs
new file mode 100644
index 0000000..665b35d
--- /dev/null
+++ b/MonitorApi.cs
@@ -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 monitors = new List();
+ 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);
+ }
+ }
+
+ }
+}
diff --git a/MonitorControl.sln b/MonitorControl.sln
new file mode 100644
index 0000000..b89715b
--- /dev/null
+++ b/MonitorControl.sln
@@ -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
diff --git a/MonitorControlNetCore.csproj b/MonitorControlNetCore.csproj
new file mode 100644
index 0000000..82dce03
--- /dev/null
+++ b/MonitorControlNetCore.csproj
@@ -0,0 +1,25 @@
+
+
+
+ WinExe
+ netcoreapp3.0
+ true
+ MonitorControl
+ MonitorControl
+ MonitorControl.App
+ com.ricardotejo.monitor-control
+ Ricardo Tejo
+ Ricardo Tejo
+ Monitor Control
+ Brightness and power control for multiple physical monitors
+ ©2019 Ricardo Tejo
+ MIT
+ false
+ icon.ico
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MonitorControlNetCore.csproj.user b/MonitorControlNetCore.csproj.user
new file mode 100644
index 0000000..baf1e08
--- /dev/null
+++ b/MonitorControlNetCore.csproj.user
@@ -0,0 +1,16 @@
+
+
+
+ <_LastSelectedProfileId>D:\personal\MonitorControl\Properties\PublishProfiles\FolderProfile.pubxml
+
+
+
+ Designer
+
+
+
+
+ Designer
+
+
+
\ No newline at end of file
diff --git a/MonitorManager.cs b/MonitorManager.cs
new file mode 100644
index 0000000..2163335
--- /dev/null
+++ b/MonitorManager.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace MonitorControl
+{
+
+ public class PhysicalMonitor
+ {
+ public IntPtr hPhysicalMonitor;
+ public string DeviceName;
+ public bool IsEnabled;
+ public bool IsPoweredOn;
+ public uint BrightnessLevel;
+ }
+ public class Monitor
+ {
+ public IntPtr hMonitor;
+ public Rect rect;
+ public List physicalMonitors;
+ }
+
+
+ internal class MonitorManager : IDisposable
+ {
+ #region [Windows API]
+ [DllImport("user32.dll", EntryPoint = "MonitorFromWindow")]
+ private static extern IntPtr MonitorFromWindow([In] IntPtr hwnd, uint dwFlags);
+
+ [DllImport("dxva2.dll", EntryPoint = "DestroyPhysicalMonitors")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool DestroyPhysicalMonitors(uint dwPhysicalMonitorArraySize, ref PHYSICAL_MONITOR[] pPhysicalMonitorArray);
+
+ [DllImport("dxva2.dll", EntryPoint = "GetNumberOfPhysicalMonitorsFromHMONITOR")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool GetNumberOfPhysicalMonitorsFromHMONITOR(IntPtr hMonitor, ref uint pdwNumberOfPhysicalMonitors);
+
+ [DllImport("dxva2.dll", EntryPoint = "GetPhysicalMonitorsFromHMONITOR")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool GetPhysicalMonitorsFromHMONITOR(IntPtr hMonitor, uint dwPhysicalMonitorArraySize,
+ [Out] PHYSICAL_MONITOR[] pPhysicalMonitorArray);
+
+ [DllImport("dxva2.dll", EntryPoint = "GetVCPFeatureAndVCPFeatureReply", SetLastError = true)]
+ private static extern Boolean GetVCPFeatureAndVCPFeatureReply([In] IntPtr hPhisicalMonitor, [In] byte bVCPCode,
+ IntPtr pvct, ref uint pdwCurrentValue, ref uint pdwMaximumValue);
+
+
+ private delegate bool MonitorEnumProc(IntPtr hDesktop, IntPtr hdc, ref Rect pRect, int dwData);
+
+ [DllImport("user32")]
+ private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lpRect, MonitorEnumProc callback, int dwData);
+
+ [DllImport("dxva2.dll", EntryPoint = "SetVCPFeature", SetLastError = true)]
+ private static extern bool SetVCPFeature([In] IntPtr hPhisicalMonitor, byte bVCPCode, uint dwNewValue);
+ #endregion
+
+ const byte SVC_FEATURE__POWER_MODE = 0xD6; // values use PowerModeEnum
+ const byte SVC_FEATURE__BRIGHTNESS = 0x10; // value range is [0-100]
+
+ public enum PowerModeEnum : uint
+ {
+ PowerOn = 0x01,
+ PowerStandby = 0x02,
+ PowerSuspend = 0x03,
+ PowerOff = 0x04,
+ PowerOffButton = 0x05 // Readonly
+ }
+
+ List monitors;
+ List physicalMonitors;
+
+ public IReadOnlyList Monitors
+ {
+ get
+ {
+ return monitors.AsReadOnly();
+ }
+ }
+ public void Initialize()
+ {
+ monitors = new List();
+ physicalMonitors = new List();
+
+
+ List<(IntPtr monitor, Rect rect)> hMonitors = new List<(IntPtr, Rect)>();
+
+ MonitorEnumProc callback = (IntPtr hMonitor, IntPtr hdc, ref Rect prect, int d) =>
+ {
+ monitors.Add(new Monitor
+ {
+ hMonitor = hMonitor,
+ rect = prect,
+ });
+ return true;
+ };
+
+
+ if (EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0))
+ {
+ foreach (var m in monitors)
+ {
+ uint mcount = 0;
+ if (!GetNumberOfPhysicalMonitorsFromHMONITOR(m.hMonitor, ref mcount))
+ {
+ throw new Exception("Cannot get monitor count!");
+ }
+ PHYSICAL_MONITOR[] physicalMonitors = new PHYSICAL_MONITOR[mcount];
+
+ if (!GetPhysicalMonitorsFromHMONITOR(m.hMonitor, 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}");
+
+ this.physicalMonitors.AddRange(physicalMonitors);
+
+ m.physicalMonitors = physicalMonitors.Select(a => new PhysicalMonitor
+ {
+ DeviceName = a.szPhysicalMonitorDescription,
+ hPhysicalMonitor = a.hPhysicalMonitor
+ }).ToList();
+
+ }
+
+ foreach (var p in monitors.SelectMany(a => a.physicalMonitors))
+ {
+ uint cv = 0;
+
+ // power
+ if (GetFeatureValue(p.hPhysicalMonitor, SVC_FEATURE__POWER_MODE, ref cv))
+ {
+ p.IsPoweredOn = (cv == (uint)PowerModeEnum.PowerOn);
+ Debug.WriteLine($"{p.hPhysicalMonitor} + {p.DeviceName} + POWER={cv}");
+ p.IsEnabled = true;
+ }
+ else
+ {
+ string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
+ Debug.WriteLine($"ERROR for {p.DeviceName}: `{errorMessage}`");
+ }
+
+ // BRIG
+ if (GetFeatureValue(p.hPhysicalMonitor, SVC_FEATURE__BRIGHTNESS, ref cv))
+ {
+ p.BrightnessLevel = cv;
+ Debug.WriteLine($"{p.hPhysicalMonitor} + {p.DeviceName} + BRIGHTNESS={cv}");
+ }
+ else
+ {
+ string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
+ Debug.WriteLine($"ERROR for {p.DeviceName}: `{errorMessage}`");
+ }
+ }
+ }
+ }
+
+
+ private bool GetFeatureValue(IntPtr hPhysicalMonitor, byte svc_feature, ref uint currentValue)
+ {
+ uint mv = 0;
+ return GetVCPFeatureAndVCPFeatureReply(hPhysicalMonitor, svc_feature, IntPtr.Zero, ref currentValue, ref mv);
+ //string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
+ }
+
+ private bool SetFeatureValue(IntPtr hPhysicalMonitor, byte svc_feature, uint newVurrent)
+ {
+ return SetVCPFeature(hPhysicalMonitor, svc_feature, newVurrent);
+ //string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
+ }
+
+ public void ChangeBrightness(IntPtr hPhysicalMonitor, uint brightness)
+ {
+ SetFeatureValue(hPhysicalMonitor, SVC_FEATURE__BRIGHTNESS, brightness);
+ // TODO: use HighLevel API to set brightness on non VESA monitors
+ // https://docs.microsoft.com/en-us/windows/win32/api/highlevelmonitorconfigurationapi/nf-highlevelmonitorconfigurationapi-setmonitorbrightness
+ }
+
+ public void ChangePower(IntPtr hPhysicalMonitor, bool PowerOn)
+ {
+ SetFeatureValue(hPhysicalMonitor, SVC_FEATURE__POWER_MODE, PowerOn ? (uint)PowerModeEnum.PowerOn : (uint)PowerModeEnum.PowerOff);
+ }
+
+
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+
+ PHYSICAL_MONITOR[] toDestroy = physicalMonitors.ToArray();
+ DestroyPhysicalMonitors((uint)toDestroy.Length, ref toDestroy);
+ }
+
+
+ }
+
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ public struct PHYSICAL_MONITOR
+ {
+ public IntPtr hPhysicalMonitor;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
+ public string szPhysicalMonitorDescription;
+ }
+
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Rect
+ {
+ public int Left;
+ public int Top;
+ public int Right;
+ public int Bottom;
+ }
+
+
+
+}
diff --git a/Properties/PublishProfiles/FolderProfile.pubxml b/Properties/PublishProfiles/FolderProfile.pubxml
new file mode 100644
index 0000000..9ccab2a
--- /dev/null
+++ b/Properties/PublishProfiles/FolderProfile.pubxml
@@ -0,0 +1,14 @@
+
+
+
+
+ FileSystem
+ Release
+ Any CPU
+ netcoreapp3.0
+ bin\Release\netcoreapp3.0\publish\
+ false
+
+
\ No newline at end of file
diff --git a/Properties/PublishProfiles/FolderProfile.pubxml.user b/Properties/PublishProfiles/FolderProfile.pubxml.user
new file mode 100644
index 0000000..1a189e4
--- /dev/null
+++ b/Properties/PublishProfiles/FolderProfile.pubxml.user
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..502e69d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,39 @@
+# Monitor Control
+
+Control brightness of connected monitors and also switch them on/off, specially designed for Laptop based setup with Windows 10.
+
+
+
+## Features
+- Change brightness setting per monitor
+- Power on/off each monitor
+- Show monitors orientation
+- Show monitors using the horizontal position in the setup
+
+## Instalation
+
+Requires Microsoft .NET Core Runtime 3.0, download from
+https://dotnet.microsoft.com/download/dotnet-core/3.0/runtime
+
+**Note**: Some Laptop screen and external monitors are not supported.
+
+
+## TODO List:
+- Identify monitors with a number on the screen
+- Change brightness in sync with main display using windows control or prefined keys
+
+
+## Licence
+
+Copyright ©2019 Ricardo Tejo
+
+MIT License:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+*Icon derived from an Icon made by [Freepik](https://www.flaticon.com/authors/freepik) from [Flaticon](https://www.flaticon.com/)*
diff --git a/icon.afdesign b/icon.afdesign
new file mode 100644
index 0000000..d3e1961
Binary files /dev/null and b/icon.afdesign differ
diff --git a/icon.ico b/icon.ico
new file mode 100644
index 0000000..dcabc71
Binary files /dev/null and b/icon.ico differ
diff --git a/ui.png b/ui.png
new file mode 100644
index 0000000..cac3f09
Binary files /dev/null and b/ui.png differ