Add window-based target picker as the default; process list goes advanced
New host/src/inject/window_list.{hpp,cpp} enumerates visible, titled, non-tool
top-level (alt-tab-style) windows via EnumWindows -- root-owner only, our own
process excluded -- and maps each to its owning pid + image name. The Injection
panel now defaults to this window list (each row "title [process.exe pid]", with a
filter over title or process), since there are far fewer windows than processes and
a window maps straight to the HWND the capturer wants. The full process list stays
as the advanced picker under Debug details.
Verified live: launched the host and captured its window -- the picker lists real
windows (Discord/Firefox/Explorer/...) in "title [exe pid]" form, filter present,
and the host's own window correctly excluded. x64 build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
82
host/src/inject/window_list.cpp
Normal file
82
host/src/inject/window_list.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "inject/window_list.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "inject/process_list.hpp"
|
||||
|
||||
namespace coop
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct EnumCtx
|
||||
{
|
||||
std::vector<WindowEntry>* out;
|
||||
const std::unordered_map<unsigned long, std::wstring>* names;
|
||||
DWORD self_pid;
|
||||
};
|
||||
|
||||
BOOL CALLBACK enum_proc(HWND hwnd, LPARAM lparam)
|
||||
{
|
||||
auto* ctx = reinterpret_cast<EnumCtx*>(lparam);
|
||||
|
||||
// Keep only "alt-tab" windows: visible, titled, root-owner, non-tool, not ours.
|
||||
if (!IsWindowVisible(hwnd) || GetAncestor(hwnd, GA_ROOTOWNER) != hwnd)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
const int len = GetWindowTextLengthW(hwnd);
|
||||
if (len <= 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
if ((GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) != 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
DWORD pid = 0;
|
||||
GetWindowThreadProcessId(hwnd, &pid);
|
||||
if (pid == 0 || pid == ctx->self_pid)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
std::wstring title(static_cast<std::size_t>(len), L'\0');
|
||||
GetWindowTextW(hwnd, title.data(), len + 1);
|
||||
|
||||
std::wstring exe;
|
||||
if (const auto it = ctx->names->find(pid); it != ctx->names->end())
|
||||
{
|
||||
exe = it->second;
|
||||
}
|
||||
ctx->out->push_back(WindowEntry{pid, hwnd, std::move(title), std::move(exe)});
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<WindowEntry> list_windows()
|
||||
{
|
||||
// pid -> image name, so each window can show its owning process without a separate
|
||||
// OpenProcess per window.
|
||||
std::unordered_map<unsigned long, std::wstring> names;
|
||||
for (const ProcessEntry& p : list_processes())
|
||||
{
|
||||
names.emplace(p.pid, p.exe_name);
|
||||
}
|
||||
|
||||
std::vector<WindowEntry> out;
|
||||
EnumCtx ctx{&out, &names, GetCurrentProcessId()};
|
||||
EnumWindows(&enum_proc, reinterpret_cast<LPARAM>(&ctx));
|
||||
|
||||
std::sort(out.begin(), out.end(), [](const WindowEntry& a, const WindowEntry& b) {
|
||||
return _wcsicmp(a.title.c_str(), b.title.c_str()) < 0;
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace coop
|
||||
24
host/src/inject/window_list.hpp
Normal file
24
host/src/inject/window_list.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// Enumerates visible top-level windows for the injection target picker. There are
|
||||
// far fewer windows than processes, and a window maps directly to the HWND the
|
||||
// capturer wants, so this is the default (friendlier) way to pick a game.
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace coop
|
||||
{
|
||||
|
||||
struct WindowEntry
|
||||
{
|
||||
unsigned long pid = 0; // owning process id
|
||||
void* hwnd = nullptr; // HWND (opaque here to keep windows.h out of the header)
|
||||
std::wstring title; // window caption
|
||||
std::wstring exe_name; // owning process image base name, e.g. "game.exe"
|
||||
};
|
||||
|
||||
// Snapshot of the visible, titled, non-tool top-level (alt-tab-style) windows, with
|
||||
// our own process's windows excluded. Sorted by title (case-insensitive).
|
||||
std::vector<WindowEntry> list_windows();
|
||||
|
||||
} // namespace coop
|
||||
Reference in New Issue
Block a user