diff options
Diffstat (limited to 'WarframeClock/OverlayWindow.xaml.cs')
-rw-r--r-- | WarframeClock/OverlayWindow.xaml.cs | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/WarframeClock/OverlayWindow.xaml.cs b/WarframeClock/OverlayWindow.xaml.cs new file mode 100644 index 0000000..8ce8f1c --- /dev/null +++ b/WarframeClock/OverlayWindow.xaml.cs @@ -0,0 +1,145 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Interop; + +namespace WarframeClock +{ + public partial class OverlayWindow : Window + { + private IntPtr targetWindowHandle; + private Native.Rect parentBounds = new Native.Rect() + { + Top = 0, + Left = 0, + Right = 300, + Bottom = 300, + }; + + public IntPtr WindowHandle + { + get; private set; + } + + public OverlayWindow() + { + InitializeComponent(); + WindowHandle = new WindowInteropHelper(this).EnsureHandle(); + } + + public void Start() + { + var updateLoop = Task.Factory.StartNew(() => + { + while (true) + { + Dispatcher.BeginInvoke(new Action(() => Update())); + Thread.Sleep(100); + } + }); + System.Windows.Threading.Dispatcher.Run(); + } + + private void Update() + { + var handle = WindowHandle; + + GetTargetWindow(); + if (targetWindowHandle == IntPtr.Zero || + Native.GetWindowRect(targetWindowHandle, out Native.Rect newBounds) == 0) + { + targetWindowHandle = IntPtr.Zero; + if (IsVisible) + { + Console.WriteLine($"ClockOverlay ({handle}): Hide"); + Hide(); + } + return; + } + + // Set top-most flag if the target window is foreground. Move to + // behind the current foreground window otherwise. + var currentActiveWindow = Native.GetForegroundWindow(); + if (currentActiveWindow != IntPtr.Zero && + (!IsVisible || currentActiveWindow == targetWindowHandle != Topmost)) + { + if (currentActiveWindow == targetWindowHandle) + { + Console.WriteLine($"ClockOverlay ({handle}): Overlay is now top-most"); + Topmost = true; + } + else + { + Console.WriteLine($"ClockOverlay ({handle}): Overlay is now non-top-most, " + + $"behind {currentActiveWindow}"); + + // Setting Topmost to false brings the overlay to just + // behind the other top-most window -- above the foreground + // window. This is not what we want to do here. + // Setting Topmost is still needed to update the private + // backing fields. + Topmost = false; + Native.SetWindowPos(handle, currentActiveWindow, 0, 0, 0, 0, + 0x0001 /* SWP_NOSIZE */ | 0x0002 /* SWP_NOMOVE */ | 0x0010 /* SWP_NOACTIVATE */); + } + } + + if (!IsVisible || + parentBounds.Left != newBounds.Left || parentBounds.Right != newBounds.Right || + parentBounds.Top != newBounds.Top || parentBounds.Bottom != newBounds.Bottom) + { + parentBounds = newBounds; + + var width = parentBounds.Right - parentBounds.Left; + var height = parentBounds.Bottom - parentBounds.Top; + + // Do not use window.Left (and so on) because they're DPI-aware. + // We don't want that.... + Console.WriteLine($"ClockOverlay ({handle}): Move window to: " + + $"l={parentBounds.Left};t={parentBounds.Top};w={width};h={height}"); + Native.SetWindowPos(handle, IntPtr.Zero, parentBounds.Left, parentBounds.Top, width, height, + 0x0004 /* SWP_NOZORDER */); + } + + // Render text + var infoText = $"{Clock.CetusExpiryString()} {Clock.VallisExpiryString()}"; + OverlayLabel.Content = infoText; + if (!IsVisible) + { + Console.WriteLine($"ClockOverlay ({handle}): Show"); + Show(); + } + } + + private void GetTargetWindow() + { + if (targetWindowHandle == IntPtr.Zero || Native.IsWindow(targetWindowHandle) == 0) + { + targetWindowHandle = IntPtr.Zero; + + var ary = System.Diagnostics.Process.GetProcesses(); + foreach (var process in ary) + { + if (process.ProcessName == "Warframe" || process.ProcessName == "Warframe.x64") + { + try + { + targetWindowHandle = process.MainWindowHandle; + if (targetWindowHandle != IntPtr.Zero) + { + Console.WriteLine($"ClockOverlay: Found a process with name={process.ProcessName}; " + + $"MainWindowHandle={targetWindowHandle}"); + } + } + catch (Exception) + { + // Ignore errors; maybe the process is just exiting. Let's retry during the next cycle. + } + } + process.Dispose(); + } + } + } + } +} |