Publish the first version of the language restorer mod - #5214
Conversation
|
/ai-review |
|
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. |
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 there's no existing mod that covers it ( 1. The flyout window can survive the unload, leaving a dangling
The fix is the one you already use in the Action Center mod: own the window from a single dedicated thread with its own message loop, ask that thread to destroy its windows, and join it in 2. Deadlock on unload / settings change: a lock is held across a cross-thread
Meanwhile Copy out, release, then call: void Wh_ModBeforeUninit(void) {
g_unloading.store(true, std::memory_order_release);
HideFlyout();
std::vector<HWND> hwnds;
{
std::lock_guard<std::mutex> lock(g_subclassedWindowsMutex);
hwnds.assign(g_subclassedWindows.begin(), g_subclassedWindows.end());
g_subclassedWindows.clear();
}
for (HWND hWnd : hwnds) {
WindhawkUtils::RemoveWindowSubclassFromAnyThread(hWnd, InputIndicatorButtonSubclassProc);
}
}Do the same in 3. Per the mod lifetime docs: "In case a mod is loaded into a process which is already running, all events are executed in the Windhawk Engine thread." That's the normal case — enabling the mod, updating it, or changing a setting while Explorer is running. Consequences:
Install the LL hooks from a dedicated thread that owns a message loop and is joined in 4. Heavy, blocking work runs inside the low-level keyboard hook.
Keep the hook proc to state inspection plus a 5. The Alt+Shift handler fires on unrelated chords and on auto-repeat. if (g_enableAltShift && isKeyDown && isAltDown && (kbd->vkCode == VK_SHIFT || ...)) {This matches any Shift key-down while Alt is held, so Alt+Shift+Tab (reverse Alt-Tab), Alt+Shift+Esc, and every Alt+Shift+letter menu accelerator switch the keyboard layout and pop the flyout. Modifier keys also auto-repeat, so holding Alt+Shift cycles layouts continuously (the 250 ms debounce in Track the modifier sequence explicitly (Alt down → Shift down with no other key in between → act on release), ignore repeats via Separately, the 6. Win+Space swallows the Space but lets the Win key-up through, so the Start menu opens. The hook returns 7. Shared state is read and written from several threads without synchronization.
Simplest fix once the mod has a single owning thread (items 1 and 3): keep a settings struct guarded by 8. Globals with non-trivial destructors run at process shutdown, when On Explorer restart / sign-out / reboot the OS terminates all other threads first and only then runs global destructors, alone under the loader lock. Both types are handle-like ( [[clang::no_destroy]] static ScopedGdiplus g_gdiplus;
[[clang::no_destroy]] static ScopedHook g_keyboardHook;
[[clang::no_destroy]] static ScopedHook g_mouseHook;Keep the existing explicit 9. The window class is registered under Explorer's wc.hInstance = GetModuleHandleW(nullptr); // explorer.exe, not the mod
RegisterClassExW(&wc); // return value discarded
10.
11.
12. The README has no screenshot. This is a purely visual mod with two distinct styles, five themes and a light/dark split — a screenshot (or a short GIF) of each style is what users will judge it on in the catalog. 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 |
|
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 |
|
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 |
|
Hi, @m417z. Sorry for bothering, but it seems like the AI reviewer has had a problem and it says that there have been 3 AI reviews when only one has been done. |
|
Thanks for the review. This is a 1.0.0 release and the mod is stable and functional. I'll address the performance optimizations (#1, #3, #4) in future releases, and fix Alt+Shift (#2) in the next version. If the maintainer considers any of these blocking for 1.0.0, let me know and I'll try to fix them. |
|
You haven't answered my question:
If the answer is yes, please return false in |
Yes, the mod only needs to target the taskbar explorer.exe process. I'll fix this. |
Updated version log from v1.0.1 to v1.0.0 and added unloading logic for secondary explorer processes.
|
/ai-review |
|
Return |
Change return value from TRUE to FALSE when unloading.
Is it correct now or are there any other adjustments to include? |
|
This part is correct, yes. |
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 overall structure (dedicated worker + hook threads, per-load window class with no 1. if (!IsMainExplorerShell()) {
g_Initialized = TRUE;
DeleteCriticalSection(&g_Ctx.csLock);
return TRUE;
}Returning
Entering a deleted Return Worth double-checking the behaviour change this early return introduced, too: the comments at lines 2810-2815 and 2872-2876 still say the shell role is deliberately never baked in at init time and is re-evaluated by the worker's tick, but the new early return does bake it in. If the mod loads into a freshly started 2.
HWND hFore = GetForegroundWindow(); // -> Shell_TrayWnd
if (hFore != hWnd && hFore != hFlyout) { // hWnd is the *child* toolbar, so this passes
g_targetWindow.store(hFore, ...);
}
Add an explicit exclusion in one shared helper used by all three sites — you already have 3. Both low-level hooks are installed unconditionally, for the whole session
4. The tray retry timer polls forever (line 2627) UINT_PTR trayRetryTimer = SetTimer(NULL, 0, 1500, NULL);
The comment above the function says 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. |
Added IsTaskbarWindow function to check if a window is a taskbar window. Updated logic to prevent taskbar windows from being set as the target window in various functions.
|
/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.
1.
The retry logic makes it fire twice per switch: Please drop both if (hTarget && IsWindow(hTarget)) {
PostMessageW(hTarget, WM_INPUTLANGCHANGEREQUEST,
0, reinterpret_cast<LPARAM>(targetHkl));
}2. The if (!PostMessageW(hTarget, WM_INPUTLANGCHANGEREQUEST, ...)) {
SendMessageW(hTarget, WM_INPUTLANGCHANGEREQUEST, ...);
}This is a blocking cross-process send from the worker thread to an arbitrary application's window. If that app is hung, the send never returns — and 3. When the taskbar is foreground and no previous target is remembered, the code walks the desktop's children in Z order and takes the first visible non-taskbar top-level window: HWND hReal = GetWindow(GetDesktopWindow(), GW_CHILD);
while (hReal) {
if (IsWindowVisible(hReal) && !IsTaskbarWindow(hReal)) {
g_targetWindow.store(hReal, std::memory_order_release);
break;
}
hReal = GetWindow(hReal, GW_HWNDNEXT);
}That first entry is whatever happens to be topmost — very often a system helper window that is 4. The taskbar filter misses taskbar children, so the click path still captures the wrong window
Two things fix this properly:
5. The keyboard hook breaks the hook chain on Win key-up (line 2468) s_inKbdHook = false;
return 0;Returning without calling 6. Both low-level hooks are still installed unconditionally, for the whole session (lines 2657-2666) Repeating this from the previous review since it's unchanged: 7. The tray retry timer still polls forever (line 2702) Also unchanged: 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've checked myself and none of the problems seems to occur, if the maintainer believes that these problems need to be addressed I'll try to do it. 21:53:44.379 2444 explorer.exe [WH] [local@win7-language-switcher-restorer] [1008:SwitchToLayout]: SwitchToLayout: Changed to layout FFFFFFFFF0030410 for thread 5116 (window: 0000000000010372) 22:06:02.231 2444 explorer.exe [WH] [local@win7-language-switcher-restorer] [1008:SwitchToLayout]: SwitchToLayout: Changed to layout FFFFFFFFF0030410 for thread 10004 (window: 0000000000010332) /ready-for-reviewer |
This is a Windhawk mod that restores the Windows 7/8.1 Language switcher on the taskbar on Windows 10 and Windows 11. Credits to ac for the contribution.
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.