aboutsummaryrefslogtreecommitdiffstats
path: root/WarframeClock/OverlayWindowBase.cs
blob: 0479d304319a2367755c3c030fa0235b46d5c5cb (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
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Interop;

namespace WarframeClock
{
    public class OverlayWindowBase : Window
    {
        private IntPtr _windowHandle;
        private bool _xTopmost;
        private IntPtr _targetWindowHandle;
        private Native.Rect _parentBounds;

        public OverlayWindowBase()
        {
            _xTopmost = false;
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            _windowHandle = new WindowInteropHelper(this).EnsureHandle();

            const int WS_EX_NOACTIVATE = 0x08000000;
            const int GWL_EXSTYLE = -20;
            int exStyle = Native.GetWindowLong(_windowHandle, GWL_EXSTYLE);
            Native.SetWindowLong(_windowHandle, GWL_EXSTYLE, exStyle | WS_EX_NOACTIVATE);
        }

        protected bool AdjustOverlay()
        {
            if (!GetTargetWindow(out var newBounds))
            {
                if (IsVisible)
                {
                    Trace.WriteLine("OverlayWindowBase: Hide overlay; target window disappeared");
                    Hide();
                }
                return false;
            }

            // 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))
            {
                const uint flags =
                       0x0001 /* SWP_NOSIZE */ |
                       0x0002 /* SWP_NOMOVE */ |
                       0x0008 /* SWP_NOREDRAW */ |
                       0x0010 /* SWP_NOACTIVATE */ |
                       0x0200 /* SWP_NOOWNERZORDER */ |
                       0x0400 /* SWP_NOSENDCHANGING */;

                // 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)
                {
                    Trace.WriteLine("OverlayWindowBase: Overlay is now top-most");
                    _xTopmost = true;
                    Native.SetWindowPos(_windowHandle, new IntPtr(-1), 0, 0, 0, 0, flags);
                }
                else
                {
                    Trace.WriteLine($"OverlayWindowBase: Overlay is now non-top-most, behind {currentActiveWindow}");
                    _xTopmost = false;
                    Native.SetWindowPos(_windowHandle, currentActiveWindow, 0, 0, 0, 0, flags);
                    Native.SetWindowPos(_windowHandle, new IntPtr(-2), 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....
                Trace.WriteLine("OverlayWindowBase: 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 */);
            }
            if (!IsVisible)
            {
                Trace.WriteLine("OverlayWindowBase: Show overlay");
                Show();
            }

            return true;
        }

        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)
                            {
                                string message = $"Found a process with name={process.ProcessName}; " +
                                                 $"MainWindowHandle={_targetWindowHandle}";
                                Trace.WriteLine($"OverlayWindowBase: {message}");
                            }
                        }
                        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))
            {
                Trace.WriteLine("OverlayWindowBase: GetWindowRect failed; aborting this cycle");
                _targetWindowHandle = IntPtr.Zero;
                return false;
            }

            return true;
        }
    }
}