Created
October 17, 2021 08:05
-
-
Save Helios-vmg/940a977ceb687b0babf9b9811f11cd3d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <vector> | |
#include <optional> | |
#include <sstream> | |
#include <stdexcept> | |
#include <functional> | |
#include <Windows.h> | |
typedef std::function<bool(HWND)> enumerate_windows_cb; | |
static BOOL CALLBACK EnumWindows_cb(HWND handle, LPARAM param){ | |
auto &cb = *(const enumerate_windows_cb *)param; | |
return cb(handle); | |
} | |
void enumerate_windows(const enumerate_windows_cb &cb){ | |
if (!EnumWindows(EnumWindows_cb, (LPARAM)&cb)){ | |
auto error = GetLastError(); | |
if (!error) | |
return; | |
std::stringstream stream; | |
stream << "EnumWindows() failed with error " << error; | |
throw std::runtime_error(stream.str()); | |
} | |
} | |
std::string read_string(){ | |
std::string ret; | |
std::getline(std::cin, ret); | |
return ret; | |
} | |
std::optional<int> read_number(){ | |
auto input = read_string(); | |
std::stringstream stream(input); | |
int ret; | |
if (!(stream >> ret)) | |
return {}; | |
return ret; | |
} | |
std::wstring to_wstring(const std::string &s){ | |
std::wstring ret; | |
ret.reserve(s.size()); | |
for (auto c : s) | |
ret += (wchar_t)c; | |
return ret; | |
} | |
std::string to_string(const std::wstring &s){ | |
std::string ret; | |
ret.reserve(s.size()); | |
for (auto c : s) | |
ret += c < 0x80 ? (char)c : '?'; | |
return ret; | |
} | |
std::wstring get_window_text(HWND handle){ | |
std::wstring ret(8192, 0); | |
auto length = GetWindowTextW(handle, &ret[0], ret.size()); | |
if (!length){ | |
auto error = GetLastError(); | |
if (!error) | |
return {}; | |
std::stringstream stream; | |
stream << "GetWindowTextW() failed with error " << error; | |
throw std::runtime_error(stream.str()); | |
} | |
ret.resize(length); | |
ret.shrink_to_fit(); | |
return ret; | |
} | |
bool matches(const std::wstring &text, const std::wstring &pattern){ | |
if (text.size() < pattern.size()) | |
return false; | |
auto n = text.size() - pattern.size() + 1; | |
for (size_t i = 0; i < n; i++){ | |
bool match = true; | |
for (size_t j = 0; j < pattern.size(); j++){ | |
if (tolower(text[i + j]) != tolower(pattern[j])){ | |
match = false; | |
break; | |
} | |
} | |
if (match) | |
return true; | |
} | |
return false; | |
} | |
class Window{ | |
public: | |
HWND handle; | |
std::wstring title; | |
DWORD pid; | |
Window() = default; | |
Window(const Window &) = default; | |
Window &operator=(const Window &) = default; | |
Window(Window &&other){ | |
*this = std::move(other); | |
} | |
Window &operator=(Window &&other){ | |
this->handle = other.handle; | |
this->title = std::move(other.title); | |
this->pid = other.pid; | |
return *this; | |
} | |
Window(HWND handle){ | |
this->handle = handle; | |
this->title = get_window_text(handle); | |
GetWindowThreadProcessId(this->handle, &this->pid); | |
} | |
}; | |
void bring_to_front(HWND handle){ | |
WINDOWPLACEMENT placement; | |
if (!GetWindowPlacement(handle, &placement)){ | |
auto error = GetLastError(); | |
std::stringstream stream; | |
stream << "GetWindowPlacement() failed with error " << error; | |
throw std::runtime_error(stream.str()); | |
} | |
int state; | |
switch (placement.showCmd){ | |
case SW_SHOWMINIMIZED: | |
state = SW_RESTORE; | |
break; | |
case SW_SHOWMAXIMIZED: | |
state = SW_SHOWMAXIMIZED; | |
break; | |
default: | |
state = SW_NORMAL; | |
break; | |
} | |
if (!ShowWindow(handle, state)){ | |
auto error = GetLastError(); | |
std::stringstream stream; | |
std::cerr << "ShowWindow() failed with error " << error << ". Will continue anyway.\n"; | |
} | |
if (!SetForegroundWindow(handle)){ | |
auto error = GetLastError(); | |
std::stringstream stream; | |
stream << "SetForegroundWindow() failed with error " << error; | |
throw std::runtime_error(stream.str()); | |
} | |
} | |
std::optional<Window> find_window(const std::wstring &query){ | |
std::vector<Window> non_matching_windows; | |
std::optional<Window> ret; | |
enumerate_windows([&query, &ret, &non_matching_windows](HWND handle){ | |
Window window(handle); | |
if (matches(window.title, query)){ | |
ret = std::move(window); | |
return false; | |
} | |
non_matching_windows.emplace_back(std::move(window)); | |
return true; | |
}); | |
if (!ret){ | |
std::cout << "Window not found. The following titles did not match the query:\n"; | |
for (auto &w : non_matching_windows) | |
std::cout << to_string(w.title) << std::endl; | |
return {}; | |
} | |
return ret; | |
} | |
void operate_one_window(){ | |
std::cout << "Please enter a string to match against: "; | |
auto query = to_wstring(read_string()); | |
auto o = find_window(query); | |
if (!o) | |
return; | |
auto window = std::move(*o); | |
std::cout << | |
"Window found: " << to_string(window.title) << " PID " << window.pid << "\n" | |
"Press ENTER to bring it to front.\n"; | |
read_string(); | |
bring_to_front(window.handle); | |
} | |
void operate_two_windows(){ | |
std::cout << "Please enter a string to match against: "; | |
auto o = find_window(to_wstring(read_string())); | |
if (!o) | |
return; | |
auto window1 = std::move(*o); | |
std::cout << | |
"Window found: " << to_string(window1.title) << " PID " << window1.pid << "\n" | |
"Please enter another string to match against: "; | |
o = find_window(to_wstring(read_string())); | |
if (!o) | |
return; | |
auto window2 = std::move(*o); | |
std::cout << | |
"Window found: " << to_string(window2.title) << " PID " << window2.pid << "\n" | |
"Press ENTER to start.\n"; | |
read_string(); | |
for (int i = 0; i < 5; i++){ | |
bring_to_front(window1.handle); | |
Sleep(1000); | |
bring_to_front(window2.handle); | |
Sleep(1000); | |
} | |
} | |
int main(){ | |
try{ | |
int mode; | |
while (true){ | |
std::cout << | |
"Select operation mode:\n" | |
"1. Bring one window to front.\n" | |
"2. Repeatedly switch between two windows.\n"; | |
auto input = read_number(); | |
if (!input){ | |
std::cerr << "You must enter a number.\n"; | |
continue; | |
} | |
mode = *input; | |
if (mode < 1 || mode > 2){ | |
std::cerr << "You must enter either 1 or 2.\n"; | |
continue; | |
} | |
break; | |
} | |
if (mode == 1) | |
operate_one_window(); | |
else | |
operate_two_windows(); | |
}catch (std::exception &e){ | |
std::cerr << "Exception caught: " << e.what() << std::endl; | |
return -1; | |
}catch (...){ | |
std::cerr << "Unknown exception caught.\n"; | |
return -1; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment