aboutsummaryrefslogtreecommitdiffstats
path: root/WarframeClock/OverlayWindow.xaml.cs
diff options
context:
space:
mode:
Diffstat (limited to 'WarframeClock/OverlayWindow.xaml.cs')
-rw-r--r--WarframeClock/OverlayWindow.xaml.cs145
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();
+ }
+ }
+ }
+ }
+}