aboutsummaryrefslogtreecommitdiffstats
path: root/WarframeClock/OverlayWindow.xaml.cs
blob: 8ce8f1cfb937b066e8d54920dffa28944cb49656 (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
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();
                }
            }
        }
    }
}