From 9f72bf5d496c8e2a65dfde57d7fe25aaff91d7ab Mon Sep 17 00:00:00 2001 From: Nguyen Duy Date: Mon, 16 Dec 2024 09:32:22 +0700 Subject: [PATCH] refactor: add vdd controller - remove startup driver check - improve errors handling - update the way to fetch driver version --- app/CLI.cs | 106 ++++++++++---------- app/Components/CustomPage.xaml.cs | 12 +-- app/Components/DisplayItem.xaml.cs | 5 +- app/Daemon.cs | 42 -------- app/Languages/en.xaml | 2 + app/MainWindow.xaml.cs | 7 +- app/Program.cs | 54 +--------- app/Tray.cs | 149 +++++++++++++++++++++++----- app/Vdd/Controller.cs | 152 +++++++++++++++++++++++++++++ app/Vdd/Errors.cs | 44 +++++++++ 10 files changed, 390 insertions(+), 183 deletions(-) delete mode 100644 app/Daemon.cs create mode 100644 app/Vdd/Controller.cs create mode 100644 app/Vdd/Errors.cs diff --git a/app/CLI.cs b/app/CLI.cs index 5d37d35..5013bf3 100644 --- a/app/CLI.cs +++ b/app/CLI.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text.RegularExpressions; @@ -9,6 +8,25 @@ namespace ParsecVDisplay { internal static class CLI { + static IntPtr VddHandle = IntPtr.Zero; + + static void ShowHelp() + { + Console.WriteLine("vdd command [args...]"); + Console.WriteLine(" -a|add - Add a virtual display"); + Console.WriteLine(" -r|remove - Remove the last added virtual display"); + Console.WriteLine(" X - Remove the virtual display at index X (number)"); + Console.WriteLine(" all - Remove all the added virtual displays"); + Console.WriteLine(" -l|list - Show all the added virtual displays and specs"); + Console.WriteLine(" -s|set X WxH - Set resolution for a virtual display"); + Console.WriteLine(" where X is index number, WxH is size, e.g 1920x1080"); + Console.WriteLine(" X @R - Set only the refresh rate R, e.g @60, @120 (hz)"); + Console.WriteLine(" on Powershell, you should replace '@' with 'r'"); + Console.WriteLine(" X WxH@R - Set full display mode as above, e.g 1920x1080@144"); + Console.WriteLine(" -v|version - Query driver version and status"); + Console.WriteLine(" -h|help - Show this help"); + } + public static int Execute(string[] args) { AttachConsole(-1); @@ -19,21 +37,31 @@ namespace ParsecVDisplay { switch (args[0]) { + case "-a": case "add": return AddDisplay(); + + case "-r": case "remove": return RemoveDisplay(args); + + case "-l": case "list": return ListDisplay(); + + case "-s": case "set": return SetDisplayMode(args); - case "status": - return QueryDriverStatus(); + + case "-v": case "version": - return QueryDriverVersion(); + return QueryVersion(); + + case "-h": case "help": ShowHelp(); return 0; + default: Console.WriteLine("Invalid command '{0}'", args[0]); ShowHelp(); @@ -50,7 +78,7 @@ namespace ParsecVDisplay } finally { - ParsecVDD.Uninit(); + Vdd.Core.CloseHandle(VddHandle); } } else @@ -62,7 +90,7 @@ namespace ParsecVDisplay static Device.Status PrepareVdd() { - var status = ParsecVDD.QueryStatus(); + var status = Vdd.Core.QueryStatus(out var _); if (status == Device.Status.NOT_INSTALLED) { @@ -73,7 +101,7 @@ namespace ParsecVDisplay throw new Exception($"The driver is not OK, got status {status}"); } - if (!ParsecVDD.Init()) + if (!Vdd.Core.OpenHandle(out VddHandle)) { throw new Exception("Failed to obtain the driver device handle"); } @@ -91,16 +119,18 @@ namespace ParsecVDisplay static int AddDisplay() { - var displays = ParsecVDD.GetDisplays(); - if (displays.Count >= ParsecVDD.MAX_DISPLAYS) + var displays = Vdd.Core.GetDisplays(); + int maxCount = Vdd.Core.MAX_DISPLAYS; + + if (displays.Count >= maxCount) { - throw new Exception(string.Format("Exceeded limit ({0}), could not add more displays", ParsecVDD.MAX_DISPLAYS)); + throw new Exception(string.Format("Exceeded limit ({0}), could not add more displays", maxCount)); } PrepareVdd(); CheckAppRunning(); - if (ParsecVDD.AddDisplay(out int index)) + if (Vdd.Core.AddDisplay(VddHandle, out int index)) { Console.WriteLine($"Added a virtual display with index {0}.", index); return index; @@ -119,7 +149,7 @@ namespace ParsecVDisplay if (args.Length == 1 || removeAll || int.TryParse(arg1, out index)) { - var displays = ParsecVDD.GetDisplays(); + var displays = Vdd.Core.GetDisplays(); if (displays.Count == 0) { @@ -131,7 +161,7 @@ namespace ParsecVDisplay PrepareVdd(); foreach (var di in displays) { - if (!ParsecVDD.RemoveDisplay(di.DisplayIndex)) + if (!Vdd.Core.RemoveDisplay(VddHandle, di.DisplayIndex)) throw new Exception(string.Format("Failed to remove the display at index {0}.", index)); } @@ -146,7 +176,7 @@ namespace ParsecVDisplay if (display != null) { PrepareVdd(); - if (!ParsecVDD.RemoveDisplay(display.DisplayIndex)) + if (!Vdd.Core.RemoveDisplay(VddHandle, display.DisplayIndex)) throw new Exception(string.Format("Failed to remove the display at index {0}.", display.DisplayIndex)); Console.WriteLine("Removed display at index {0}.", display.DisplayIndex); @@ -166,7 +196,7 @@ namespace ParsecVDisplay static int ListDisplay() { - var displays = ParsecVDD.GetDisplays(); + var displays = Vdd.Core.GetDisplays(); if (displays.Count > 0) { @@ -182,7 +212,7 @@ namespace ParsecVDisplay } else { - Console.WriteLine("No Parsec Display available."); + Console.WriteLine("No virtual displays present."); } return 0; @@ -200,7 +230,7 @@ namespace ParsecVDisplay if (int.TryParse(argIndex, out int index)) { - var displays = ParsecVDD.GetDisplays(); + var displays = Vdd.Core.GetDisplays(); if (displays.Count == 0) { @@ -241,47 +271,17 @@ namespace ParsecVDisplay } } - static int QueryDriverStatus() + static int QueryVersion() { - var status = ParsecVDD.QueryStatus(); - Console.WriteLine("The driver status is {0}", status); + var status = Vdd.Core.QueryStatus(out var version); + + Console.WriteLine(Vdd.Core.ADAPTER); + Console.WriteLine("- Status: {0}", status); + Console.WriteLine("- Version: {0}.{1}", version.Major, version.Minor); return (int)status; } - static int QueryDriverVersion() - { - PrepareVdd(); - - if (ParsecVDD.QueryVersion(out var version)) - { - Console.WriteLine("{0} v{1}", ParsecVDD.ADAPTER, version); - return 0; - } - else - { - throw new Exception("Failed to query the driver version."); - } - } - - static void ShowHelp() - { - Console.WriteLine("vdd command [args...]"); - Console.WriteLine(" add - Add a virtual display"); - Console.WriteLine(" remove - Remove the last added virtual display"); - Console.WriteLine(" X - Remove the virtual display at index X (number)"); - Console.WriteLine(" all - Remove all the added virtual displays"); - Console.WriteLine(" list - Show all the added virtual displays and specs"); - Console.WriteLine(" set X WxH - Set resolution for a virtual display"); - Console.WriteLine(" where X is index number, WxH is size, e.g 1920x1080"); - Console.WriteLine(" X @R - Set only the refresh rate R, e.g @60, @120 (hz)"); - Console.WriteLine(" on Powershell, you should replace '@' with 'r'"); - Console.WriteLine(" X WxH@R - Set full display mode as above, e.g 1920x1080@144"); - Console.WriteLine(" status - Query the driver status"); - Console.WriteLine(" version - Query the driver version"); - Console.WriteLine(" help - Show this help"); - } - static void ParseDisplayModeArg(string arg, out int? width, out int? height, out int? hz) { Match match; diff --git a/app/Components/CustomPage.xaml.cs b/app/Components/CustomPage.xaml.cs index 7876126..bc0a769 100644 --- a/app/Components/CustomPage.xaml.cs +++ b/app/Components/CustomPage.xaml.cs @@ -16,7 +16,7 @@ namespace ParsecVDisplay.Components InitializeComponent(); xParentGPU.Items.Clear(); - foreach (var item in Enum.GetValues(typeof(ParsecVDD.ParentGPU))) + foreach (var item in Enum.GetValues(typeof(Vdd.Utils.ParentGPU))) { xParentGPU.Items.Add(item.ToString()); } @@ -50,15 +50,15 @@ namespace ParsecVDisplay.Components } } - ParsecVDD.ParentGPU parentGPU; + Vdd.Utils.ParentGPU parentGPU; bool validParentGPU = Enum.TryParse(xParentGPU.SelectedValue.ToString(), true, out parentGPU); if (modes.Count > 0 && validParentGPU) { if (Helper.IsAdmin()) { - ParsecVDD.SetCustomDisplayModes(modes); - ParsecVDD.SetParentGPU(parentGPU); + Vdd.Utils.SetCustomDisplayModes(modes); + Vdd.Utils.SetParentGPU(parentGPU); } else { @@ -79,7 +79,7 @@ namespace ParsecVDisplay.Components { TextBoxes = FindVisualChildren(this).ToArray(); - var modes = ParsecVDD.GetCustomDisplayModes(); + var modes = Vdd.Utils.GetCustomDisplayModes(); for (int i = 0, j = 0; i < 15 && j < modes.Count; i += 3, j++) { @@ -88,7 +88,7 @@ namespace ParsecVDisplay.Components TextBoxes[i + 2].Text = $"{modes[j].Hz}"; } - var parentGPU = ParsecVDD.GetParentGPU(); + var parentGPU = Vdd.Utils.GetParentGPU(); xParentGPU.SelectedValue = parentGPU.ToString(); } diff --git a/app/Components/DisplayItem.xaml.cs b/app/Components/DisplayItem.xaml.cs index ecc58f0..62da034 100644 --- a/app/Components/DisplayItem.xaml.cs +++ b/app/Components/DisplayItem.xaml.cs @@ -198,10 +198,7 @@ namespace ParsecVDisplay.Components private void RemoveDisplay(object sender, RoutedEventArgs e) { - if (Display.DisplayIndex >= 0) - { - ParsecVDD.RemoveDisplay(Display.DisplayIndex); - } + Tray.Instance.Invoke(() => Tray.Instance.RemoveDisplay(Display.DisplayIndex)); } } } \ No newline at end of file diff --git a/app/Daemon.cs b/app/Daemon.cs deleted file mode 100644 index c071ef9..0000000 --- a/app/Daemon.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Threading; - -namespace ParsecVDisplay -{ - internal static class Daemon - { - static Thread EventThread; - static CancellationTokenSource Cancellation; - - public static void Start() - { - Cancellation = new CancellationTokenSource(); - var token = Cancellation.Token; - - EventThread = new Thread(() => EventLoop(token)); - EventThread.IsBackground = false; - EventThread.Priority = ThreadPriority.Highest; - - EventThread.Start(); - } - - public static void Stop() - { - Cancellation?.Cancel(); - EventThread?.Join(); - } - - static void EventLoop(CancellationToken cancellation) - { - while (true) - { - if (cancellation.IsCancellationRequested) - break; - - ParsecVDD.Ping(); - - Thread.Sleep(100); - } - } - } -} diff --git a/app/Languages/en.xaml b/app/Languages/en.xaml index af97b74..8accd43 100644 --- a/app/Languages/en.xaml +++ b/app/Languages/en.xaml @@ -50,5 +50,7 @@ Please install the driver first. The driver is not OK, please check again. Current status: {0}. Failed to obtain the device handle, please check the driver installation again. + Failed to add virtual display. + Failed to remove virtual display. \ No newline at end of file diff --git a/app/MainWindow.xaml.cs b/app/MainWindow.xaml.cs index 1810453..50c7540 100644 --- a/app/MainWindow.xaml.cs +++ b/app/MainWindow.xaml.cs @@ -27,6 +27,7 @@ namespace ParsecVDisplay xDisplays.Children.Clear(); xNoDisplay.Visibility = Visibility.Hidden; + this.Title = Program.AppName; this.IsVisibleChanged += delegate { UpdateDriverLabel(); }; } @@ -81,15 +82,15 @@ namespace ParsecVDisplay private void UpdateDriverLabel() { - ParsecVDD.QueryVersion(out var dver); - xDriver.Content = $"{ParsecVDD.NAME} v{dver}"; + Vdd.Controller.QueryStatus(out var ver); + xDriver.Content = $"{Vdd.Core.NAME} v{ver.Major}.{ver.Minor}"; } private void DisplayChanged(object sender, EventArgs e) { Dispatcher.Invoke(() => { - var displays = ParsecVDD.GetDisplays(out bool noMonitors); + var displays = Vdd.Core.GetDisplays(out bool noMonitors); xDisplays.Children.Clear(); xNoDisplay.Visibility = displays.Count > 0 diff --git a/app/Program.cs b/app/Program.cs index 76b1452..dea3963 100644 --- a/app/Program.cs +++ b/app/Program.cs @@ -19,13 +19,13 @@ namespace ParsecVDisplay if (args.Length >= 2 && args[0] == "-custom") { var modes = Display.ParseModes(args[1]); - ParsecVDD.SetCustomDisplayModes(modes); + Vdd.Utils.SetCustomDisplayModes(modes); if (args.Length >= 3) { - if (Enum.TryParse(args[2], true, out var kind)) + if (Enum.TryParse(args[2], true, out var kind)) { - ParsecVDD.SetParentGPU(kind); + Vdd.Utils.SetParentGPU(kind); } } @@ -41,17 +41,9 @@ namespace ParsecVDisplay if (SingleInstance()) { App.LoadTranslations(); + Helper.StayAwake(false); - if (InitDriver()) - { - Helper.StayAwake(false); - Daemon.Start(); - - Application.Run(new Tray()); - - Daemon.Stop(); - ParsecVDD.Uninit(); - } + Application.Run(new Tray()); } return 0; @@ -81,41 +73,5 @@ namespace ParsecVDisplay return isOwned; } - - static bool InitDriver() - { - string error = null; - var status = ParsecVDD.QueryStatus(); - - if (!(status == Device.Status.OK || Config.SkipDriverCheck)) - { - switch (status) - { - case Device.Status.RESTART_REQUIRED: - error = App.GetTranslation("t_msg_must_restart_pc"); - break; - case Device.Status.DISABLED: - error = App.GetTranslation("t_msg_driver_is_disabled", ParsecVDD.ADAPTER); - break; - case Device.Status.NOT_INSTALLED: - error = App.GetTranslation("t_msg_please_install_driver"); - break; - default: - error = App.GetTranslation("t_msg_driver_status_not_ok", status); - break; - } - } - else if (ParsecVDD.Init() != true) - { - error = App.GetTranslation("t_msg_failed_to_obtain_handle"); - } - - if (error != null) - { - MessageBox.Show(error, AppName, MessageBoxButtons.OK, MessageBoxIcon.Warning); - } - - return error == null; - } } } \ No newline at end of file diff --git a/app/Tray.cs b/app/Tray.cs index b0f3d6e..4687985 100644 --- a/app/Tray.cs +++ b/app/Tray.cs @@ -42,6 +42,7 @@ namespace ParsecVDisplay public Tray() { Instance = this; + Vdd.Controller.Start(); GuiThread = new Thread(App.Main); GuiThread.IsBackground = true; @@ -115,41 +116,135 @@ namespace ParsecVDisplay }); } + private void WarnVddStatus(Device.Status status) + { + if (status == Device.Status.OK) + return; + + string error = null; + switch (status) + { + case Device.Status.RESTART_REQUIRED: + error = App.GetTranslation("t_msg_must_restart_pc"); + break; + case Device.Status.DISABLED: + error = App.GetTranslation("t_msg_driver_is_disabled", Vdd.Core.ADAPTER); + break; + case Device.Status.NOT_INSTALLED: + error = App.GetTranslation("t_msg_please_install_driver"); + break; + default: + error = App.GetTranslation("t_msg_driver_status_not_ok", status); + break; + } + + if (error != null) + MessageBox.Show(Owner, error, Program.AppName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + public void HandleVddError(Exception ex) + { + string message = null; + + if (ex is Vdd.ErrorDriverStatus errStatus) + { + switch (errStatus.Status) + { + case Device.Status.RESTART_REQUIRED: + message = App.GetTranslation("t_msg_must_restart_pc"); + break; + case Device.Status.DISABLED: + message = App.GetTranslation("t_msg_driver_is_disabled", Vdd.Core.ADAPTER); + break; + case Device.Status.NOT_INSTALLED: + message = App.GetTranslation("t_msg_please_install_driver"); + break; + default: + message = App.GetTranslation("t_msg_driver_status_not_ok", errStatus.Status); + break; + } + } + else if (ex is Vdd.ErrorDeviceHandle) + { + message = App.GetTranslation("t_msg_failed_to_obtain_handle"); + } + else if (ex is Vdd.ErrorExceededLimit errLimit) + { + message = App.GetTranslation("t_msg_exceeded_display_limit", errLimit.Limit); + } + else if (ex is Vdd.ErrorOperationFailed errOperation) + { + switch (errOperation.Type) + { + case Vdd.ErrorOperationFailed.Operation.AddDisplay: + message = App.GetTranslation("t_msg_failed_to_add_display"); + break; + case Vdd.ErrorOperationFailed.Operation.RemoveDisplay: + message = App.GetTranslation("t_msg_failed_to_remove_display"); + break; + } + } + else + { + message = ex.ToString(); + } + + if (message != null) + { + MessageBox.Show(Owner, message, + Program.AppName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } + void RestoreDisplays() { - ParsecVDD.Ping(); - var savedCount = Config.DisplayCount; + //var savedCount = Config.DisplayCount; - if (savedCount > 0) - { - var displays = ParsecVDD.GetDisplays(); - var amount = savedCount - displays.Count; + //if (savedCount > 0) + //{ + // var displays = Vdd.Core.GetDisplays(); + // var amount = savedCount - displays.Count; - for (int i = 0; i < amount; i++) - ParsecVDD.AddDisplay(out var _); - } + // for (int i = 0; i < amount; i++) + // Controller.AddDisplay(out var _); + //} } public void AddDisplay(object sender, EventArgs e) { - if (ParsecVDD.GetDisplays().Count >= ParsecVDD.MAX_DISPLAYS) + try { - MessageBox.Show(Owner, App.GetTranslation("t_msg_exceeded_display_limit", ParsecVDD.MAX_DISPLAYS), - Program.AppName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + Vdd.Controller.AddDisplay(); } - else + catch (Exception ex) { - ParsecVDD.AddDisplay(out var _); + HandleVddError(ex); + } + } + + public void RemoveDisplay(int index) + { + try + { + Vdd.Controller.RemoveDisplay(index); + } + catch (Vdd.ErrorOperationFailed) + { + MessageBox.Show(Owner, App.GetTranslation("t_msg_failed_to_remove_display"), + Program.AppName, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } void RemoveLastDisplay(object sender, EventArgs e) { - var displays = ParsecVDD.GetDisplays(); - if (displays.Count > 0) + try { - var last = displays[displays.Count - 1]; - ParsecVDD.RemoveDisplay(last.DisplayIndex); + Vdd.Controller.RemoveLastDisplay(); + } + catch (Vdd.ErrorOperationFailed) + { + MessageBox.Show(Owner, App.GetTranslation("t_msg_failed_to_remove_display"), + Program.AppName, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } @@ -157,12 +252,12 @@ namespace ParsecVDisplay { ShowApp(); - var status = ParsecVDD.QueryStatus(); - ParsecVDD.QueryVersion(out string version); - + var status = Vdd.Core.QueryStatus(out var version); var caption = $"{Program.AppName} v{Program.AppVersion}"; - MessageBox.Show(Owner, $"Parsec Virtual Display v{version}\n" + + MessageBox.Show(Owner, + $"{Vdd.Core.ADAPTER}\n" + + $"Version: {version}\n" + $"{App.GetTranslation("t_msg_driver_status")}: {status}", caption, MessageBoxButtons.OK, MessageBoxIcon.Information); } @@ -206,7 +301,7 @@ namespace ParsecVDisplay if (sender == MI_RunOnStartup) Config.RunOnStartup = MI_RunOnStartup.Checked; else if (sender == MI_RestoreDisplays) - Config.DisplayCount = MI_RestoreDisplays.Checked ? ParsecVDD.GetDisplays().Count : -1; + Config.DisplayCount = MI_RestoreDisplays.Checked ? Vdd.Core.GetDisplays().Count : -1; else if (sender == MI_FallbackDisplay) Config.FallbackDisplay = MI_FallbackDisplay.Checked; else if (sender == MI_KeepScreenOn) @@ -217,7 +312,7 @@ namespace ParsecVDisplay { if (Config.DisplayCount >= 0) { - var displays = ParsecVDD.GetDisplays(); + var displays = Vdd.Core.GetDisplays(); Config.DisplayCount = displays.Count; } } @@ -265,7 +360,7 @@ namespace ParsecVDisplay void Exit(object sender, EventArgs e) { - var displays = ParsecVDD.GetDisplays(); + var displays = Vdd.Core.GetDisplays(); if (displays.Count > 0) { if (MessageBox.Show(Owner, App.GetTranslation("t_msg_prompt_leave_all"), @@ -282,7 +377,7 @@ namespace ParsecVDisplay for (int i = displays.Count - 1; i >= 0; i--) { var index = displays[i].DisplayIndex; - ParsecVDD.RemoveDisplay(index); + Vdd.Controller.RemoveDisplay(index); } if (Config.DisplayCount >= 0) @@ -292,6 +387,8 @@ namespace ParsecVDisplay App.Current?.Dispatcher.Invoke(App.Current.Shutdown); GuiThread.Join(); + Vdd.Controller.Stop(); + TrayIcon.Visible = false; Application.Exit(); } diff --git a/app/Vdd/Controller.cs b/app/Vdd/Controller.cs new file mode 100644 index 0000000..e9b1284 --- /dev/null +++ b/app/Vdd/Controller.cs @@ -0,0 +1,152 @@ +using System; +using System.Diagnostics; +using System.Threading; + +namespace ParsecVDisplay.Vdd +{ + internal static class Controller + { + static Thread UpdateThread; + static Thread StatusThread; + static CancellationTokenSource Cancellation; + static IntPtr VddHandle = IntPtr.Zero; + + static Device.Status LastStatus; + + public static void Start() + { + Cancellation = new CancellationTokenSource(); + + UpdateThread = new Thread(() => UpdateLoop(Cancellation.Token)); + UpdateThread.IsBackground = false; + UpdateThread.Priority = ThreadPriority.Highest; + + StatusThread = new Thread(() => StatusLoop(Cancellation.Token)); + + UpdateThread.Start(); + StatusThread.Start(); + } + + public static void Stop() + { + Cancellation?.Cancel(); + UpdateThread?.Join(); + StatusThread?.Join(); + + Device.CloseHandle(VddHandle); + } + + static void UpdateLoop(CancellationToken cancellation) + { + while (true) + { + if (cancellation.IsCancellationRequested) + break; + + if (VddHandle.IsValidHandle() && LastStatus == Device.Status.OK) + Core.Update(VddHandle); + + Thread.Sleep(100); + } + } + + static void StatusLoop(CancellationToken cancellation) + { + var sw = Stopwatch.StartNew(); + + while (true) + { + if (cancellation.IsCancellationRequested) + break; + + if (sw.ElapsedMilliseconds >= 500) + { + var status = QueryStatus(out var _); + unsafe + { + fixed (Device.Status* s = &LastStatus) + { + Interlocked.Exchange(ref *(int*)s, (int)status); + } + } + + if (status == Device.Status.OK) + { + if (!VddHandle.IsValidHandle()) + { + Device.OpenHandle(Core.ADAPTER_GUID, out var handle); + Interlocked.Exchange(ref VddHandle, handle); + } + } + else + { + var handle = VddHandle; + Interlocked.Exchange(ref VddHandle, IntPtr.Zero); + Device.CloseHandle(handle); + } + + sw.Restart(); + } + + Thread.Sleep(50); + } + } + + public static Device.Status QueryStatus(out Version version) + { + return Device.QueryStatus(Core.CLASS_GUID, Core.HARDWARE_ID, out version); + } + + public static Device.Status QueryStatus() + { + return QueryStatus(out var _); + } + + public static void AddDisplay() + { + var status = QueryStatus(); + if (status != Device.Status.OK) + throw new ErrorDriverStatus(status); + + int limit = Core.MAX_DISPLAYS; + var displays = Core.GetDisplays(); + + if (displays.Count >= limit) + { + throw new ErrorExceededLimit(limit); + } + else + { + if (!Core.AddDisplay(VddHandle, out var _)) + { + throw new ErrorOperationFailed(ErrorOperationFailed.Operation.AddDisplay); + } + } + } + + public static void RemoveDisplay(int index) + { + var status = QueryStatus(); + if (status != Device.Status.OK) + throw new ErrorDriverStatus(status); + + if (index >= 0) + { + if (!Core.RemoveDisplay(VddHandle, index)) + { + throw new ErrorOperationFailed(ErrorOperationFailed.Operation.RemoveDisplay); + } + } + } + + public static void RemoveLastDisplay() + { + var displays = Core.GetDisplays(); + if (displays.Count > 0) + { + var last = displays[displays.Count - 1]; + RemoveDisplay(last.DisplayIndex); + } + } + } +} \ No newline at end of file diff --git a/app/Vdd/Errors.cs b/app/Vdd/Errors.cs new file mode 100644 index 0000000..feb0988 --- /dev/null +++ b/app/Vdd/Errors.cs @@ -0,0 +1,44 @@ +using System; + +namespace ParsecVDisplay.Vdd +{ + internal class ErrorDriverStatus : Exception + { + public readonly Device.Status Status; + + public ErrorDriverStatus(Device.Status status) + { + this.Status = status; + } + } + + internal class ErrorDeviceHandle : Exception + { + } + + internal class ErrorExceededLimit : Exception + { + public readonly int Limit; + + public ErrorExceededLimit(int limit) + { + this.Limit = limit; + } + } + + internal class ErrorOperationFailed : Exception + { + public enum Operation + { + AddDisplay, + RemoveDisplay, + } + + public readonly Operation Type; + + public ErrorOperationFailed(Operation type) + { + this.Type = type; + } + } +} \ No newline at end of file