// Top-level window enumeration and focus via Win32 + DWM. #include #include "backends.hpp" #include "win_util.hpp" namespace alvin::win_impl { namespace { std::string processName(DWORD pid) { HANDLE proc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); if (!proc) return {}; wchar_t path[MAX_PATH]; DWORD size = MAX_PATH; std::string name; if (QueryFullProcessImageNameW(proc, 0, path, &size)) { std::wstring_view p(path, size); if (auto slash = p.find_last_of(L"\\/"); slash != std::wstring_view::npos) p.remove_prefix(slash + 1); name = narrow(p); } CloseHandle(proc); return name; } // Windows a user would consider "open windows" (roughly what Alt+Tab shows). bool isUserWindow(HWND hwnd) { if (!IsWindowVisible(hwnd) || GetWindow(hwnd, GW_OWNER) != nullptr) return false; if (GetWindowLongPtrW(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) return false; DWORD cloaked = 0; // hidden UWP frames and windows on other virtual desktops if (SUCCEEDED(DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, sizeof cloaked)) && cloaked) return false; return GetWindowTextLengthW(hwnd) > 0; } WindowInfo describe(HWND hwnd, HWND foreground) { WindowInfo w; w.id.value = reinterpret_cast(hwnd); int len = GetWindowTextLengthW(hwnd); std::wstring title(static_cast(len) + 1, L'\0'); GetWindowTextW(hwnd, title.data(), len + 1); title.resize(static_cast(len)); w.title = narrow(title); DWORD pid = 0; GetWindowThreadProcessId(hwnd, &pid); w.pid = pid; w.appId = processName(pid); // Extended frame bounds exclude the invisible resize borders of Windows 10+. RECT r{}; if (FAILED(DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &r, sizeof r))) GetWindowRect(hwnd, &r); w.bounds = {r.left, r.top, r.right - r.left, r.bottom - r.top}; w.focused = hwnd == foreground; w.minimized = IsIconic(hwnd) != 0; w.visible = !w.minimized; HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL); MONITORINFO mi{}; mi.cbSize = sizeof mi; if (mon && GetMonitorInfoW(mon, &mi)) { const RECT& m = mi.rcMonitor; w.fullscreen = r.left <= m.left && r.top <= m.top && r.right >= m.right && r.bottom >= m.bottom; } return w; } class Win32Windows final : public WindowManager { public: WindowBackend backend() const override { return WindowBackend::Win32; } Result> list() override { struct Ctx { HWND foreground; std::vector out; } ctx{GetForegroundWindow(), {}}; EnumWindows( [](HWND hwnd, LPARAM data) -> BOOL { auto* c = reinterpret_cast(data); if (isUserWindow(hwnd)) c->out.push_back(describe(hwnd, c->foreground)); return TRUE; }, reinterpret_cast(&ctx)); return std::move(ctx.out); } Result> active() override { HWND fg = GetForegroundWindow(); if (!fg) return std::optional{}; return std::optional{describe(fg, fg)}; } Status focus(WindowId id) override { HWND hwnd = reinterpret_cast(id.value); if (!IsWindow(hwnd)) return fail(ErrorCode::NotFound, "no such window"); if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE); if (SetForegroundWindow(hwnd)) return {}; // The foreground lock only lets the process that received the last input // change focus. A synthetic Alt tap makes us that process. INPUT alt[2]{}; alt[0].type = alt[1].type = INPUT_KEYBOARD; alt[0].ki.wVk = alt[1].ki.wVk = VK_MENU; alt[1].ki.dwFlags = KEYEVENTF_KEYUP; SendInput(2, alt, sizeof(INPUT)); if (SetForegroundWindow(hwnd)) return {}; return fail(ErrorCode::PermissionDenied, "Windows refused to change the foreground window"); } }; } // namespace Result> createWin32Windows() { ensureDpiAware(); return std::make_unique(); } } // namespace alvin::win_impl