blackout_key.txt)./api/state.php?key=... 호출 → 본문이 "1"(정상) 또는 "0"(검정).0이면 가상 데스크톱 전체(=4패널 모두)를 덮는 검정 창 표시, 1이면 제거.{"ok":true,"key":"mt_xxxxxxxx"}1(정상) 또는 0(검정) · 한 글자만 보면 됨{"ok":true,"screen":0,"black":true,"auto_revert_in":540,"ts":...}| screen | 의미 | C++ 동작 |
|---|---|---|
| 1 | 화면 정상(켜짐) | 오버레이 제거 |
| 0 | 검정(블랙아웃) | 전 모니터 검정 덮기 |
※ 타이머 자동복귀는 서버가 알아서 처리합니다. C++는 그냥 10초마다 1/0만 읽으면 됩니다.
// blackout.cpp — m.tion.kr 모니터 블랙아웃 클라이언트 (Windows)
// 빌드(MSVC): cl /EHsc blackout.cpp winhttp.lib user32.lib gdi32.lib
// 빌드(MinGW): g++ blackout.cpp -o blackout.exe -lwinhttp -lgdi32 -luser32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winhttp.h>
#include <string>
#include <fstream>
#include <iostream>
#pragma comment(lib, "winhttp.lib")
static const wchar_t* HOST = L"m.tion.kr"; // 서버 주소
// ───── HTTPS GET → 응답 본문 반환 ─────
std::string httpGet(const std::wstring& path) {
std::string out;
HINTERNET hS = WinHttpOpen(L"blackout/1.0", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hS) return out;
HINTERNET hC = WinHttpConnect(hS, HOST, INTERNET_DEFAULT_HTTPS_PORT, 0);
if (hC) {
HINTERNET hR = WinHttpOpenRequest(hC, L"GET", path.c_str(), NULL,
WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (hR) {
if (WinHttpSendRequest(hR, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0)
&& WinHttpReceiveResponse(hR, NULL)) {
DWORD avail = 0;
do {
avail = 0; WinHttpQueryDataAvailable(hR, &avail);
if (!avail) break;
char buf[2048]; DWORD got = 0;
DWORD toRead = avail < sizeof(buf) ? avail : sizeof(buf);
if (WinHttpReadData(hR, buf, toRead, &got) && got) out.append(buf, got);
} while (avail > 0);
}
WinHttpCloseHandle(hR);
}
WinHttpCloseHandle(hC);
}
WinHttpCloseHandle(hS);
return out;
}
// ───── 키 저장/로드 (exe 옆 blackout_key.txt) ─────
std::string keyPath() {
char b[MAX_PATH]; GetModuleFileNameA(NULL, b, MAX_PATH);
std::string p(b); auto s = p.find_last_of("\\/");
return (s == std::string::npos ? "" : p.substr(0, s + 1)) + "blackout_key.txt";
}
std::string loadKey() { std::ifstream f(keyPath()); std::string k; std::getline(f, k); return k; }
void saveKey(const std::string& k) { std::ofstream f(keyPath()); f << k; }
// JSON에서 "field":"값" 추출 (라이브러리 없이)
std::string field(const std::string& j, const std::string& name) {
std::string pat = "\"" + name + "\":\""; auto p = j.find(pat);
if (p == std::string::npos) return "";
p += pat.size(); auto e = j.find('"', p);
return e == std::string::npos ? "" : j.substr(p, e - p);
}
// ───── 6자리 페어링 코드 → API 키 ─────
std::string pairWithCode() {
std::cout << "\n[연결] m.tion.kr 모바일에서 '연결 코드 만들기'를 누르세요.\n";
std::cout << "6자리 코드 입력: ";
std::string in; std::getline(std::cin, in);
std::string d; for (char c : in) if (c >= '0' && c <= '9') d += c;
std::wstring wd(d.begin(), d.end());
std::string body = httpGet(L"/api/pair_claim.php?code=" + wd);
std::string key = field(body, "key");
if (!key.empty()) { saveKey(key); std::cout << "연결 성공! 키 저장됨.\n"; }
else std::cout << "코드 만료/오류. 새 코드로 다시 시도하세요.\n";
return key;
}
// ───── 블랙아웃 오버레이: 모든 모니터(가상 데스크톱 전체)를 덮는 검정 창 ─────
HWND g_ov = NULL;
LRESULT CALLBACK WndProc(HWND h, UINT m, WPARAM w, LPARAM l) {
if (m == WM_PAINT) {
PAINTSTRUCT ps; HDC dc = BeginPaint(h, &ps); RECT r; GetClientRect(h, &r);
FillRect(dc, &r, (HBRUSH)GetStockObject(BLACK_BRUSH)); EndPaint(h, &ps); return 0;
}
return DefWindowProc(h, m, w, l);
}
void showBlack() {
if (g_ov) return;
static bool reg = false;
if (!reg) {
WNDCLASSA wc = {}; wc.lpfnWndProc = WndProc; wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = "BlackoutWnd"; wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hCursor = LoadCursor(NULL, IDC_ARROW); RegisterClassA(&wc); reg = true;
}
int x = GetSystemMetrics(SM_XVIRTUALSCREEN), y = GetSystemMetrics(SM_YVIRTUALSCREEN);
int cx = GetSystemMetrics(SM_CXVIRTUALSCREEN), cy = GetSystemMetrics(SM_CYVIRTUALSCREEN);
g_ov = CreateWindowExA(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, "BlackoutWnd", "",
WS_POPUP, x, y, cx, cy, NULL, NULL, GetModuleHandle(NULL), NULL);
ShowWindow(g_ov, SW_SHOW);
SetWindowPos(g_ov, HWND_TOPMOST, x, y, cx, cy, SWP_SHOWWINDOW);
}
void hideBlack() { if (g_ov) { DestroyWindow(g_ov); g_ov = NULL; } }
int main() {
std::string key = loadKey();
while (key.empty()) key = pairWithCode(); // 최초 1회만 코드 입력
std::wstring path = L"/api/state.php?key=" + std::wstring(key.begin(), key.end());
std::cout << "감시 시작 (10초 주기). 이 프로그램을 켜두면 자동으로 동작합니다.\n";
bool black = false;
for (;;) {
std::string s = httpGet(path); // 응답: "1"(정상) 또는 "0"(검정)
if (!s.empty()) {
bool wantBlack = (s[0] == '0');
if (wantBlack && !black) { showBlack(); black = true; }
else if (!wantBlack && black) { hideBlack(); black = false; }
}
if (black && g_ov) SetWindowPos(g_ov, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
// 10초 대기하면서 창 메시지 처리
for (int i = 0; i < 20; i++) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); }
Sleep(500);
}
}
return 0;
}
cl /EHsc blackout.cpp winhttp.lib user32.lib gdi32.lib
g++ blackout.cpp -o blackout.exe -lwinhttp -lgdi32 -luser32
blackout_key.txt에 저장됨).shell:startup 폴더에 바로가기)하면 부팅 시 자동 동작.-mwindows) 또는 작업 스케줄러 "사용자 로그온 시 / 숨김"으로 등록하세요.SM_CXVIRTUALSCREEN 전체를 덮어 모니터 개수와 무관하게 4패널 모두 검정 처리됩니다.blackout_key.txt 삭제 후 새 코드로 다시 페어링.