Skip to content

Commit 8f60e7c

Browse files
committed
Add hook checks and original show/hide calls to WindowManager
Introduces API functions to check if 'will show' and 'will hide' hooks are set, and to call the original native show/hide implementations for a window, bypassing hooks. Refactors macOS swizzling logic to use new HandleWillShow/HandleWillHide methods and adds corresponding methods to the WindowManager interface and implementation. Updates C API and headers to expose these new capabilities.
1 parent 7d82686 commit 8f60e7c

File tree

7 files changed

+161
-38
lines changed

7 files changed

+161
-38
lines changed

src/capi/window_manager_c.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,4 +288,44 @@ void native_window_manager_set_will_hide_hook(native_window_will_hide_callback_t
288288
// Swallow exceptions to avoid unwinding across API boundary
289289
}
290290
});
291+
}
292+
293+
FFI_PLUGIN_EXPORT
294+
bool native_window_manager_has_will_show_hook(void) {
295+
try {
296+
auto& manager = WindowManager::GetInstance();
297+
return manager.HasWillShowHook();
298+
} catch (...) {
299+
return false;
300+
}
301+
}
302+
303+
FFI_PLUGIN_EXPORT
304+
bool native_window_manager_has_will_hide_hook(void) {
305+
try {
306+
auto& manager = WindowManager::GetInstance();
307+
return manager.HasWillHideHook();
308+
} catch (...) {
309+
return false;
310+
}
311+
}
312+
313+
FFI_PLUGIN_EXPORT
314+
bool native_window_manager_call_original_show(native_window_id_t window_id) {
315+
try {
316+
auto& manager = WindowManager::GetInstance();
317+
return manager.CallOriginalShow(window_id);
318+
} catch (...) {
319+
return false;
320+
}
321+
}
322+
323+
FFI_PLUGIN_EXPORT
324+
bool native_window_manager_call_original_hide(native_window_id_t window_id) {
325+
try {
326+
auto& manager = WindowManager::GetInstance();
327+
return manager.CallOriginalHide(window_id);
328+
} catch (...) {
329+
return false;
330+
}
291331
}

src/capi/window_manager_c.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,36 @@ FFI_PLUGIN_EXPORT
133133
void native_window_manager_set_will_hide_hook(native_window_will_hide_callback_t callback,
134134
void* user_data);
135135

136+
/**
137+
* Check if the "will show" hook is set.
138+
* @return true if hook is set, false otherwise.
139+
*/
140+
FFI_PLUGIN_EXPORT
141+
bool native_window_manager_has_will_show_hook(void);
142+
143+
/**
144+
* Check if the "will hide" hook is set.
145+
* @return true if hook is set, false otherwise.
146+
*/
147+
FFI_PLUGIN_EXPORT
148+
bool native_window_manager_has_will_hide_hook(void);
149+
150+
/**
151+
* Call the original native show implementation for the specified window.
152+
* This bypasses the swizzled hook path on macOS.
153+
* @return true on success, false if the window wasn't found or unsupported.
154+
*/
155+
FFI_PLUGIN_EXPORT
156+
bool native_window_manager_call_original_show(native_window_id_t window_id);
157+
158+
/**
159+
* Call the original native hide implementation for the specified window.
160+
* This bypasses the swizzled hook path on macOS.
161+
* @return true on success, false if the window wasn't found or unsupported.
162+
*/
163+
FFI_PLUGIN_EXPORT
164+
bool native_window_manager_call_original_hide(native_window_id_t window_id);
165+
136166
#ifdef __cplusplus
137167
}
138168
#endif

src/platform/macos/coordinate_utils_macos.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ struct NSPointExt {
3838
NSRect primaryScreenFrame = [[NSScreen screens][0] frame];
3939
return NSMakePoint(topLeftPoint.x, primaryScreenFrame.size.height - topLeftPoint.y);
4040
}
41-
41+
4242
// Convert top-left window position to bottom-left window position
4343
// This is the correct method for converting window positions, as it accounts for window height
4444
static NSPoint bottomLeftForWindow(CGPoint topLeftPoint, CGFloat windowHeight) {

src/platform/macos/window_manager_macos.mm

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,28 +46,33 @@ - (void)na_swizzled_orderOut:(id)sender;
4646
@implementation NSWindow (NativeAPISwizzle)
4747

4848
- (void)na_swizzled_makeKeyAndOrderFront:(id)sender {
49-
// Ensure registry is in sync and then invoke hook with correct WindowId
49+
// Resolve window id and handle hook if present
50+
if (nativeapi::WindowManager::GetInstance().HasWillShowHook()) {
5051
auto windows = nativeapi::WindowManager::GetInstance().GetAll();
5152
for (const auto& window : windows) {
5253
if (window->GetNativeObject() == (__bridge void*)self) {
53-
nativeapi::WindowManager::GetInstance().InvokeWillShowHook(window->GetId());
54-
break;
54+
nativeapi::WindowManager::GetInstance().HandleWillShow(window->GetId());
55+
// Hook handles all logic; never call original here
56+
return;
5557
}
5658
}
57-
// First call original implementation so properties like title are up-to-date
58-
[self na_swizzled_makeKeyAndOrderFront:sender];
59+
}
60+
// No window found in registry, call original implementation (swapped)
61+
[self na_swizzled_makeKeyAndOrderFront:sender];
5962
}
6063

6164
- (void)na_swizzled_orderOut:(id)sender {
62-
// Invoke hook before hiding, resolving id via registry without using windowNumber
63-
auto windows = nativeapi::WindowManager::GetInstance().GetAll();
64-
for (const auto& window : windows) {
65-
if (window->GetNativeObject() == (__bridge void*)self) {
66-
nativeapi::WindowManager::GetInstance().InvokeWillHideHook(window->GetId());
67-
break;
65+
// Resolve window id and handle hook if present
66+
if (nativeapi::WindowManager::GetInstance().HasWillHideHook()) {
67+
auto windows = nativeapi::WindowManager::GetInstance().GetAll();
68+
for (const auto& window : windows) {
69+
if (window->GetNativeObject() == (__bridge void*)self) {
70+
nativeapi::WindowManager::GetInstance().HandleWillHide(window->GetId());
71+
return;
72+
}
6873
}
6974
}
70-
// Call original implementation (swapped)
75+
// No window found in registry, call original implementation (swapped)
7176
[self na_swizzled_orderOut:sender];
7277
}
7378

@@ -277,7 +282,7 @@ - (void)windowWillClose:(NSNotification*)notification {
277282
// Create or get Window wrapper - this will handle ID assignment via associated object
278283
auto window = std::make_shared<Window>((__bridge void*)ns_window);
279284
WindowId window_id = window->GetId();
280-
285+
281286
// Add to registry if not already present
282287
if (!WindowRegistry::GetInstance().Get(window_id)) {
283288
WindowRegistry::GetInstance().Add(window_id, window);
@@ -299,12 +304,12 @@ - (void)windowWillClose:(NSNotification*)notification {
299304
// Create or get Window wrapper - this will handle ID retrieval via associated object
300305
auto window = std::make_shared<Window>((__bridge void*)ns_window);
301306
WindowId window_id = window->GetId();
302-
307+
303308
// Ensure it's in the registry
304309
if (!WindowRegistry::GetInstance().Get(window_id)) {
305310
WindowRegistry::GetInstance().Add(window_id, window);
306311
}
307-
312+
308313
return window;
309314
}
310315
return nullptr;
@@ -324,18 +329,54 @@ - (void)windowWillClose:(NSNotification*)notification {
324329
}
325330
}
326331

327-
void WindowManager::InvokeWillShowHook(WindowId id) {
332+
bool WindowManager::HasWillShowHook() const {
333+
return pimpl_->will_show_hook_.has_value();
334+
}
335+
336+
bool WindowManager::HasWillHideHook() const {
337+
return pimpl_->will_hide_hook_.has_value();
338+
}
339+
340+
void WindowManager::HandleWillShow(WindowId id) {
328341
if (pimpl_->will_show_hook_) {
329342
(*pimpl_->will_show_hook_)(id);
330343
}
331344
}
332345

333-
void WindowManager::InvokeWillHideHook(WindowId id) {
346+
void WindowManager::HandleWillHide(WindowId id) {
334347
if (pimpl_->will_hide_hook_) {
335348
(*pimpl_->will_hide_hook_)(id);
336349
}
337350
}
338351

352+
bool WindowManager::CallOriginalShow(WindowId id) {
353+
auto window = Get(id);
354+
if (!window) {
355+
return false;
356+
}
357+
void* native = window->GetNativeObject();
358+
if (!native) {
359+
return false;
360+
}
361+
NSWindow* ns_window = (__bridge NSWindow*)native;
362+
[ns_window na_swizzled_makeKeyAndOrderFront:nil];
363+
return true;
364+
}
365+
366+
bool WindowManager::CallOriginalHide(WindowId id) {
367+
auto window = Get(id);
368+
if (!window) {
369+
return false;
370+
}
371+
void* native = window->GetNativeObject();
372+
if (!native) {
373+
return false;
374+
}
375+
NSWindow* ns_window = (__bridge NSWindow*)native;
376+
[ns_window na_swizzled_orderOut:nil];
377+
return true;
378+
}
379+
339380
void WindowManager::StartEventListening() {
340381
pimpl_->StartEventListening();
341382
}
@@ -345,7 +386,7 @@ - (void)windowWillClose:(NSNotification*)notification {
345386
}
346387

347388
void WindowManager::DispatchWindowEvent(const WindowEvent& event) {
348-
Emit(event); // Use Dispatch instead of DispatchSync
389+
Emit(event);
349390
}
350391

351392
} // namespace nativeapi

src/platform/windows/window_manager_windows.cpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ static WindowId GetWindowIdFromHwnd(HWND hwnd) {
3636
// Use shared_ptr so it can be properly registered in WindowRegistry
3737
auto window = std::make_shared<Window>(hwnd);
3838
WindowId window_id = window->GetId();
39-
39+
4040
// Register the window manually since constructor's shared_from_this() fails
4141
// during construction (shared_ptr control block not fully initialized yet)
4242
if (window_id != IdAllocator::kInvalidId) {
4343
WindowRegistry::GetInstance().Add(window_id, window);
4444
}
45-
45+
4646
return window_id;
4747
}
4848

@@ -95,7 +95,8 @@ static BOOL WINAPI HookedShowWindow(HWND hwnd, int nCmdShow) {
9595
}
9696

9797
static BOOL WINAPI HookedShowWindowAsync(HWND hwnd, int nCmdShow) {
98-
std::cout << "HookedShowWindowAsync called for hwnd: " << hwnd << " with nCmdShow: " << nCmdShow << std::endl;
98+
std::cout << "HookedShowWindowAsync called for hwnd: " << hwnd << " with nCmdShow: " << nCmdShow
99+
<< std::endl;
99100
InvokePreShowHideHooks(hwnd, nCmdShow);
100101
if (g_original_show_window_async) {
101102
std::cout << "Calling original ShowWindowAsync" << std::endl;
@@ -365,19 +366,19 @@ std::shared_ptr<Window> WindowManager::Get(WindowId id) {
365366
if (window) {
366367
return window;
367368
}
368-
369+
369370
// If not in registry, enumerate all windows to find it
370371
// This will create and register the window if it exists
371372
GetAll();
372-
373+
373374
// Try again after enumeration
374375
return WindowRegistry::GetInstance().Get(id);
375376
}
376377

377378
// Callback for EnumWindows to collect all top-level windows
378379
static BOOL CALLBACK EnumWindowsCallback(HWND hwnd, LPARAM lParam) {
379380
auto* windows = reinterpret_cast<std::vector<HWND>*>(lParam);
380-
381+
381382
// Only include visible windows that are not minimized to taskbar
382383
// and have a title (filters out many background windows)
383384
if (IsWindowVisible(hwnd)) {
@@ -390,23 +391,23 @@ static BOOL CALLBACK EnumWindowsCallback(HWND hwnd, LPARAM lParam) {
390391
}
391392
}
392393
}
393-
394+
394395
return TRUE; // Continue enumeration
395396
}
396397

397398
std::vector<std::shared_ptr<Window>> WindowManager::GetAll() {
398399
std::vector<HWND> hwnds;
399-
400+
400401
// Enumerate all top-level windows
401402
EnumWindows(EnumWindowsCallback, reinterpret_cast<LPARAM>(&hwnds));
402-
403+
403404
std::vector<std::shared_ptr<Window>> windows;
404405
windows.reserve(hwnds.size());
405-
406+
406407
for (HWND hwnd : hwnds) {
407408
// Get window ID from HWND, creating Window object if needed
408409
WindowId window_id = GetWindowIdFromHwnd(hwnd);
409-
410+
410411
if (window_id != IdAllocator::kInvalidId) {
411412
// Try to get existing window from registry
412413
auto window = WindowRegistry::GetInstance().Get(window_id);
@@ -415,7 +416,7 @@ std::vector<std::shared_ptr<Window>> WindowManager::GetAll() {
415416
}
416417
}
417418
}
418-
419+
419420
return windows;
420421
}
421422

src/platform/windows/window_windows.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ Window::Window() {
127127

128128
Window::Window(void* native_window) {
129129
HWND hwnd = static_cast<HWND>(native_window);
130-
130+
131131
if (!hwnd) {
132132
// Allocate ID even for null window to maintain consistency
133133
WindowId id = IdAllocator::Allocate<Window>();
@@ -138,11 +138,11 @@ Window::Window(void* native_window) {
138138
// Check if window already has an ID stored as a custom property
139139
HANDLE prop_handle = GetProp(hwnd, kWindowIdProperty);
140140
WindowId id = IdAllocator::kInvalidId;
141-
141+
142142
if (prop_handle) {
143143
id = static_cast<WindowId>(reinterpret_cast<uintptr_t>(prop_handle));
144144
}
145-
145+
146146
if (id == IdAllocator::kInvalidId || id == 0) {
147147
// Allocate new ID if window doesn't have one
148148
id = IdAllocator::Allocate<Window>();
@@ -165,7 +165,7 @@ Window::~Window() {
165165
// Remove window from registry on destruction
166166
if (pimpl_ && pimpl_->window_id_ != IdAllocator::kInvalidId) {
167167
WindowRegistry::GetInstance().Remove(pimpl_->window_id_);
168-
168+
169169
// Remove the custom property from HWND if window is still valid
170170
if (pimpl_->hwnd_) {
171171
RemoveProp(pimpl_->hwnd_, kWindowIdProperty);

src/window_manager.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ class WindowManager : public EventEmitter<WindowEvent> {
118118
*/
119119
std::shared_ptr<Window> GetCurrent();
120120

121-
122121
/**
123122
* Hooks invoked before native window show/hide operations (e.g., via swizzling).
124123
* These are declarations only; platform implementations can register and invoke them.
@@ -130,9 +129,21 @@ class WindowManager : public EventEmitter<WindowEvent> {
130129
void SetWillShowHook(std::optional<WindowWillShowHook> hook);
131130
void SetWillHideHook(std::optional<WindowWillHideHook> hook);
132131

132+
// Check if hooks are set
133+
bool HasWillShowHook() const;
134+
bool HasWillHideHook() const;
135+
133136
// Called by platform layer BEFORE the actual show/hide happens
134-
void InvokeWillShowHook(WindowId id);
135-
void InvokeWillHideHook(WindowId id);
137+
void HandleWillShow(WindowId id);
138+
void HandleWillHide(WindowId id);
139+
140+
/**
141+
* Call the platform's original show/hide implementations for a window,
142+
* bypassing swizzled paths. Returns true if successfully invoked.
143+
* On unsupported platforms, these return false.
144+
*/
145+
bool CallOriginalShow(WindowId id);
146+
bool CallOriginalHide(WindowId id);
136147

137148
protected:
138149
/**

0 commit comments

Comments
 (0)