Customizable Dual Hanging Dice - #5011
Conversation
|
Thanks for the pull request! This repository uses a two-stage review: an AI review that you run yourself, followed by a human review. To get started, comment See the pull request review process for the full details. |
|
/ai-review |
Submission reviewNote: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding. Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it. Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them. Nice idea and the physics look fun, but the mod is currently hosted in the wrong process and has a few stability/performance problems that need fixing first. 1. This should be a tool mod ( The mod installs no function hooks at all — it only creates its own layered window and calls
The fix is mechanical: switch to Your own mac-magnifying-cursor already does exactly this, and so do the closest comparable overlay mods — neko-cat and one-hair. 2. WaitForSingleObject(g_hThread, 1000);
CloseHandle(g_hThread);If the wait times out, Nothing in the shutdown path can block indefinitely here, so just wait properly, the way mac-magnifying-cursor does: WaitForSingleObject(g_hThread, INFINITE);Related: 3. The system-wide g_hEventHookPos = SetWinEventHook(
EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE,
NULL, WinEventProc, 0, 0, WINEVENT_OUTOFPROCESS);
None of the 4. The 16 ms timer runs a full GDI+ repaint forever, even when nothing is moving SetTimer(g_hWnd, 1, 16, NULL);
...
case WM_TIMER:
PhysicsStep();
RedrawOverlay(hWnd);
5. HBITMAP hBmp = CreateCompatibleBitmap(hdcScr, g_winW, g_winH);
BITMAPINFO bi = {};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = g_winW;
bi.bmiHeader.biHeight = -g_winH; // top-down
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
void* bits;
HBITMAP hBmp = CreateDIBSection(hdcMem, &bi, DIB_RGB_COLORS, &bits, NULL, 0);One more nuance while you're here: Optional improvements
Minor polish — none of this affects users in normal operation, so it's your call.
Functionality notes
Non-critical observations and ideas about the feature behavior itself.
Next steps:
See the review process for details. |
|
/ai-review |
Submission reviewNote: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding. Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it. Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them. Nice, self-contained little mod — the physics and the layered-window drawing are clean. A few things should be fixed before merge: 1. This should be a tool mod ( The mod installs no function hooks at all — it only uses
You already use this pattern in your own macOS magnifying cursor mod, so it should be a small change: switch 2. The README preview image is a 404. That repo exists, but 3. Changing a setting while the overlay is hidden for fullscreen leaves it visible and frozen on top of the fullscreen app.
SetWindowPos(g_hWnd, HWND_TOPMOST, posX, 10, g_winW, g_winH, SWP_NOACTIVATE | SWP_SHOWWINDOW);
UpdateForegroundHookState();
CheckFullscreenState();but The same shape happens at startup: if a fullscreen app is already running, Simplest fix — don't show from SetWindowPos(g_hWnd, HWND_TOPMOST, posX, 10, g_winW, g_winH,
SWP_NOACTIVATE | (g_isHiddenByFullscreen ? 0 : SWP_SHOWWINDOW));and drop the 4. The overlay steals focus. Two separate problems:
g_hWnd = CreateWindowEx(
WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE,
...
Optional improvements
Minor polish — none of this affects users, so it's your call.
Functionality notes
Non-critical observations and ideas about the feature behavior itself.
Next steps:
See the review process for details. |
|
/ai-review |
Submission reviewNote: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding. Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it. Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them. Nice idea and the code is generally tidy, but the mod is injecting into Explorer without needing to, and the WinEvent flags silently disable a large part of the reactions. 1. This should be a tool mod, not an The mod installs no function hooks at all — it only uses There's also a concrete user-visible consequence: Please convert it to a tool mod: mods/neko-cat.wh.cpp is essentially the same architecture as this mod (worker thread + layered overlay window + animation loop) and is already a tool mod; mods/explorer-folder-hover-menu.wh.cpp has the launcher snippet as a byte-for-byte copy of the wiki version. 2. #ifndef WINEVENT_OUTOFPROCESS
#define WINEVENT_OUTOFPROCESS 0x0003
#endifThere is no such constant in the Windows SDK, so the Use the documented flag instead: SetWinEventHook(EVENT_SYSTEM_MINIMIZESTART, EVENT_SYSTEM_MINIMIZESTART,
NULL, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);( 3. Changing settings while hidden for fullscreen un-hides a frozen overlay on top of the fullscreen app.
SetWindowPos(g_hWnd, HWND_TOPMOST, posX, 10, g_winW, g_winH, SWP_NOACTIVATE | SWP_SHOWWINDOW);
Either skip 4. Fullscreen transitions that don't change the foreground window are never detected.
Add 5. The 16 ms timer runs forever, including when the dice are at rest.
case WM_TIMER:
if (PhysicsStep()) {
RedrawOverlay(hWnd);
} else {
KillTimer(hWnd, 1); // nothing moving; re-armed on interaction
}
break;…and call Optional improvements
Minor polish — none of this affects users, so it's your call.
Functionality notes
Non-critical observations and ideas about the feature behavior itself.
Next steps:
See the review process for details. |
|
/ai-review |
|
This pull request has already had 3 AI reviews in the last 24 hours, which is the limit, so no review was posted this time. Comment |
|
/ai-review |
Submission reviewNote: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding. Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it. Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them. Nice idea, and the teardown path (joining the thread, 1. This should be a tool mod, not an The mod installs zero function hooks — it only uses
Switch to 2. #ifndef WINEVENT_OUTOFPROCESS
#define WINEVENT_OUTOFPROCESS 0x0003
#endifThere is no such flag in g_hEventHookMin = SetWinEventHook(
EVENT_SYSTEM_MINIMIZESTART, EVENT_SYSTEM_MINIMIZESTART,
nullptr, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);Delete the 3. The dice never reach rest, so the mod repaints at ~60 FPS forever.
bool moved1 = (fabsf(g_d1.vx) > 0.01f || fabsf(g_d1.vy) > 0.01f || ...);But the integrator applies gravity ( Two fixes, ideally both:
4. The overlay re-appears — frozen — on top of fullscreen apps.
SetWindowPos(g_hWnd, HWND_TOPMOST, posX, 10, g_winW, g_winH, SWP_NOACTIVATE | SWP_SHOWWINDOW);
UpdateForegroundHookState();
CheckFullscreenState();If the overlay was hidden by the fullscreen logic, Make visibility derive from state in one place rather than being set from three. E.g. drop bool shouldHide = g_settings.hideOnFullscreen && IsWindowFullscreen(GetForegroundWindow());
if (shouldHide != g_isHiddenByFullscreen) {
g_isHiddenByFullscreen = shouldHide;
if (shouldHide) {
ShowWindow(g_hWnd, SW_HIDE);
} else {
ShowWindow(g_hWnd, SW_SHOWNOACTIVATE);
SetWindowPos(g_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
}5. Fullscreen state is polled 62 times a second, even while hidden.
Optional improvements
Minor polish — none of this affects users, so it's your call.
Functionality notes
Non-critical observations and ideas about the feature behavior itself.
Next steps:
See the review process for details. |
|
/ready-for-reviewer |
|
Please address item 1 of the last review. Also, I see no reason not to address items 3 and 5 to improve the mod's efficiency. Let me know if I missed something. I haven't verified item 4 but it seems to be insignificant even if it's a real issue. |
|
/ai-review |
Submission reviewNote: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding. Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it. Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them. Nice, self-contained novelty mod — no external content, no function hooks, settings map cleanly onto the code, and the README has a preview GIF on an allowed host. The main problem is the process model: the mod targets 1. The mod declares
The fix is the documented one: rename 2. Remove the home-grown g_hSingleInstanceMutex = CreateMutex(NULL, TRUE, L"Global\\WindhawkHangingDiceSingleInstanceMutex");Once you adopt the boilerplate above this is redundant — the snippet already dedupes with a
Delete the mutex and all four of its cleanup blocks. 3. The fullscreen hide/show state machine can get stuck with the dice permanently hidden Two distinct paths:
4. Settings changes have no visible effect while the dice are at rest
SetWindowPos(g_hWnd, HWND_TOPMOST, posX, 10, g_winW, g_winH, SWP_NOACTIVATE);
UpdateForegroundHookState();
CheckFullscreenState();The layered surface is only ever updated by CheckFullscreenState();
RedrawOverlay(g_hWnd);5. void Wh_ModUninit() {
if (g_hWnd) {
PostMessage(g_hWnd, WM_CLOSE, 0, 0);
}
if (g_hUIThread) {
WaitForSingleObject(g_hUIThread, 2000);
...The mod image must be unloadable the instant
As a side effect the class registration also survives ( Publish the window handle before HANDLE g_hWindowReady; // signaled by the overlay thread once it has a window, or has given up
BOOL WhTool_ModInit() {
g_hWindowReady = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (!g_hWindowReady) return FALSE;
g_hUIThread = CreateThread(nullptr, 0, OverlayThreadProc, nullptr, 0, &g_uiThreadId);
if (!g_hUIThread) { CloseHandle(g_hWindowReady); g_hWindowReady = nullptr; return FALSE; }
WaitForSingleObject(g_hWindowReady, INFINITE);
return TRUE;
}
void WhTool_ModUninit() {
if (g_hWnd) PostMessage(g_hWnd, WM_CLOSE, 0, 0);
if (g_hUIThread) {
WaitForSingleObject(g_hUIThread, INFINITE); // not 2000
CloseHandle(g_hUIThread);
g_hUIThread = nullptr;
}
if (g_hWindowReady) { CloseHandle(g_hWindowReady); g_hWindowReady = nullptr; }
}with Optional improvements
Minor polish — none of this affects users, so it's your call.
Functionality notes
Non-critical observations and ideas about the feature behavior itself.
Next steps:
See the review process for details. |
|
@m417z I've been trying to fix this for over an hour, but I couldn't figure it out. I have no idea how to fix it. What should I do? |
|
By the way, everything works fine |
|
The review explains it, there's not much to add. If you need community help, you can join the Windhawk Discord server and try your luck: |
Customizable Dual Hanging Dice
Add interactive hanging dice to your desktop with dynamic physics. They swing and react in real-time when you move, minimize, maximize, or restore windows, and automatically hide during fullscreen games or apps to stay out of your way.
Changelog
Forced HWND_TOPMOST z-order positioning to ensure the overlay stays on top of all windows during active state updates.
Enhanced fullscreen detection logic to properly re-apply topmost window placement upon restoring visibility.
Mod authorship
If this pull request introduces a new mod, please complete the section below.
This mod was created by: