aboutsummaryrefslogtreecommitdiffstats
path: root/WarframeClock/OverlayWindow.xaml.cs
blob: 520828ecd661727f669fbbff89329a52f633f34d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;

namespace WarframeClock
{
    /// <summary>
    /// Interaction logic for OverlayWindow.xaml
    /// </summary>
    public partial class OverlayWindow : Window
    {
        private readonly IntPtr windowHandle;
        private readonly DispatcherTimer updateTimer;
        private bool xTopmost;
        private IntPtr targetWindowHandle;
        private Native.Rect parentBounds;

        public OverlayWindow()
        {
            InitializeComponent();
            windowHandle = new WindowInteropHelper(this).EnsureHandle();
            xTopmost = false;

            updateTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(100), DispatcherPriority.Background,
                UpdateTimerTick, Dispatcher.CurrentDispatcher);
            updateTimer.Start();
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            updateTimer.Stop();
        }

        private void UpdateTimerTick(object sender, EventArgs e)
        {
            if (!GetTargetWindow(out var newBounds))
            {
                if (IsVisible)
                {
                    Logging("Hide overlay; target window disappeared");
                    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 != xTopmost))
            {
                // 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.
                // So, let's use SetWindowPos directly instead.
                if (currentActiveWindow == targetWindowHandle)
                {
                    Logging("Overlay is now top-most");
                    xTopmost = true;
                }
                else
                {
                    Logging($"Overlay is now non-top-most, behind {currentActiveWindow}");
                    xTopmost = false;
                }

                uint flags =
                    0x0001 /* SWP_NOSIZE */ |
                    0x0002 /* SWP_NOMOVE */ |
                    0x0008 /* SWP_NOREDRAW */ |
                    0x0010 /* SWP_NOACTIVATE */ |
                    0x0200 /* SWP_NOOWNERZORDER */ |
                    0x0400 /* SWP_NOSENDCHANGING */;
                Native.SetWindowPos(windowHandle, xTopmost ? (IntPtr) (-1) : currentActiveWindow, 0, 0, 0, 0, flags);
            }

            // Adjust window position
            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....
                Logging($"Move overlay to: l={parentBounds.Left};t={parentBounds.Top};w={width};h={height}");
                Native.SetWindowPos(windowHandle, IntPtr.Zero, parentBounds.Left, parentBounds.Top, width, height,
                    0x0004 /* SWP_NOZORDER */);
            }

            // Render text
            OverlayLabel.Content = Clock.OverlayString();
            if (!IsVisible)
            {
                Logging("Show overlay");
                Show();
            }
        }

        private bool GetTargetWindow(out Native.Rect bounds)
        {
            if (targetWindowHandle == IntPtr.Zero)
            {
                foreach (var process in Process.GetProcesses())
                {
                    if (targetWindowHandle == IntPtr.Zero &&
                        (process.ProcessName == "Warframe" || process.ProcessName == "Warframe.x64"))
                    {
                        try
                        {
                            targetWindowHandle = process.MainWindowHandle;
                            if (targetWindowHandle != IntPtr.Zero)
                            {
                                Logging($"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();
                }
            }

            if (targetWindowHandle == IntPtr.Zero)
            {
                // Suppress compile error; caller must not use the value
                bounds = new Native.Rect();
                return false;
            }

            if (!Native.GetWindowRect(targetWindowHandle, out bounds))
            {
                Logging($"GetWindowRect failed; aborting this cycle");
                targetWindowHandle = IntPtr.Zero;
                return false;
            }

            return true;
        }

        private void Logging(string message)
        {
            Console.WriteLine($"ClockOverlay: {message}");
        }
    }
}