487 lines
18 KiB
C
487 lines
18 KiB
C
// SeeThru: make any window see-through, all the way down to 5% opacity.
|
|
// Ctrl+Shift + mouse wheel over a window change that window's opacity
|
|
// Ctrl+Alt+PageDown / PageUp more / less see-through (active window)
|
|
// Ctrl+Alt+Home back to fully solid (active window)
|
|
// Ctrl+Alt+End ghost mode: click-through and always on top
|
|
|
|
#ifndef UNICODE
|
|
#define UNICODE
|
|
#endif
|
|
#ifndef _UNICODE
|
|
#define _UNICODE
|
|
#endif
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define _WIN32_WINNT 0x0A00
|
|
#include <windows.h>
|
|
#include "desktools.h"
|
|
#include <shellapi.h>
|
|
#include <stdio.h>
|
|
#include <wchar.h>
|
|
|
|
#define APP_NAME L"SeeThru"
|
|
#define WM_TRAY (WM_APP + 1)
|
|
#define WM_WHEELADJ (WM_APP + 2)
|
|
#define MAX_ENT 128
|
|
#define MIN_PCT 5
|
|
#define STEP_PCT 5
|
|
|
|
#define HK_MORE 1
|
|
#define HK_LESS 2
|
|
#define HK_RESET 3
|
|
#define HK_GHOST 4
|
|
|
|
#define TIMER_OSD 1
|
|
#define TIMER_CLEAN 2
|
|
|
|
#define IDM_RESETALL 100
|
|
#define IDM_STARTUP 101
|
|
#define IDM_HELP 102
|
|
#define IDM_EXIT 103
|
|
#define IDM_WIN_BASE 1000
|
|
|
|
typedef struct {
|
|
BOOL used;
|
|
HWND h;
|
|
LONG origEx; // extended style before we touched it
|
|
BOOL hadLayered; // window was already layered (alpha based)
|
|
BYTE origAlpha;
|
|
DWORD origFlags;
|
|
COLORREF origKey;
|
|
int pct; // current opacity percent
|
|
BOOL ghost;
|
|
BOOL wasTopmost;
|
|
} Ent;
|
|
|
|
static Ent g_ent[MAX_ENT];
|
|
static BOOL g_enabled = TRUE;
|
|
static HINSTANCE g_inst;
|
|
static HWND g_main, g_osd;
|
|
static HHOOK g_hook;
|
|
static NOTIFYICONDATAW g_nid;
|
|
static UINT g_taskbarCreated;
|
|
static int g_dpi = 96;
|
|
static HFONT g_osdFont;
|
|
static WCHAR g_osdText[128];
|
|
|
|
static int S(int v) { return MulDiv(v, g_dpi, 96); }
|
|
|
|
/* ---------------- startup setting ---------------- */
|
|
|
|
static const WCHAR *RUN_KEY = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
static BOOL StartupEnabled(void) {
|
|
WCHAR buf[MAX_PATH * 2]; DWORD sz = sizeof(buf);
|
|
return RegGetValueW(HKEY_CURRENT_USER, RUN_KEY, APP_NAME, RRF_RT_REG_SZ, NULL, buf, &sz) == ERROR_SUCCESS;
|
|
}
|
|
static void SetStartup(BOOL on) {
|
|
HKEY k;
|
|
if (RegOpenKeyExW(HKEY_CURRENT_USER, RUN_KEY, 0, KEY_WRITE, &k) != ERROR_SUCCESS) return;
|
|
if (on) {
|
|
WCHAR exe[MAX_PATH], val[MAX_PATH + 4];
|
|
GetModuleFileNameW(NULL, exe, MAX_PATH);
|
|
swprintf(val, MAX_PATH + 4, L"\"%ls\"", exe);
|
|
RegSetValueExW(k, APP_NAME, 0, REG_SZ, (BYTE *)val, (DWORD)((wcslen(val) + 1) * sizeof(WCHAR)));
|
|
} else {
|
|
RegDeleteValueW(k, APP_NAME);
|
|
}
|
|
RegCloseKey(k);
|
|
}
|
|
static DWORD RegGetFlag(const WCHAR *name) {
|
|
DWORD v = 0, sz = sizeof(v);
|
|
RegGetValueW(HKEY_CURRENT_USER, L"Software\\SeeThru", name, RRF_RT_REG_DWORD, NULL, &v, &sz);
|
|
return v;
|
|
}
|
|
static void RegSetFlag(const WCHAR *name, DWORD v) {
|
|
HKEY k;
|
|
if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\SeeThru", 0, NULL, 0, KEY_WRITE, NULL, &k, NULL) == ERROR_SUCCESS) {
|
|
RegSetValueExW(k, name, 0, REG_DWORD, (BYTE *)&v, sizeof(v));
|
|
RegCloseKey(k);
|
|
}
|
|
}
|
|
|
|
/* ---------------- tray ---------------- */
|
|
|
|
static void Balloon(const WCHAR *title, const WCHAR *text) { DT_Balloon(title, text); }
|
|
|
|
static void AddTrayIcon(void) { }
|
|
|
|
/* ---------------- on-screen indicator ---------------- */
|
|
|
|
static void ShowOsd(HWND target, const WCHAR *text) {
|
|
wcsncpy(g_osdText, text, 127); g_osdText[127] = 0;
|
|
int w = S(220), h = S(46);
|
|
RECT r;
|
|
if (target && IsWindow(target) && GetWindowRect(target, &r) && r.right > r.left) {
|
|
// center on the target window, clamped to its monitor
|
|
HMONITOR mon = MonitorFromWindow(target, MONITOR_DEFAULTTONEAREST);
|
|
MONITORINFO mi = { sizeof(mi) }; GetMonitorInfoW(mon, &mi);
|
|
int cx = (r.left + r.right) / 2, cy = (r.top + r.bottom) / 2;
|
|
if (cx < mi.rcWork.left + w / 2) cx = mi.rcWork.left + w / 2;
|
|
if (cx > mi.rcWork.right - w / 2) cx = mi.rcWork.right - w / 2;
|
|
if (cy < mi.rcWork.top + h / 2) cy = mi.rcWork.top + h / 2;
|
|
if (cy > mi.rcWork.bottom - h / 2) cy = mi.rcWork.bottom - h / 2;
|
|
r.left = cx - w / 2; r.top = cy - h / 2;
|
|
} else {
|
|
POINT p; GetCursorPos(&p);
|
|
r.left = p.x - w / 2; r.top = p.y + S(24);
|
|
}
|
|
HRGN rgn = CreateRoundRectRgn(0, 0, w + 1, h + 1, S(12), S(12));
|
|
SetWindowRgn(g_osd, rgn, FALSE);
|
|
SetWindowPos(g_osd, HWND_TOPMOST, r.left, r.top, w, h, SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
|
InvalidateRect(g_osd, NULL, TRUE);
|
|
UpdateWindow(g_osd);
|
|
SetTimer(g_main, TIMER_OSD, 1100, NULL);
|
|
}
|
|
|
|
static LRESULT CALLBACK OsdProc(HWND h, UINT msg, WPARAM wp, LPARAM lp) {
|
|
switch (msg) {
|
|
case WM_PAINT: {
|
|
PAINTSTRUCT ps; HDC dc = BeginPaint(h, &ps);
|
|
RECT rc; GetClientRect(h, &rc);
|
|
HBRUSH bg = CreateSolidBrush(RGB(32, 36, 44));
|
|
FillRect(dc, &rc, bg); DeleteObject(bg);
|
|
SelectObject(dc, g_osdFont);
|
|
SetBkMode(dc, TRANSPARENT);
|
|
SetTextColor(dc, RGB(255, 255, 255));
|
|
DrawTextW(dc, g_osdText, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
|
|
EndPaint(h, &ps);
|
|
return 0;
|
|
}
|
|
case WM_MOUSEACTIVATE: return MA_NOACTIVATE;
|
|
case WM_NCHITTEST: return HTTRANSPARENT;
|
|
}
|
|
return DefWindowProcW(h, msg, wp, lp);
|
|
}
|
|
|
|
/* ---------------- transparency core ---------------- */
|
|
|
|
static Ent *FindEnt(HWND h) {
|
|
for (int i = 0; i < MAX_ENT; i++) if (g_ent[i].used && g_ent[i].h == h) return &g_ent[i];
|
|
return NULL;
|
|
}
|
|
|
|
static BOOL Allowed(HWND h) {
|
|
if (!h || !IsWindow(h)) return FALSE;
|
|
DWORD pid = 0; GetWindowThreadProcessId(h, &pid);
|
|
if (pid == GetCurrentProcessId()) return FALSE;
|
|
WCHAR cls[64]; GetClassNameW(h, cls, 64);
|
|
if (!wcscmp(cls, L"Progman") || !wcscmp(cls, L"WorkerW")) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
// Creates (or returns) the tracking entry. Returns NULL if the window can't be handled.
|
|
static Ent *GetEnt(HWND h, const WCHAR **err) {
|
|
Ent *e = FindEnt(h);
|
|
if (e) return e;
|
|
LONG ex = GetWindowLongW(h, GWL_EXSTYLE);
|
|
BYTE a = 255; DWORD flags = 0; COLORREF key = 0;
|
|
BOOL hadLayered = (ex & WS_EX_LAYERED) != 0;
|
|
if (hadLayered) {
|
|
if (!GetLayeredWindowAttributes(h, &key, &a, &flags)) {
|
|
*err = L"This window draws its own transparency";
|
|
return NULL;
|
|
}
|
|
if (!(flags & LWA_ALPHA)) a = 255;
|
|
}
|
|
for (int i = 0; i < MAX_ENT; i++) {
|
|
if (!g_ent[i].used) {
|
|
e = &g_ent[i];
|
|
ZeroMemory(e, sizeof(*e));
|
|
e->used = TRUE; e->h = h; e->origEx = ex;
|
|
e->hadLayered = hadLayered; e->origAlpha = a; e->origFlags = flags; e->origKey = key;
|
|
e->pct = (a * 100 + 127) / 255;
|
|
e->wasTopmost = (ex & WS_EX_TOPMOST) != 0;
|
|
return e;
|
|
}
|
|
}
|
|
*err = L"Too many windows";
|
|
return NULL;
|
|
}
|
|
|
|
static void RestoreEnt(Ent *e) {
|
|
if (!e->used) return;
|
|
HWND h = e->h;
|
|
if (IsWindow(h)) {
|
|
if (e->ghost && !e->wasTopmost)
|
|
SetWindowPos(h, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
LONG cur = GetWindowLongW(h, GWL_EXSTYLE);
|
|
LONG want = (cur & ~(WS_EX_LAYERED | WS_EX_TRANSPARENT)) |
|
|
(e->origEx & (WS_EX_LAYERED | WS_EX_TRANSPARENT));
|
|
if (e->hadLayered) {
|
|
SetWindowLongW(h, GWL_EXSTYLE, want);
|
|
SetLayeredWindowAttributes(h, e->origKey, e->origAlpha, e->origFlags);
|
|
} else {
|
|
SetLayeredWindowAttributes(h, 0, 255, LWA_ALPHA);
|
|
SetWindowLongW(h, GWL_EXSTYLE, want);
|
|
}
|
|
RedrawWindow(h, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
|
|
}
|
|
e->used = FALSE;
|
|
}
|
|
|
|
static void ApplyEnt(Ent *e) {
|
|
HWND h = e->h;
|
|
LONG ex = GetWindowLongW(h, GWL_EXSTYLE) | WS_EX_LAYERED;
|
|
if (e->ghost) ex |= WS_EX_TRANSPARENT;
|
|
else ex = (ex & ~WS_EX_TRANSPARENT) | (e->origEx & WS_EX_TRANSPARENT);
|
|
SetWindowLongW(h, GWL_EXSTYLE, ex);
|
|
BYTE a = (BYTE)((e->pct * 255 + 50) / 100);
|
|
DWORD flags = LWA_ALPHA;
|
|
if (e->hadLayered && (e->origFlags & LWA_COLORKEY)) flags |= LWA_COLORKEY;
|
|
SetLayeredWindowAttributes(h, e->origKey, a, flags);
|
|
}
|
|
|
|
static BOOL Writable(HWND h) {
|
|
// A window we can't change (admin program) silently ignores SetWindowLong.
|
|
LONG ex = GetWindowLongW(h, GWL_EXSTYLE);
|
|
return (ex & WS_EX_LAYERED) != 0;
|
|
}
|
|
|
|
static void ChangeOpacity(HWND h, int deltaPct) {
|
|
if (!Allowed(h)) return;
|
|
const WCHAR *err = NULL;
|
|
Ent *e = GetEnt(h, &err);
|
|
if (!e) { ShowOsd(h, err); return; }
|
|
int pct = e->pct + deltaPct;
|
|
if (pct < MIN_PCT) pct = MIN_PCT;
|
|
if (pct > 100) pct = 100;
|
|
e->pct = pct;
|
|
if (pct >= 100 && !e->ghost) {
|
|
RestoreEnt(e);
|
|
ShowOsd(h, L"Opacity 100%");
|
|
return;
|
|
}
|
|
ApplyEnt(e);
|
|
if (!Writable(h)) {
|
|
e->used = FALSE;
|
|
ShowOsd(h, L"Can't change this window (admin program?)");
|
|
return;
|
|
}
|
|
WCHAR t[64];
|
|
swprintf(t, 64, e->ghost ? L"Opacity %d%% (ghost)" : L"Opacity %d%%", pct);
|
|
ShowOsd(h, t);
|
|
}
|
|
|
|
static void ResetWindow(HWND h) {
|
|
Ent *e = FindEnt(h);
|
|
if (e) RestoreEnt(e);
|
|
ShowOsd(h, L"Opacity 100%");
|
|
}
|
|
|
|
static void ToggleGhost(HWND h) {
|
|
if (!Allowed(h)) return;
|
|
const WCHAR *err = NULL;
|
|
Ent *e = GetEnt(h, &err);
|
|
if (!e) { ShowOsd(h, err); return; }
|
|
if (!e->ghost) {
|
|
e->ghost = TRUE;
|
|
if (e->pct > 60) e->pct = 60; // ghosts should be see-through so you can tell
|
|
ApplyEnt(e);
|
|
if (!Writable(h)) { e->used = FALSE; ShowOsd(h, L"Can't change this window (admin program?)"); return; }
|
|
SetWindowPos(h, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
WCHAR t[64]; swprintf(t, 64, L"Ghost mode on (%d%%)", e->pct);
|
|
ShowOsd(h, t);
|
|
} else {
|
|
e->ghost = FALSE;
|
|
if (!e->wasTopmost) SetWindowPos(h, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
if (e->pct >= 100) RestoreEnt(e); else ApplyEnt(e);
|
|
ShowOsd(h, L"Ghost mode off");
|
|
}
|
|
}
|
|
|
|
static void ResetAll(void) {
|
|
for (int i = 0; i < MAX_ENT; i++) if (g_ent[i].used) RestoreEnt(&g_ent[i]);
|
|
}
|
|
|
|
/* ---------------- mouse wheel hook ---------------- */
|
|
|
|
int ST_Mouse(WPARAM wp, MSLLHOOKSTRUCT *m) {
|
|
if (!g_enabled || wp != WM_MOUSEWHEEL) return 0;
|
|
if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) && (GetAsyncKeyState(VK_SHIFT) & 0x8000) &&
|
|
!(GetAsyncKeyState(VK_MENU) & 0x8000)) {
|
|
HWND w = WindowFromPoint(m->pt);
|
|
HWND root = w ? GetAncestor(w, GA_ROOT) : NULL;
|
|
if (root) {
|
|
short delta = (short)HIWORD(m->mouseData);
|
|
PostMessageW(g_main, WM_WHEELADJ, (WPARAM)root, (LPARAM)delta);
|
|
return 1; // swallow so the app underneath doesn't zoom or scroll
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------- menus ---------------- */
|
|
|
|
static void ShowHelp(void) {
|
|
MessageBoxW(NULL,
|
|
L"Change how see-through a window is:\n"
|
|
L" Hold Ctrl+Shift and scroll the mouse wheel over any window.\n"
|
|
L" Scroll down for more see-through, up for more solid.\n\n"
|
|
L"Keyboard (works on the active window):\n"
|
|
L" Ctrl+Alt+Page Down more see-through\n"
|
|
L" Ctrl+Alt+Page Up more solid\n"
|
|
L" Ctrl+Alt+Home back to fully solid\n"
|
|
L" Ctrl+Alt+End ghost mode on or off\n\n"
|
|
L"Ghost mode keeps the window on top of everything and lets your\n"
|
|
L"clicks pass straight through it to whatever is underneath.\n"
|
|
L"To turn ghost mode off, click the window's taskbar button so it\n"
|
|
L"becomes active, then press Ctrl+Alt+End again. You can also pick\n"
|
|
L"the window from the SeeThru tray menu to reset it.\n\n"
|
|
L"Windows can go as low as 5% opacity.\n"
|
|
L"Exiting SeeThru puts every window back to normal.",
|
|
L"How to use SeeThru", MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
|
|
static void ShowTrayMenu(void) {
|
|
HMENU m = CreatePopupMenu();
|
|
AppendMenuW(m, MF_STRING | MF_GRAYED, 0, L"SeeThru");
|
|
AppendMenuW(m, MF_SEPARATOR, 0, NULL);
|
|
int n = 0;
|
|
for (int i = 0; i < MAX_ENT; i++) {
|
|
Ent *e = &g_ent[i];
|
|
if (!e->used || !IsWindow(e->h)) continue;
|
|
WCHAR title[80], item[128];
|
|
GetWindowTextW(e->h, title, 60);
|
|
if (!title[0]) wcscpy(title, L"(untitled)");
|
|
swprintf(item, 128, L"Reset: %ls (%d%%%ls)", title, e->pct, e->ghost ? L", ghost" : L"");
|
|
AppendMenuW(m, MF_STRING, IDM_WIN_BASE + i, item);
|
|
n++;
|
|
}
|
|
if (n) AppendMenuW(m, MF_SEPARATOR, 0, NULL);
|
|
AppendMenuW(m, MF_STRING | (n ? 0 : MF_GRAYED), IDM_RESETALL, L"Reset all windows");
|
|
AppendMenuW(m, MF_STRING | (StartupEnabled() ? MF_CHECKED : 0), IDM_STARTUP, L"Start with Windows");
|
|
AppendMenuW(m, MF_STRING, IDM_HELP, L"How to use");
|
|
AppendMenuW(m, MF_SEPARATOR, 0, NULL);
|
|
AppendMenuW(m, MF_STRING, IDM_EXIT, L"Exit (puts windows back to normal)");
|
|
POINT p; GetCursorPos(&p);
|
|
SetForegroundWindow(g_main);
|
|
TrackPopupMenu(m, TPM_RIGHTBUTTON | TPM_BOTTOMALIGN, p.x, p.y, 0, g_main, NULL);
|
|
PostMessageW(g_main, WM_NULL, 0, 0);
|
|
DestroyMenu(m);
|
|
}
|
|
|
|
/* ---------------- main window ---------------- */
|
|
|
|
static LRESULT CALLBACK MainProc(HWND h, UINT msg, WPARAM wp, LPARAM lp) {
|
|
if (msg == g_taskbarCreated && g_taskbarCreated) { AddTrayIcon(); return 0; }
|
|
switch (msg) {
|
|
case WM_TRAY:
|
|
if (LOWORD(lp) == WM_RBUTTONUP || LOWORD(lp) == WM_LBUTTONUP) ShowTrayMenu();
|
|
return 0;
|
|
case WM_WHEELADJ: {
|
|
int notches = (int)(short)lp / WHEEL_DELTA;
|
|
if (notches == 0) notches = ((short)lp > 0) ? 1 : -1;
|
|
ChangeOpacity((HWND)wp, notches * STEP_PCT); // wheel up = more solid
|
|
return 0;
|
|
}
|
|
case WM_HOTKEY: {
|
|
HWND fg = GetForegroundWindow();
|
|
if (fg) fg = GetAncestor(fg, GA_ROOT);
|
|
switch (wp) {
|
|
case HK_MORE: ChangeOpacity(fg, -STEP_PCT); break;
|
|
case HK_LESS: ChangeOpacity(fg, +STEP_PCT); break;
|
|
case HK_RESET: if (Allowed(fg)) ResetWindow(fg); break;
|
|
case HK_GHOST: ToggleGhost(fg); break;
|
|
}
|
|
return 0;
|
|
}
|
|
case WM_TIMER:
|
|
if (wp == TIMER_OSD) { KillTimer(h, TIMER_OSD); ShowWindow(g_osd, SW_HIDE); }
|
|
else if (wp == TIMER_CLEAN) {
|
|
for (int i = 0; i < MAX_ENT; i++) if (g_ent[i].used && !IsWindow(g_ent[i].h)) g_ent[i].used = FALSE;
|
|
}
|
|
return 0;
|
|
case WM_COMMAND: {
|
|
UINT id = LOWORD(wp);
|
|
if (id >= IDM_WIN_BASE && id < IDM_WIN_BASE + MAX_ENT) {
|
|
Ent *e = &g_ent[id - IDM_WIN_BASE];
|
|
if (e->used) RestoreEnt(e);
|
|
return 0;
|
|
}
|
|
switch (id) {
|
|
case IDM_RESETALL: ResetAll(); break;
|
|
case IDM_STARTUP: SetStartup(!StartupEnabled()); break;
|
|
case IDM_HELP: ShowHelp(); break;
|
|
case IDM_EXIT: DestroyWindow(h); break;
|
|
}
|
|
return 0;
|
|
}
|
|
case WM_QUERYENDSESSION:
|
|
ResetAll();
|
|
return TRUE;
|
|
case WM_DESTROY:
|
|
ResetAll();
|
|
if (g_hook) UnhookWindowsHookEx(g_hook);
|
|
for (int i = HK_MORE; i <= HK_GHOST; i++) UnregisterHotKey(h, i);
|
|
Shell_NotifyIconW(NIM_DELETE, &g_nid);
|
|
|
|
return 0;
|
|
}
|
|
return DefWindowProcW(h, msg, wp, lp);
|
|
}
|
|
|
|
/* ---------------- DeskTools module interface ---------------- */
|
|
|
|
static BOOL g_hkOk[5];
|
|
void ST_SetEnabled(BOOL on) {
|
|
static const UINT vks[5] = { 0, VK_NEXT, VK_PRIOR, VK_HOME, VK_END };
|
|
g_enabled = on;
|
|
for (int i = HK_MORE; i <= HK_GHOST; i++) {
|
|
if (on && !g_hkOk[i]) g_hkOk[i] = RegisterHotKey(g_main, i, MOD_CONTROL | MOD_ALT | MOD_NOREPEAT, vks[i]);
|
|
if (!on && g_hkOk[i]) { UnregisterHotKey(g_main, i); g_hkOk[i] = FALSE; }
|
|
}
|
|
if (!on) ResetAll();
|
|
}
|
|
BOOL ST_IsEnabled(void) { return g_enabled; }
|
|
|
|
void ST_Init(HINSTANCE inst, BOOL enabled) {
|
|
g_inst = inst;
|
|
HDC sdc = GetDC(NULL); g_dpi = GetDeviceCaps(sdc, LOGPIXELSY); ReleaseDC(NULL, sdc);
|
|
if (g_dpi < 96) g_dpi = 96;
|
|
g_osdFont = CreateFontW(-MulDiv(12, g_dpi, 72), 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI");
|
|
WNDCLASSEXW wc; ZeroMemory(&wc, sizeof(wc));
|
|
wc.cbSize = sizeof(wc); wc.lpfnWndProc = MainProc; wc.hInstance = inst; wc.lpszClassName = L"SeeThruMain";
|
|
RegisterClassExW(&wc);
|
|
WNDCLASSEXW oc; ZeroMemory(&oc, sizeof(oc));
|
|
oc.cbSize = sizeof(oc); oc.lpfnWndProc = OsdProc; oc.hInstance = inst; oc.lpszClassName = L"SeeThruOSD";
|
|
oc.hCursor = LoadCursorW(NULL, IDC_ARROW);
|
|
RegisterClassExW(&oc);
|
|
g_main = CreateWindowExW(0, L"SeeThruMain", APP_NAME, 0, 0, 0, 0, 0, NULL, NULL, inst, NULL);
|
|
g_osd = CreateWindowExW(WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT,
|
|
L"SeeThruOSD", L"", WS_POPUP, 0, 0, 10, 10, NULL, NULL, inst, NULL);
|
|
SetLayeredWindowAttributes(g_osd, 0, 225, LWA_ALPHA);
|
|
SetTimer(g_main, TIMER_CLEAN, 5000, NULL);
|
|
g_enabled = !enabled;
|
|
ST_SetEnabled(enabled);
|
|
}
|
|
|
|
void ST_Shutdown(void) {
|
|
if (g_main) { DestroyWindow(g_main); g_main = NULL; }
|
|
if (g_osd) { DestroyWindow(g_osd); g_osd = NULL; }
|
|
if (g_osdFont) { DeleteObject(g_osdFont); g_osdFont = NULL; }
|
|
}
|
|
|
|
void ST_AppendMenu(HMENU parent, UINT base) {
|
|
HMENU m = CreatePopupMenu();
|
|
AppendMenuW(m, MF_STRING | (g_enabled ? MF_CHECKED : 0), base + DT_ID_ENABLE, L"Turned on");
|
|
AppendMenuW(m, MF_SEPARATOR, 0, NULL);
|
|
int n = 0;
|
|
for (int i = 0; i < MAX_ENT; i++) {
|
|
Ent *e = &g_ent[i];
|
|
if (!e->used || !IsWindow(e->h)) continue;
|
|
WCHAR title[80], item[128];
|
|
GetWindowTextW(e->h, title, 60);
|
|
if (!title[0]) wcscpy(title, L"(untitled)");
|
|
swprintf(item, 128, L"Reset: %ls (%d%%%ls)", title, e->pct, e->ghost ? L", ghost" : L"");
|
|
AppendMenuW(m, MF_STRING, base + IDM_WIN_BASE + i, item);
|
|
n++;
|
|
}
|
|
AppendMenuW(m, MF_STRING | (n ? 0 : MF_GRAYED), base + IDM_RESETALL, L"Reset all windows");
|
|
AppendMenuW(parent, MF_POPUP, (UINT_PTR)m, L"SeeThru");
|
|
}
|
|
|
|
void ST_Command(UINT local) {
|
|
if (local == DT_ID_ENABLE) { ST_SetEnabled(!g_enabled); return; }
|
|
SendMessageW(g_main, WM_COMMAND, local, 0);
|
|
}
|