aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-08-09 21:49:18 +0200
committerJohn Ankarström <john@ankarstrom.se>2022-08-09 21:49:18 +0200
commit805cc4cec440525629758af918d50a850209ec0b (patch)
tree570077b7091020c899a1160523b7aba0523d3ed9
parentdf2ccebabb5af3b304ba8220e4c9364f8fd9f830 (diff)
downloadEpisodeBrowser-805cc4cec440525629758af918d50a850209ec0b.tar.gz
Add WithNextWindow function.
I'm not sure if this clarifies or complicates the control flow. My hope is the former.
-rw-r--r--c/main.cpp30
-rw-r--r--c/win.cpp130
-rw-r--r--c/win.h4
3 files changed, 100 insertions, 64 deletions
diff --git a/c/main.cpp b/c/main.cpp
index 758889f..e51e0f9 100644
--- a/c/main.cpp
+++ b/c/main.cpp
@@ -52,7 +52,7 @@ BOOL (*IsThemeActive)();
BOOL (*SetWindowTheme)(HWND, const wchar_t*, const wchar_t*);
/* Initialize important global state on parent window creation. */
-static LRESULT CALLBACK CBTProc(int, WPARAM, LPARAM);
+static void InitializeMainWindow(HWND);
/* Process parent window commands. */
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
/* Process main menu commands. */
@@ -66,7 +66,7 @@ static INT_PTR CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
/* Try to style application according to current Windows theme. */
static void UpdateTheme();
-void OnTerminate() noexcept
+static void OnTerminate() noexcept
{
const wchar_t* what = L"an exception";
WcharPtr why;
@@ -128,10 +128,10 @@ int WINAPI WinMain(const HINSTANCE hInstance, const HINSTANCE, char* const, cons
wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);
Require(RegisterClassEx(&wc));
- /* Create window. A CBT hook is used to initialize important
- * global variables before any messages are sent to the new
- * window. It is vital that the hook is set up correctly. */
- const HHOOK hHook = Require(SetWindowsHookEx(WH_CBT, CBTProc, nullptr, GetCurrentThreadId()));
+ /* InitializeMainWindow is called before the first message is
+ * sent to WndProc. This is important, as it initializes
+ * global state on which WndProc relies. */
+ WithNextWindow(InitializeMainWindow);
const HWND hWnd = Require(CreateWindowEx(
0,
L"Episode Browser",
@@ -139,7 +139,6 @@ int WINAPI WinMain(const HINSTANCE hInstance, const HINSTANCE, char* const, cons
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
nullptr, nullptr, hInstance, nullptr));
- Require(UnhookWindowsHookEx(hHook));
g_hWndStatus = Require(CreateWindowEx(
0,
@@ -167,21 +166,16 @@ int WINAPI WinMain(const HINSTANCE hInstance, const HINSTANCE, char* const, cons
return 0;
}
-static LRESULT CALLBACK CBTProc(const int nCode, const WPARAM wParam, const LPARAM lParam)
+void InitializeMainWindow(const HWND hWnd)
{
- if (nCode < 0 || nCode != HCBT_CREATEWND || g_hWnd)
- return CallNextHookEx(0, nCode, wParam, lParam);
-
/* This code is run ONCE, at the creation of the top-level
* window -- before WndProc! This is important, as it
* initializes global variables that are used by WndProc. */
- g_hWnd = reinterpret_cast<HWND>(wParam);
-
/* Look up DPI. */
if (auto lib = Library::Maybe(L"User32.dll");
auto GetDpiForWindow = lib->GetProcAddress<UINT(HWND)>("GetDpiForWindow"))
- g_dpi = GetDpiForWindow(g_hWnd);
+ g_dpi = GetDpiForWindow(hWnd);
/* Load normal font. */
if (auto lib = Library::Maybe(L"User32.dll");
@@ -205,8 +199,8 @@ static LRESULT CALLBACK CBTProc(const int nCode, const WPARAM wParam, const LPAR
}
/* Create child windows. */
- g_dlv = new DataListView(g_hWnd);
- g_elv = new EpisodeListView(g_hWnd);
+ g_dlv = new DataListView(hWnd);
+ g_elv = new EpisodeListView(hWnd);
/* Get saved view settings. */
Pl("cfg","get_view_watched",&g_bViewWatched);
@@ -221,7 +215,9 @@ static LRESULT CALLBACK CBTProc(const int nCode, const WPARAM wParam, const LPAR
Pl("cfg","get_dlv_height",&dlvHeight);
g_dlv->SetHeight(dlvHeight);
- return 0;
+ /* The global main window handle must only be set AFTER
+ * successful initialization. */
+ g_hWnd = hWnd;
}
LRESULT CALLBACK WndProc(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam)
diff --git a/c/win.cpp b/c/win.cpp
index 593b340..a68d5b3 100644
--- a/c/win.cpp
+++ b/c/win.cpp
@@ -3,6 +3,90 @@
#include "win.h"
+void WithNextWindow(void (*proc)(HWND))
+{
+ /* WithNextWindow uses a CBT hook to call an arbitrary
+ * procedure just before the creation of the next window. The
+ * hook is removed as soon as HCBT_CREATEWND is received.
+ * Thus, proc is guaranteed to be executed only once. Note
+ * that static storage must be used, as SetWindowHookEx cannot
+ * accept a capturing lambda as the hook procedure. */
+
+ static __thread auto procNext = proc;
+ static __thread HHOOK hHook = Require(SetWindowsHookEx(WH_CBT, [](const int nCode,
+ const WPARAM wParam, const LPARAM lParam) -> LRESULT CALLBACK
+ {
+ if (nCode == HCBT_CREATEWND) {
+ Require(UnhookWindowsHookEx(hHook));
+ procNext(reinterpret_cast<HWND>(wParam));
+ return 0;
+ } else
+ return CallNextHookEx(0, nCode, wParam, lParam);
+ }, nullptr, GetCurrentThreadId()));
+}
+
+/* Display next created window in the center of given window. */
+static void CenterNextWindow(HWND hWnd)
+{
+ if (!hWnd)
+ return;
+
+ /* CenterNextWindow employs a trick similar to that above.
+ * Note, however, that repositioning does not work unless it
+ * is delayed until the new window is activated. This
+ * complicates the code somewhat. */
+
+ static __thread HWND hWndParent = hWnd;
+ static __thread HWND hWndNext = nullptr;
+ static __thread HHOOK hHook = Require(SetWindowsHookEx(WH_CBT, [](const int nCode,
+ const WPARAM wParam, const LPARAM lParam) noexcept -> LRESULT CALLBACK
+ {
+ if (!hWndNext && nCode == HCBT_CREATEWND) {
+ hWndNext = reinterpret_cast<HWND>(wParam);
+ return 0;
+ } else if (nCode == HCBT_ACTIVATE
+ && reinterpret_cast<HWND>(wParam) == hWndNext) {
+ Require(UnhookWindowsHookEx(hHook));
+
+ long lStyle = GetWindowLong(hWndNext, GWL_STYLE);
+ if (!(lStyle & WS_POPUP)) return 0;
+
+ RECT rcMain, rcMsg;
+ GetWindowRect(hWndParent, &rcMain);
+ GetWindowRect(hWndNext, &rcMsg);
+ SetWindowPos(hWndNext, nullptr,
+ rcMain.left+(rcMain.right-rcMain.left)/2-(rcMsg.right-rcMsg.left)/2,
+ rcMain.top+(rcMain.bottom-rcMain.top)/2-(rcMsg.bottom-rcMsg.top)/2,
+ -1, -1,
+ SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
+
+ return 0;
+ } else
+ return CallNextHookEx(0, nCode, wParam, lParam);
+ }, nullptr, GetCurrentThreadId()));
+}
+
+int EBMessageBox(const wchar_t* const wszText, const wchar_t* const wszCaption, const UINT uType)
+{
+ extern HWND g_hWnd;
+ CenterNextWindow(g_hWnd);
+ return MessageBox(g_hWnd, wszText, wszCaption, uType);
+}
+
+int GetRelativeCursorPos(const HWND hWnd, POINT* const pt) noexcept
+{
+ RECT rc;
+ if (!GetClientRect(hWnd, &rc)) return 0;
+ SetLastError(ERROR_SUCCESS);
+ if (!MapWindowPoints(hWnd, nullptr, (POINT*)&rc, 2)) return 0;
+
+ POINT ptMouse;
+ if (!GetCursorPos(&ptMouse)) return 0;
+ pt->x = ptMouse.x-rc.left;
+ pt->y = ptMouse.y-rc.top;
+ return 1;
+}
+
Win32Error::Win32Error() noexcept
: code(GetLastError()) {}
@@ -64,49 +148,3 @@ Library::~Library()
{
FreeLibrary(m_hModule);
}
-
-int EBMessageBox(const wchar_t* const wszText, const wchar_t* const wszCaption, const UINT uType)
-{
- extern HWND g_hWnd;
-
- auto proc = [](const int nCode, const WPARAM wParam, const LPARAM lParam) noexcept
- -> LRESULT CALLBACK
- {
- if (!g_hWnd || nCode < 0 || nCode != HCBT_ACTIVATE)
- return CallNextHookEx(0, nCode, wParam, lParam);
-
- HWND hWnd = reinterpret_cast<HWND>(wParam);
- long lStyle = GetWindowLong(hWnd, GWL_STYLE);
- if (!(lStyle & WS_POPUP)) return 0;
-
- RECT rcMain, rcMsg;
- GetWindowRect(g_hWnd, &rcMain);
- GetWindowRect(hWnd, &rcMsg);
- SetWindowPos(hWnd, nullptr,
- rcMain.left+(rcMain.right-rcMain.left)/2-(rcMsg.right-rcMsg.left)/2,
- rcMain.top+(rcMain.bottom-rcMain.top)/2-(rcMsg.bottom-rcMsg.top)/2,
- -1, -1,
- SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
-
- return 0;
- };
-
- HHOOK hHook = Require(SetWindowsHookEx(WH_CBT, proc, nullptr, GetCurrentThreadId()));
- int r = MessageBox(g_hWnd, wszText, wszCaption, uType);
- Require(UnhookWindowsHookEx(hHook));
- return r;
-}
-
-int GetRelativeCursorPos(const HWND hWnd, POINT* const pt)
-{
- RECT rc;
- if (!GetClientRect(hWnd, &rc)) return 0;
- SetLastError(ERROR_SUCCESS);
- if (!MapWindowPoints(hWnd, nullptr, (POINT*)&rc, 2)) return 0;
-
- POINT ptMouse;
- if (!GetCursorPos(&ptMouse)) return 0;
- pt->x = ptMouse.x-rc.left;
- pt->y = ptMouse.y-rc.top;
- return 1;
-}
diff --git a/c/win.h b/c/win.h
index ff38ce0..30d3cc5 100644
--- a/c/win.h
+++ b/c/win.h
@@ -4,10 +4,12 @@
#include <optional>
#include <windows.h>
+/* Run given procedure at creation of next window. */
+void WithNextWindow(void (*proc)(HWND));
/* Display message box centered in main window. */
int EBMessageBox(const wchar_t* wszText, const wchar_t* wszCaption, UINT uType);
/* Retrieve mouse position relative to given window's client area. */
-int GetRelativeCursorPos(HWND hWnd, POINT* pt);
+int GetRelativeCursorPos(HWND hWnd, POINT* pt) noexcept;
/* Cached values from GetSystemMetrics. */
template <int I> auto Metric = GetSystemMetrics(I);