From 25e2b804e9a9c929971dcfa18645958e14520ae8 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 28 May 2020 20:01:50 +0100 Subject: [PATCH] added basic keyboard overlay: -show keys & keys with modifiers when they're typed --- HighlightInput/HighlightInput.csproj | 1 + HighlightInput/KeyboardOverlay.cs | 173 +++++++++++++++++++++++++++ HighlightInput/Program.cs | 7 +- 3 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 HighlightInput/KeyboardOverlay.cs diff --git a/HighlightInput/HighlightInput.csproj b/HighlightInput/HighlightInput.csproj index 4766987..4746df2 100644 --- a/HighlightInput/HighlightInput.csproj +++ b/HighlightInput/HighlightInput.csproj @@ -60,6 +60,7 @@ + diff --git a/HighlightInput/KeyboardOverlay.cs b/HighlightInput/KeyboardOverlay.cs new file mode 100644 index 0000000..8f75890 --- /dev/null +++ b/HighlightInput/KeyboardOverlay.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace HighlightInput +{ + class KeyboardOverlay : IDisposable + { + #region Tweakables + readonly double c_fadeMillis = TimeSpan.FromSeconds(1).TotalMilliseconds; + readonly double c_fadeStartDelay = TimeSpan.FromSeconds(1).TotalMilliseconds; + + readonly float c_percentFromBottom = 0.2f; + readonly int c_paddingX = 20; + readonly int c_paddingY = 10; + readonly int c_border = 6; + + readonly Dictionary c_keyReplacements = new Dictionary() + { + { Keys.Oem8, "`" }, + { Keys.D0, "0" }, { Keys.D1, "1" }, { Keys.D2, "2" }, { Keys.D3, "3" }, { Keys.D4, "4" }, { Keys.D5, "5" }, { Keys.D6, "6" }, { Keys.D7, "7" }, { Keys.D8, "8" }, { Keys.D9, "9" }, + { Keys.Back, "Backspace" }, + { Keys.OemMinus, "-" }, { Keys.Oemplus, "+" }, + }; + #endregion + + #region State + private object m_lock = new object(); + private GameOverlay.Windows.GraphicsWindow m_overlay; + private GameOverlay.Windows.WindowBounds m_desktopBounds; + + private DateTime m_shownTime = DateTime.MinValue; + private string m_text; + #endregion + + public KeyboardOverlay() + { + GameOverlay.Drawing.Rectangle rect = GetDesktopRect(); + m_overlay = new GameOverlay.Windows.GraphicsWindow((int)rect.Left, (int)rect.Top, (int)(rect.Right - rect.Left), (int)(rect.Bottom - rect.Top), + new GameOverlay.Drawing.Graphics() { PerPrimitiveAntiAliasing = true, TextAntiAliasing = true }); + + m_overlay.DrawGraphics += DrawGraphics; + m_overlay.FPS = 60; + m_overlay.Create(); + + m_overlay.IsTopmost = true; + m_overlay.Show(); + } + + //TODO: re-check desktop size occasionally & call m_overlay.Resize with this rect + private GameOverlay.Drawing.Rectangle GetDesktopRect() + { + lock (m_lock) + { + var desktop = GameOverlay.Windows.WindowHelper.GetDesktopWindow(); + + if (desktop == IntPtr.Zero) + { + throw new Exception("Unable to get the desktop window"); + } + + if (!GameOverlay.Windows.WindowHelper.GetWindowBounds(desktop, out m_desktopBounds)) + { + throw new Exception("Unable to get the desktop bounds"); + } + + int middleX = m_desktopBounds.Right / 2; + int middleY = (int)(m_desktopBounds.Bottom * (1.0f - c_percentFromBottom)); + + //define a max size for the overlay, but what the user sees will be much smaller (measures the text) + int maxWidth = 1000; + int maxHeight = 200; + + return GameOverlay.Drawing.Rectangle.Create(middleX - maxWidth / 2, middleY, maxWidth, maxHeight); + } + } + + public void KeyDown(KeyEventArgs args) + { + lock(m_lock) + { + //exit shortcut: Ctrl Alt Escape + if(args.Control && args.Alt && args.KeyCode == Keys.Escape) + { + args.Handled = true; + Application.Exit(); + } + + string str = args.KeyCode.ToString(); + + //do we have a nice alias? + if(c_keyReplacements.ContainsKey(args.KeyCode)) + { + str = c_keyReplacements[args.KeyCode]; + } + + //don't show any modifiers on their own + //TODO: stop combinations of modifiers showing without actual keys + if (args.Modifiers == Keys.None && (str.Contains("Shift") || str.Contains("Control") || str.Contains("Menu"))) + { + return; + } + + m_text = args.Modifiers == 0 ? str : $"{args.Modifiers} + {str}"; + m_shownTime = DateTime.Now; + } + } + + //TODO: hook setup & destroy graphics properly + private void DrawGraphics(object sender, GameOverlay.Windows.DrawGraphicsEventArgs e) + { + lock (m_lock) + { + e.Graphics.ClearScene(); + + if (!string.IsNullOrEmpty(m_text)) + { + GameOverlay.Drawing.Font font = e.Graphics.CreateFont("Calibri", 50, true); + GameOverlay.Drawing.Point textSize = e.Graphics.MeasureString(font, m_text); + + int middleX = m_overlay.Width / 2; + int middleY = m_overlay.Height / 2; + + double millisElapsed = DateTime.Now.Subtract(m_shownTime).TotalMilliseconds; + if (millisElapsed > c_fadeStartDelay + c_fadeMillis) + { + return; + } + + float alpha = 1.0f; + if(millisElapsed > c_fadeStartDelay) + { + alpha = 1.0f - (float)((millisElapsed - c_fadeStartDelay) / c_fadeMillis); + } + + var backColour = e.Graphics.CreateSolidBrush(0.6f, 0.6f, 0.6f, 0.85f * alpha); + var fontColour = e.Graphics.CreateSolidBrush(0f, 0f, 0f, alpha); + + var rect = GameOverlay.Drawing.Rectangle.Create((int)(middleX - textSize.X / 2), (int)(middleY - textSize.Y / 2), (int)textSize.X, (int)textSize.Y); + Inflate(ref rect, c_paddingX, c_paddingY); + + e.Graphics.FillRectangle(backColour, rect); + if(c_border > 0) + { + Inflate(ref rect, c_border / 2, c_border / 2); + e.Graphics.DrawRectangle(fontColour, rect, c_border); + } + + e.Graphics.DrawText(font, fontColour, middleX - textSize.X / 2, middleY - textSize.Y / 2, m_text); + } + } + } + + public void Dispose() + { + lock (m_lock) + { + m_overlay.Dispose(); + } + } + + private void Inflate(ref GameOverlay.Drawing.Rectangle rect, int inflateX, int inflateY) + { + rect.Left -= inflateX; + rect.Top -= inflateY; + rect.Right += inflateX; + rect.Bottom += inflateY; + } + } +} diff --git a/HighlightInput/Program.cs b/HighlightInput/Program.cs index 5481933..c416397 100644 --- a/HighlightInput/Program.cs +++ b/HighlightInput/Program.cs @@ -6,12 +6,12 @@ namespace HighlightInput { class Program { - static void Main(string[] args) { var globalHooker = new MKB.WinApi.GlobalHooker(); using (var mouseOverlay = new MouseOverlay()) + using (var kbOverlay = new KeyboardOverlay()) using (var msListener = new MKB.MouseHookListener(globalHooker)) using (var kbListener = new MKB.KeyboardHookListener(globalHooker)) { @@ -19,10 +19,7 @@ static void Main(string[] args) msListener.MouseMove += (sender, eventArgs) => mouseOverlay.MouseMove(eventArgs); msListener.MouseUp += (sender, eventArgs) => mouseOverlay.MouseUp(eventArgs); - kbListener.KeyDown += (object sender, KeyEventArgs eventArgs) => - { - Console.WriteLine(eventArgs.KeyCode); - }; + kbListener.KeyDown += (sender, eventArgs) => kbOverlay.KeyDown(eventArgs); msListener.Start(); kbListener.Start();