Add Glass Cursors mod - #5202
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 |
|
/ai-review |
|
@fizixes |
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 rasterizer, and the reversibility story ( 1. This should be a tool mod, not an
Change 2. WaitForSingleObject(g_animationThread, 2000); // <-- may return WAIT_TIMEOUT
CloseHandle(g_animationThread);
...
CloseHandle(g_animationStopEvent);
g_busyFrames.clear();Windhawk unloads the mod with a single The thread never blocks indefinitely (it only waits on the stop event with a short timeout), so there is no reason not to wait unconditionally: WaitForSingleObject(g_animationThread, INFINITE);3. The animation thread does 60 Gate the loop on the busy cursor actually being displayed, and idle-poll cheaply otherwise. bool BusyCursorVisible() {
CURSORINFO ci = {sizeof(ci)};
if (!GetCursorInfo(&ci) || !(ci.flags & CURSOR_SHOWING) || !ci.hCursor) {
return false;
}
for (DWORD id : {CURSOR_WAIT, CURSOR_APPSTARTING}) {
if (ci.hCursor == (HCURSOR)LoadImage(nullptr, MAKEINTRESOURCE(id),
IMAGE_CURSOR, 0, 0, LR_SHARED)) {
return true;
}
}
return false;
}then wait It's also worth testing whether the animation can be handed to the system entirely: build an 4. Every settings change reloads the mod and re-renders all 62 cursors synchronously. That init path is not cheap: 14 static roles + 24 busy frames + 24 working frames = 62 renders, each rasterizing an Two fixes:
5. The README has no images. This is an entirely visual mod with four pointer styles, two hand styles, and configurable color/opacity/size — users have no way to tell what any of it looks like from the description. Please add at least one screenshot showing the cursor set (ideally a small grid of the roles, plus a GIF of the loading spinner). Only 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. Good iteration — the tool-mod conversion, the unconditional thread join, the gated animation loop, the pre-created frames, the 1. If the animation can't start, the tool process exits and leaves the glass cursors applied with no way back. In the tool-mod pattern the mod's own thread is the only thing keeping
Either way Keep the thread alive when frames are unavailable, and fail loudly (restoring the scheme) if the thread itself can't be created. Note that DWORD WINAPI AnimationThreadProc(LPVOID) {
const bool framesReady = PrepareAnimationFrames() &&
g_busyFrames.size() == kSpinnerFrames &&
g_workingFrames.size() == kSpinnerFrames;
if (!framesReady) {
Wh_Log(L"Animation frames unavailable, static cursors only");
}
int frame = 0;
while (WaitForSingleObject(g_animationStopEvent, 0) != WAIT_OBJECT_0) {
const DWORD visibleRole = framesReady ? GetVisibleBusyCursorRole() : 0;
...
}
return 0;
}
BOOL WhTool_ModInit() {
SetUnhandledExceptionFilter(RestoreCursorsOnCrash);
LoadSettings();
ApplyStaticCursors();
if (!StartAnimation()) {
RestoreWindowsCursorScheme(); // don't strand the user's cursors
return FALSE; // boilerplate then ExitProcess(1)
}
return TRUE;
}theme-toggler-tray is the minimal version of the same contract — its 2. Anything that broadcasts The animation thread is a natural host for a message-only window: // in the animation thread, before the poll loop
HWND hMsgWnd = CreateWindowExW(0, L"Static", nullptr, 0, 0, 0, 0, 0,
HWND_MESSAGE, nullptr, nullptr, nullptr);
// subclass it (WindhawkUtils::SetWindowSubclassFromAnyThread) and on
// WM_SETTINGCHANGE with wParam == SPI_SETCURSORS, re-run
// LoadSettings() + ApplyStaticCursors() + re-apply the current frames.Two things to watch: destroy the window and unregister the subclass before the thread exits (so nothing from the mod image can run after 3. The README still has no images. (L13-L36) This is an entirely visual mod with four pointer styles, two hand styles, and configurable colour, opacity and size — nobody can tell what any of that looks like from the text. Please add at least one screenshot of the cursor set (a small grid of the roles works well), ideally plus a GIF of the loading spinner so the animated part is visible too. Only 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. Overall this is in good shape: the tool-mod boilerplate is a verbatim copy of the wiki snippet, the settings block and the code agree in both directions (every declared setting is read, every read name is declared, and every The README has no screenshot or GIF. This mod is entirely artwork — four pointer silhouettes, two hand designs, two fill opacities, six artwork scales and an animated spinner — and there is no way for a user browsing windhawk.net to see any of it, or to pick between 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 |
|
Hey @fizixes is there a way we can implement the mica or mica alt effect under the cursor, that's one thing I wish we could do, maybe you can help me figure it out in this mod. |
|
New commits were pushed, so this pull request left the human review queue and is back to waiting-for-author. Comment |
As it turns out, it's insanely computationally cheap, so I've added Mica, Acryllic, and Mica alt. It should work for 2.0 alpha 3 now, and also with the macOS magnifying cursor now, it retreats when another program wants to change the cursor. awaiting AI review now. |
|
/ai-review |
|
Something went wrong while preparing the AI review, so there is nothing to post this time. This is a problem on the reviewer's side, not with this pull request. Comment |
|
/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 |
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. The lifecycle work from the previous rounds is holding up well: the worker thread owns all rendering and all global state, teardown joins unconditionally and destroys the window on its owning thread, the class registration no longer tolerates 1. The tool process never makes itself DPI-aware, so the backdrop is sampled from the wrong screen pixels on scaled displays. The worker thread reads screen coordinates from Without it, on any non-100% display the screen DC is virtualized: the capture is read back from a rescaled copy of the desktop (soft/blurry material), and on a second monitor with a different scale factor the virtualization is anchored to the primary's scale, so the mod samples a region that isn't under the pointer at all. One call at the top of // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 — all coordinates and metrics
// below must be real physical pixels.
if (HMODULE user32 = GetModuleHandleW(L"user32.dll")) {
using SetThreadDpiAwarenessContext_t = HANDLE(WINAPI*)(HANDLE);
if (auto setContext = (SetThreadDpiAwarenessContext_t)GetProcAddress(
user32, "SetThreadDpiAwarenessContext")) {
setContext((HANDLE)-4);
}
}Worth verifying side by side on a 150% display and on a mixed-DPI pair before and after — this is the kind of thing that looks fine on a single 100% monitor and is visibly wrong everywhere else. 2. The sampled styles run a screen readback and a session-global
Also worth measuring before this merges: leave the mod running with the pointer moving for an hour and watch the USER/GDI object counts for 3. cursor-motion-blur needs the same decision and shows the shape: 4. The README still has no images. (L13-L45) This is the fourth round and the fourth time asking, so I'll keep it short: the mod is entirely artwork — four pointer silhouettes, two hand designs, four material styles, six artwork scales and an animated spinner — and someone browsing windhawk.net has no way to see any of it, or to choose between 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. Good shape overall: the tool-mod boilerplate is a verbatim copy of the wiki snippet, 1. The README has no screenshot or GIF. The entire mod is artwork — four glass materials, four pointer silhouettes, two hand styles, six artwork sizes, an animated spinner — and there is currently no image at all, so a user browsing the catalog has no way to know what they'd be installing. Please add at least one image of the cursor set, and ideally a GIF of the animated busy/working spinner. Allowed image hosts are 2. The sampled styles run a permanent monitor-refresh-rate loop, and they're the default.
While the pointer moves it's a lot heavier: Concrete asks:
3. The worker thread never opts into per-monitor DPI awareness. The mod's
Fix is one line at the top of the worker thread, as cursor-motion-blur.wh.cpp#L405 does: DWORD WINAPI AnimationThreadProc(LPVOID) {
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
...bt-battery-monitor.wh.cpp#L1676-L1679 does the same via 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. |
|
I will give a bit of feedback, I like the effects, they were implemented very closely with the original design of the effects, the last big toggle I think would be useful is maybe accent color for the loading wheel (you know the part thats visibly spinning) this should be for the working (loading wheel with cursor) and busy (just loading wheel) states. otherwise its very well implemented. |
Adds Glass Cursors, a DPI-aware translucent cursor replacement with
configurable pointer styles, glass color, opacity, sizing, and animated
loading cursors.
Changelog
If this pull request updates an existing mod, describe the changes below:
Mod authorship
If this pull request introduces a new mod, please complete the section below.
This mod was created by:
Please select the options that best apply. Your selection does not affect the acceptance criteria, but it helps reviewers understand the context of the code and provide relevant feedback.