Add DeskTools source (main, header, DeskIcons, SeeThru, QuickNote)
This commit is contained in:
+261
@@ -0,0 +1,261 @@
|
||||
// DeskTools: DeskIcons, SeeThru, QuickNote and Pin a Window in one small tray program.
|
||||
|
||||
#ifndef UNICODE
|
||||
#define UNICODE
|
||||
#endif
|
||||
#ifndef _UNICODE
|
||||
#define _UNICODE
|
||||
#endif
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define _WIN32_WINNT 0x0A00
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <objbase.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include "desktools.h"
|
||||
|
||||
#define APP_NAME L"DeskTools"
|
||||
#define WM_TRAY (WM_APP + 50)
|
||||
|
||||
#define BASE_DI 10000
|
||||
#define BASE_ST 20000
|
||||
#define BASE_QN 30000
|
||||
#define BASE_PW 40000
|
||||
#define IDM_STARTUP 1
|
||||
#define IDM_HELP 2
|
||||
#define IDM_EXIT 3
|
||||
|
||||
static HINSTANCE g_inst;
|
||||
static HWND g_main;
|
||||
static HHOOK g_hook;
|
||||
static NOTIFYICONDATAW g_nid;
|
||||
static UINT g_taskbarCreated;
|
||||
|
||||
/* ---------------- settings ---------------- */
|
||||
|
||||
static const WCHAR *RUN_KEY = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
||||
|
||||
static BOOL RunEntryExists(const WCHAR *name) {
|
||||
WCHAR buf[MAX_PATH * 2]; DWORD sz = sizeof(buf);
|
||||
return RegGetValueW(HKEY_CURRENT_USER, RUN_KEY, name, RRF_RT_REG_SZ, NULL, buf, &sz) == ERROR_SUCCESS;
|
||||
}
|
||||
static void DeleteRunEntry(const WCHAR *name) {
|
||||
HKEY k;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, RUN_KEY, 0, KEY_WRITE, &k) == ERROR_SUCCESS) {
|
||||
RegDeleteValueW(k, name);
|
||||
RegCloseKey(k);
|
||||
}
|
||||
}
|
||||
static void SetStartup(BOOL on) {
|
||||
if (!on) { DeleteRunEntry(APP_NAME); return; }
|
||||
HKEY k;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, RUN_KEY, 0, KEY_WRITE, &k) != ERROR_SUCCESS) return;
|
||||
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)));
|
||||
RegCloseKey(k);
|
||||
}
|
||||
static DWORD GetSetting(const WCHAR *name, DWORD def) {
|
||||
DWORD v = def, sz = sizeof(v);
|
||||
if (RegGetValueW(HKEY_CURRENT_USER, L"Software\\DeskTools", name, RRF_RT_REG_DWORD, NULL, &v, &sz) != ERROR_SUCCESS)
|
||||
return def;
|
||||
return v;
|
||||
}
|
||||
static void PutSetting(const WCHAR *name, DWORD v) {
|
||||
HKEY k;
|
||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\DeskTools", 0, NULL, 0, KEY_WRITE, NULL, &k, NULL) == ERROR_SUCCESS) {
|
||||
RegSetValueExW(k, name, 0, REG_DWORD, (BYTE *)&v, sizeof(v));
|
||||
RegCloseKey(k);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- tray ---------------- */
|
||||
|
||||
void DT_Balloon(const WCHAR *title, const WCHAR *text) {
|
||||
if (!g_nid.hWnd) return;
|
||||
g_nid.uFlags = NIF_INFO;
|
||||
wcsncpy(g_nid.szInfoTitle, title, 63); g_nid.szInfoTitle[63] = 0;
|
||||
wcsncpy(g_nid.szInfo, text, 255); g_nid.szInfo[255] = 0;
|
||||
g_nid.dwInfoFlags = NIIF_INFO;
|
||||
Shell_NotifyIconW(NIM_MODIFY, &g_nid);
|
||||
}
|
||||
|
||||
static void AddTrayIcon(void) {
|
||||
ZeroMemory(&g_nid, sizeof(g_nid));
|
||||
g_nid.cbSize = sizeof(g_nid);
|
||||
g_nid.hWnd = g_main;
|
||||
g_nid.uID = 1;
|
||||
g_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
g_nid.uCallbackMessage = WM_TRAY;
|
||||
g_nid.hIcon = (HICON)LoadImageW(g_inst, MAKEINTRESOURCEW(1), IMAGE_ICON,
|
||||
GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
|
||||
wcscpy(g_nid.szTip, L"DeskTools");
|
||||
Shell_NotifyIconW(NIM_ADD, &g_nid);
|
||||
}
|
||||
|
||||
static void ShowHelp(void) {
|
||||
MessageBoxW(NULL,
|
||||
L"DESKICONS (send a window to the desktop)\n"
|
||||
L" Shift+click a window's minimize button, or press Ctrl+Alt+I.\n"
|
||||
L" Drag the label anywhere. Double-click it to bring the window back.\n\n"
|
||||
L"SEETHRU (make a window see-through)\n"
|
||||
L" Hold Ctrl+Shift and scroll over a window, down 5% at a time.\n"
|
||||
L" Ctrl+Alt+Page Down / Page Up on the active window.\n"
|
||||
L" Ctrl+Alt+Home: fully solid again. Ctrl+Alt+End: ghost mode\n"
|
||||
L" (stays on top, clicks pass through it).\n\n"
|
||||
L"QUICKNOTE (type on the desktop)\n"
|
||||
L" Double-click empty wallpaper and start typing.\n"
|
||||
L" Drag across text to select it, Ctrl+C to copy.\n"
|
||||
L" Drag the small gray square to move a note. Right-click for color or delete.\n\n"
|
||||
L"PIN A WINDOW (keep a window on top)\n"
|
||||
L" Press Ctrl+Alt+P to pin or unpin the active window,\n"
|
||||
L" or choose Pin a window... from the tray menu and click a window.\n"
|
||||
L" A blue box with a plus sign appears on its title bar.\n"
|
||||
L" Click that box to unpin it.\n\n"
|
||||
L"Each tool can be turned off from its submenu in the tray menu.\n"
|
||||
L"Exiting DeskTools brings every window back to normal.",
|
||||
L"How to use DeskTools", MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
|
||||
static void ShowTrayMenu(void) {
|
||||
HMENU m = CreatePopupMenu();
|
||||
AppendMenuW(m, MF_STRING | MF_GRAYED, 0, L"DeskTools");
|
||||
AppendMenuW(m, MF_SEPARATOR, 0, NULL);
|
||||
DI_AppendMenu(m, BASE_DI);
|
||||
ST_AppendMenu(m, BASE_ST);
|
||||
QN_AppendMenu(m, BASE_QN);
|
||||
PW_AppendMenu(m, BASE_PW);
|
||||
AppendMenuW(m, MF_SEPARATOR, 0, NULL);
|
||||
AppendMenuW(m, MF_STRING | (RunEntryExists(APP_NAME) ? 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 every window back to normal)");
|
||||
POINT p; GetCursorPos(&p);
|
||||
SetForegroundWindow(g_main);
|
||||
UINT cmd = (UINT)TrackPopupMenu(m, TPM_RIGHTBUTTON | TPM_BOTTOMALIGN | TPM_RETURNCMD, p.x, p.y, 0, g_main, NULL);
|
||||
PostMessageW(g_main, WM_NULL, 0, 0);
|
||||
DestroyMenu(m); // also frees the submenus
|
||||
|
||||
if (!cmd) return;
|
||||
if (cmd >= BASE_DI && cmd < BASE_ST) { DI_Command(cmd - BASE_DI); PutSetting(L"DeskIcons", DI_IsEnabled()); }
|
||||
else if (cmd >= BASE_ST && cmd < BASE_QN) { ST_Command(cmd - BASE_ST); PutSetting(L"SeeThru", ST_IsEnabled()); }
|
||||
else if (cmd >= BASE_QN && cmd < BASE_PW) { QN_Command(cmd - BASE_QN); PutSetting(L"QuickNote", QN_IsEnabled()); }
|
||||
else if (cmd >= BASE_PW) { PW_Command(cmd - BASE_PW); PutSetting(L"PinWindow", PW_IsEnabled()); }
|
||||
else if (cmd == IDM_STARTUP) SetStartup(!RunEntryExists(APP_NAME));
|
||||
else if (cmd == IDM_HELP) ShowHelp();
|
||||
else if (cmd == IDM_EXIT) DestroyWindow(g_main);
|
||||
}
|
||||
|
||||
/* ---------------- one mouse hook for every tool ---------------- */
|
||||
|
||||
static LRESULT CALLBACK MouseLL(int code, WPARAM wp, LPARAM lp) {
|
||||
if (code == HC_ACTION) {
|
||||
MSLLHOOKSTRUCT *m = (MSLLHOOKSTRUCT *)lp;
|
||||
if (PW_Mouse(wp, m)) return 1; // picking a window to pin takes priority
|
||||
if (DI_Mouse(wp, m)) return 1;
|
||||
if (ST_Mouse(wp, m)) return 1;
|
||||
if (QN_Mouse(wp, m)) return 1;
|
||||
}
|
||||
return CallNextHookEx(g_hook, code, wp, lp);
|
||||
}
|
||||
|
||||
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_DESTROY:
|
||||
if (g_hook) { UnhookWindowsHookEx(g_hook); g_hook = NULL; }
|
||||
PW_Shutdown();
|
||||
DI_Shutdown();
|
||||
ST_Shutdown();
|
||||
QN_Shutdown();
|
||||
Shell_NotifyIconW(NIM_DELETE, &g_nid);
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcW(h, msg, wp, lp);
|
||||
}
|
||||
|
||||
/* ---------------- start up ---------------- */
|
||||
|
||||
static BOOL OldAppRunning(const WCHAR *mutexName) {
|
||||
HANDLE m = OpenMutexW(SYNCHRONIZE, FALSE, mutexName);
|
||||
if (m) { CloseHandle(m); return TRUE; }
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
typedef BOOL (WINAPI *SetDpiCtxFn)(HANDLE);
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE inst, HINSTANCE prev, PWSTR cmd, int show) {
|
||||
(void)prev; (void)cmd; (void)show;
|
||||
g_inst = inst;
|
||||
|
||||
HANDLE mutex = CreateMutexW(NULL, TRUE, L"DeskTools_SingleInstance_Mutex");
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
MessageBoxW(NULL, L"DeskTools is already running. Look for its icon in the system tray.", APP_NAME, MB_OK | MB_ICONINFORMATION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The separate apps would fight DeskTools over the same shortcuts, so they need to be closed first.
|
||||
WCHAR running[160] = L"";
|
||||
if (OldAppRunning(L"DeskIcons_SingleInstance_Mutex")) wcscat(running, L" DeskIcons\n");
|
||||
if (OldAppRunning(L"SeeThru_SingleInstance_Mutex")) wcscat(running, L" SeeThru\n");
|
||||
if (OldAppRunning(L"QuickNote_SingleInstance_Mutex")) wcscat(running, L" QuickNote\n");
|
||||
if (running[0]) {
|
||||
WCHAR msg[512];
|
||||
swprintf(msg, 512, L"DeskTools already includes these, and they are still running on their own:\n\n%ls\n"
|
||||
L"Right-click each one's icon in the system tray and choose Exit, then start DeskTools again.", running);
|
||||
MessageBoxW(NULL, msg, APP_NAME, MB_OK | MB_ICONINFORMATION);
|
||||
ReleaseMutex(mutex); CloseHandle(mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the separate apps were set to start with Windows, offer to switch that over to DeskTools.
|
||||
BOOL oldRun = RunEntryExists(L"DeskIcons") || RunEntryExists(L"SeeThru") || RunEntryExists(L"QuickNote");
|
||||
if (oldRun && !GetSetting(L"AskedAboutOldStartup", 0)) {
|
||||
PutSetting(L"AskedAboutOldStartup", 1);
|
||||
if (MessageBoxW(NULL, L"Some of the separate apps (DeskIcons, SeeThru, QuickNote) are set to start with Windows.\n\n"
|
||||
L"Turn those off and have DeskTools start with Windows instead?",
|
||||
APP_NAME, MB_YESNO | MB_ICONQUESTION) == IDYES) {
|
||||
DeleteRunEntry(L"DeskIcons"); DeleteRunEntry(L"SeeThru"); DeleteRunEntry(L"QuickNote");
|
||||
SetStartup(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
CoInitialize(NULL);
|
||||
SetDpiCtxFn setCtx = (SetDpiCtxFn)(void *)GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetProcessDpiAwarenessContext");
|
||||
if (!setCtx || !setCtx((HANDLE)-4)) SetProcessDPIAware();
|
||||
|
||||
WNDCLASSEXW wc; ZeroMemory(&wc, sizeof(wc));
|
||||
wc.cbSize = sizeof(wc); wc.lpfnWndProc = MainProc; wc.hInstance = inst; wc.lpszClassName = L"DeskToolsMain";
|
||||
wc.hIcon = LoadIconW(inst, MAKEINTRESOURCEW(1));
|
||||
RegisterClassExW(&wc);
|
||||
g_main = CreateWindowExW(0, L"DeskToolsMain", APP_NAME, 0, 0, 0, 0, 0, NULL, NULL, inst, NULL);
|
||||
g_taskbarCreated = RegisterWindowMessageW(L"TaskbarCreated");
|
||||
AddTrayIcon();
|
||||
|
||||
DI_Init(inst, GetSetting(L"DeskIcons", 1) != 0);
|
||||
ST_Init(inst, GetSetting(L"SeeThru", 1) != 0);
|
||||
QN_Init(inst, GetSetting(L"QuickNote", 1) != 0);
|
||||
PW_Init(inst, GetSetting(L"PinWindow", 1) != 0);
|
||||
g_hook = SetWindowsHookExW(WH_MOUSE_LL, MouseLL, inst, 0);
|
||||
|
||||
if (!GetSetting(L"Welcomed", 0)) {
|
||||
PutSetting(L"Welcomed", 1);
|
||||
DT_Balloon(L"DeskTools is running",
|
||||
L"DeskIcons, SeeThru, QuickNote and Pin a Window are all in this one icon. Right-click it for the menu and help.");
|
||||
}
|
||||
|
||||
MSG m;
|
||||
while (GetMessageW(&m, NULL, 0, 0) > 0) {
|
||||
TranslateMessage(&m);
|
||||
DispatchMessageW(&m);
|
||||
}
|
||||
CoUninitialize();
|
||||
ReleaseMutex(mutex); CloseHandle(mutex);
|
||||
return 0;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Shared declarations for the DeskTools modules.
|
||||
#ifndef DESKTOOLS_H
|
||||
#define DESKTOOLS_H
|
||||
#include <windows.h>
|
||||
|
||||
void DT_Balloon(const WCHAR *title, const WCHAR *text);
|
||||
|
||||
// Each module: init, shutdown, enable/disable, tray submenu, menu command, mouse hook.
|
||||
#define DT_MODULE(P) \
|
||||
void P##_Init(HINSTANCE inst, BOOL enabled); \
|
||||
void P##_Shutdown(void); \
|
||||
void P##_SetEnabled(BOOL on); \
|
||||
BOOL P##_IsEnabled(void); \
|
||||
void P##_AppendMenu(HMENU parent, UINT base); \
|
||||
void P##_Command(UINT local); \
|
||||
int P##_Mouse(WPARAM wp, MSLLHOOKSTRUCT *m);
|
||||
|
||||
DT_MODULE(DI)
|
||||
DT_MODULE(ST)
|
||||
DT_MODULE(QN)
|
||||
DT_MODULE(PW)
|
||||
|
||||
#define DT_ID_ENABLE 9000
|
||||
#endif
|
||||
@@ -0,0 +1,703 @@
|
||||
// DeskIcons: send any window to the desktop as a draggable icon.
|
||||
// Shift+click a minimize button, or press Ctrl+Alt+I, to iconify the window.
|
||||
// Double-click the icon to bring the window back.
|
||||
|
||||
#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 <windowsx.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#define APP_NAME L"DeskIcons"
|
||||
#define WM_TRAY (WM_APP + 1)
|
||||
#define WM_ICONIFY (WM_APP + 2)
|
||||
#define WM_RESTOREIDX (WM_APP + 3)
|
||||
#define WM_CLOSEIDX (WM_APP + 4)
|
||||
#define HOTKEY_ID 1
|
||||
#define MAX_ITEMS 256
|
||||
#define TIMER_CHECK 1
|
||||
|
||||
#define IDM_RESTOREALL 100
|
||||
#define IDM_ONTOP 101
|
||||
#define IDM_STARTUP 102
|
||||
#define IDM_HELP 103
|
||||
#define IDM_EXIT 104
|
||||
#define IDM_SHOWICONS 105
|
||||
#define IDM_I_RESTORE 200
|
||||
#define IDM_I_CLOSE 201
|
||||
|
||||
typedef struct {
|
||||
BOOL used;
|
||||
HWND target;
|
||||
HWND iconWnd;
|
||||
DWORD pid;
|
||||
HICON hIcon;
|
||||
BOOL hover;
|
||||
WCHAR title[256];
|
||||
} Item;
|
||||
|
||||
static Item g_items[MAX_ITEMS];
|
||||
static BOOL g_enabled = TRUE;
|
||||
static HINSTANCE g_inst;
|
||||
static HWND g_main;
|
||||
static HHOOK g_mouseHook;
|
||||
static volatile BOOL g_swallowUp;
|
||||
static BOOL g_onTop;
|
||||
static BOOL g_showIcons;
|
||||
static BOOL g_hotkeyOk;
|
||||
static NOTIFYICONDATAW g_nid;
|
||||
static HFONT g_font;
|
||||
static int g_dpi = 96;
|
||||
static UINT g_taskbarCreated;
|
||||
static WCHAR g_stateFile[MAX_PATH];
|
||||
static HWND g_lastFg;
|
||||
|
||||
// drag state
|
||||
static BOOL g_pressed, g_dragging;
|
||||
static POINT g_dragStart, g_dragOrig;
|
||||
|
||||
static int S(int v) { return MulDiv(v, g_dpi, 96); }
|
||||
static int ItemW(void) { return g_showIcons ? S(88) : S(120); }
|
||||
static int ItemH(void) { return g_showIcons ? S(96) : S(58); }
|
||||
|
||||
/* ---------------- settings ---------------- */
|
||||
|
||||
static DWORD RegGetDword(const WCHAR *name, DWORD def) {
|
||||
DWORD v = def, sz = sizeof(v);
|
||||
if (RegGetValueW(HKEY_CURRENT_USER, L"Software\\DeskIcons", name, RRF_RT_REG_DWORD, NULL, &v, &sz) != ERROR_SUCCESS)
|
||||
return def;
|
||||
return v;
|
||||
}
|
||||
static void RegSetDword(const WCHAR *name, DWORD v) {
|
||||
HKEY k;
|
||||
if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\DeskIcons", 0, NULL, 0, KEY_WRITE, NULL, &k, NULL) == ERROR_SUCCESS) {
|
||||
RegSetValueExW(k, name, 0, REG_DWORD, (BYTE *)&v, sizeof(v));
|
||||
RegCloseKey(k);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/* ---------------- state file (crash recovery) ---------------- */
|
||||
|
||||
static void SaveState(void) {
|
||||
FILE *f = _wfopen(g_stateFile, L"w");
|
||||
if (!f) return;
|
||||
for (int i = 0; i < MAX_ITEMS; i++) {
|
||||
Item *it = &g_items[i];
|
||||
if (!it->used) continue;
|
||||
RECT r; GetWindowRect(it->iconWnd, &r);
|
||||
fprintf(f, "%llx %lu %ld %ld\n", (unsigned long long)(ULONG_PTR)it->target,
|
||||
(unsigned long)it->pid, r.left, r.top);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* ---------------- helpers ---------------- */
|
||||
|
||||
static int CountItems(void) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < MAX_ITEMS; i++) if (g_items[i].used) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void UpdateTrayTip(void) { }
|
||||
|
||||
static void Balloon(const WCHAR *title, const WCHAR *text) { DT_Balloon(title, text); }
|
||||
|
||||
static Item *FindItemByTarget(HWND t) {
|
||||
for (int i = 0; i < MAX_ITEMS; i++) if (g_items[i].used && g_items[i].target == t) return &g_items[i];
|
||||
return NULL;
|
||||
}
|
||||
static BOOL IsOurIconWindow(HWND h) {
|
||||
for (int i = 0; i < MAX_ITEMS; i++) if (g_items[i].used && g_items[i].iconWnd == h) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static HWND FindDesktopHost(void) {
|
||||
HWND p = FindWindowW(L"Progman", NULL);
|
||||
if (p && FindWindowExW(p, NULL, L"SHELLDLL_DefView", NULL)) return p;
|
||||
HWND w = NULL;
|
||||
while ((w = FindWindowExW(NULL, w, L"WorkerW", NULL)) != NULL)
|
||||
if (FindWindowExW(w, NULL, L"SHELLDLL_DefView", NULL)) return w;
|
||||
return p;
|
||||
}
|
||||
|
||||
static BOOL IsDesktopClass(HWND h) {
|
||||
WCHAR cls[64];
|
||||
if (!h || !GetClassNameW(h, cls, 64)) return FALSE;
|
||||
return !wcscmp(cls, L"Progman") || !wcscmp(cls, L"WorkerW");
|
||||
}
|
||||
|
||||
static BOOL CanIconify(HWND h) {
|
||||
if (!h || !IsWindow(h) || !IsWindowVisible(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") || !wcscmp(cls, L"Shell_TrayWnd") ||
|
||||
!wcscmp(cls, L"Shell_SecondaryTrayWnd") || !wcscmp(cls, L"Windows.UI.Core.CoreWindow"))
|
||||
return FALSE;
|
||||
if (GetWindowLongW(h, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) return FALSE;
|
||||
if (FindItemByTarget(h)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static HICON GetTargetIcon(HWND h) {
|
||||
int sz = S(48);
|
||||
HICON hi = NULL;
|
||||
DWORD pid = 0; GetWindowThreadProcessId(h, &pid);
|
||||
HANDLE p = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
if (p) {
|
||||
WCHAR path[MAX_PATH]; DWORD n = MAX_PATH;
|
||||
if (QueryFullProcessImageNameW(p, 0, path, &n)) {
|
||||
const WCHAR *base = wcsrchr(path, L'\\'); base = base ? base + 1 : path;
|
||||
if (_wcsicmp(base, L"ApplicationFrameHost.exe") != 0) {
|
||||
HICON large = NULL;
|
||||
if (SHDefExtractIconW(path, 0, 0, &large, NULL, MAKELONG(sz, 16)) == S_OK && large)
|
||||
hi = large;
|
||||
}
|
||||
}
|
||||
CloseHandle(p);
|
||||
}
|
||||
if (!hi) {
|
||||
DWORD_PTR r = 0;
|
||||
if (SendMessageTimeoutW(h, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 200, &r) && r) hi = CopyIcon((HICON)r);
|
||||
if (!hi) { HICON c = (HICON)GetClassLongPtrW(h, GCLP_HICON); if (c) hi = CopyIcon(c); }
|
||||
if (!hi && SendMessageTimeoutW(h, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 200, &r) && r) hi = CopyIcon((HICON)r);
|
||||
if (!hi) hi = CopyIcon(LoadIconW(NULL, IDI_APPLICATION));
|
||||
}
|
||||
return hi;
|
||||
}
|
||||
|
||||
/* ---------------- rendering ---------------- */
|
||||
|
||||
static void DrawContent(HDC dc, Item *it, int W, int H) {
|
||||
SelectObject(dc, g_font);
|
||||
SetBkMode(dc, TRANSPARENT);
|
||||
UINT fmt = DT_CENTER | DT_WORDBREAK | DT_END_ELLIPSIS | DT_EDITCONTROL | DT_NOPREFIX;
|
||||
WCHAR title[256];
|
||||
wcscpy(title, it->title[0] ? it->title : L"(untitled)");
|
||||
RECT tr;
|
||||
if (g_showIcons) {
|
||||
int isz = S(48);
|
||||
int ix = (W - isz) / 2, iy = S(6);
|
||||
DrawIconEx(dc, ix, iy, it->hIcon, isz, isz, 0, NULL, DI_NORMAL);
|
||||
SetRect(&tr, S(3), iy + isz + S(4), W - S(3), H - S(3));
|
||||
} else {
|
||||
// text only: center the (up to 3 line) label vertically in the tile
|
||||
RECT box = { S(5), S(4), W - S(5), H - S(4) };
|
||||
RECT calc = box;
|
||||
DrawTextW(dc, title, -1, &calc, fmt | DT_CALCRECT);
|
||||
int th = calc.bottom - calc.top, bh = box.bottom - box.top;
|
||||
if (th > bh) th = bh;
|
||||
tr = box;
|
||||
tr.top = box.top + (bh - th) / 2;
|
||||
tr.bottom = tr.top + th;
|
||||
}
|
||||
RECT sr = tr; OffsetRect(&sr, 1, 1);
|
||||
SetTextColor(dc, RGB(0, 0, 0));
|
||||
DrawTextW(dc, title, -1, &sr, fmt);
|
||||
SetTextColor(dc, RGB(255, 255, 255));
|
||||
DrawTextW(dc, title, -1, &tr, fmt);
|
||||
}
|
||||
|
||||
static void RenderItem(Item *it) {
|
||||
int W = ItemW(), H = ItemH();
|
||||
HDC screen = GetDC(NULL);
|
||||
BITMAPINFO bi; ZeroMemory(&bi, sizeof(bi));
|
||||
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bi.bmiHeader.biWidth = W; bi.bmiHeader.biHeight = -H;
|
||||
bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
BYTE *pb = NULL, *pw = NULL, *po = NULL;
|
||||
HDC db = CreateCompatibleDC(screen), dw = CreateCompatibleDC(screen), dout = CreateCompatibleDC(screen);
|
||||
HBITMAP bb = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, (void **)&pb, NULL, 0);
|
||||
HBITMAP bw = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, (void **)&pw, NULL, 0);
|
||||
HBITMAP bo = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, (void **)&po, NULL, 0);
|
||||
if (!bb || !bw || !bo) goto done;
|
||||
HGDIOBJ ob = SelectObject(db, bb), ow = SelectObject(dw, bw), oo = SelectObject(dout, bo);
|
||||
|
||||
RECT all = { 0, 0, W, H };
|
||||
FillRect(db, &all, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
||||
FillRect(dw, &all, (HBRUSH)GetStockObject(WHITE_BRUSH));
|
||||
DrawContent(db, it, W, H);
|
||||
DrawContent(dw, it, W, H);
|
||||
GdiFlush();
|
||||
|
||||
// Recover per-pixel alpha by comparing the render over black and over white.
|
||||
int hx0 = S(2), hy0 = S(2), hx1 = W - S(2), hy1 = H - S(2);
|
||||
for (int y = 0; y < H; y++) {
|
||||
for (int x = 0; x < W; x++) {
|
||||
int i = (y * W + x) * 4;
|
||||
int d = ((pw[i] - pb[i]) + (pw[i + 1] - pb[i + 1]) + (pw[i + 2] - pb[i + 2])) / 3;
|
||||
int a = 255 - d; if (a < 0) a = 0; if (a > 255) a = 255;
|
||||
int c0 = pb[i], c1 = pb[i + 1], c2 = pb[i + 2];
|
||||
if (c0 > a) c0 = a; if (c1 > a) c1 = a; if (c2 > a) c2 = a;
|
||||
|
||||
if (it->hover && x >= hx0 && x < hx1 && y >= hy0 && y < hy1) {
|
||||
BOOL edge = (x == hx0 || x == hx1 - 1 || y == hy0 || y == hy1 - 1);
|
||||
int ha = edge ? 150 : 70; // highlight alpha
|
||||
int hr = 153, hg = 209, hb = 255; // light blue, like Explorer selection
|
||||
int inv = 255 - a;
|
||||
c0 += (hb * ha / 255) * inv / 255;
|
||||
c1 += (hg * ha / 255) * inv / 255;
|
||||
c2 += (hr * ha / 255) * inv / 255;
|
||||
a += ha * inv / 255;
|
||||
}
|
||||
if (a < 1) a = 1; // keep the whole tile clickable
|
||||
po[i] = (BYTE)c0; po[i + 1] = (BYTE)c1; po[i + 2] = (BYTE)c2; po[i + 3] = (BYTE)a;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
SIZE sz = { W, H }; POINT src = { 0, 0 };
|
||||
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
|
||||
UpdateLayeredWindow(it->iconWnd, screen, NULL, &sz, dout, &src, 0, &bf, ULW_ALPHA);
|
||||
}
|
||||
SelectObject(db, ob); SelectObject(dw, ow); SelectObject(dout, oo);
|
||||
done:
|
||||
if (bb) DeleteObject(bb);
|
||||
if (bw) DeleteObject(bw);
|
||||
if (bo) DeleteObject(bo);
|
||||
DeleteDC(db); DeleteDC(dw); DeleteDC(dout);
|
||||
ReleaseDC(NULL, screen);
|
||||
}
|
||||
|
||||
/* ---------------- placement and z-order ---------------- */
|
||||
|
||||
static void ApplyZOrder(HWND h) {
|
||||
if (g_onTop) SetWindowPos(h, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
else {
|
||||
SetWindowPos(h, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
SetWindowPos(h, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); // adjusted in WM_WINDOWPOSCHANGING
|
||||
}
|
||||
}
|
||||
|
||||
static POINT FindFreeSlot(void) {
|
||||
RECT wa; SystemParametersInfoW(SPI_GETWORKAREA, 0, &wa, 0);
|
||||
int W = ItemW(), H = ItemH(), gap = S(4), m = S(10);
|
||||
int rows = (wa.bottom - wa.top - m) / (H + gap); if (rows < 1) rows = 1;
|
||||
int cols = (wa.right - wa.left - m) / (W + gap); if (cols < 1) cols = 1;
|
||||
for (int c = 0; c < cols; c++) {
|
||||
for (int r = 0; r < rows; r++) {
|
||||
RECT slot;
|
||||
slot.left = wa.right - m - (c + 1) * W - c * gap;
|
||||
slot.top = wa.top + m + r * (H + gap);
|
||||
slot.right = slot.left + W; slot.bottom = slot.top + H;
|
||||
BOOL free = TRUE;
|
||||
for (int i = 0; i < MAX_ITEMS && free; i++) {
|
||||
if (!g_items[i].used) continue;
|
||||
RECT ir, x; GetWindowRect(g_items[i].iconWnd, &ir);
|
||||
if (IntersectRect(&x, &ir, &slot)) free = FALSE;
|
||||
}
|
||||
if (free) { POINT p = { slot.left, slot.top }; return p; }
|
||||
}
|
||||
}
|
||||
POINT p = { wa.right - m - W, wa.top + m };
|
||||
return p;
|
||||
}
|
||||
|
||||
/* ---------------- items ---------------- */
|
||||
|
||||
static Item *CreateItem(HWND target, int x, int y, BOOL useXY) {
|
||||
Item *it = NULL;
|
||||
for (int i = 0; i < MAX_ITEMS; i++) if (!g_items[i].used) { it = &g_items[i]; break; }
|
||||
if (!it) return NULL;
|
||||
ZeroMemory(it, sizeof(*it));
|
||||
it->target = target;
|
||||
GetWindowThreadProcessId(target, &it->pid);
|
||||
GetWindowTextW(target, it->title, 256);
|
||||
it->hIcon = GetTargetIcon(target);
|
||||
if (!useXY) { POINT p = FindFreeSlot(); x = p.x; y = p.y; }
|
||||
it->iconWnd = CreateWindowExW(WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE,
|
||||
L"DeskIconsItem", it->title, WS_POPUP,
|
||||
x, y, ItemW(), ItemH(), NULL, NULL, g_inst, it);
|
||||
if (!it->iconWnd) { if (it->hIcon) DestroyIcon(it->hIcon); return NULL; }
|
||||
it->used = TRUE;
|
||||
RenderItem(it);
|
||||
ShowWindow(it->iconWnd, SW_SHOWNOACTIVATE);
|
||||
ApplyZOrder(it->iconWnd);
|
||||
return it;
|
||||
}
|
||||
|
||||
static void RemoveItem(Item *it) {
|
||||
if (!it->used) return;
|
||||
it->used = FALSE;
|
||||
if (it->iconWnd) DestroyWindow(it->iconWnd);
|
||||
if (it->hIcon) DestroyIcon(it->hIcon);
|
||||
it->iconWnd = NULL; it->hIcon = NULL;
|
||||
SaveState();
|
||||
UpdateTrayTip();
|
||||
}
|
||||
|
||||
static void Iconify(HWND h) {
|
||||
h = GetAncestor(h, GA_ROOT);
|
||||
if (!CanIconify(h)) return;
|
||||
ShowWindow(h, SW_MINIMIZE);
|
||||
ShowWindow(h, SW_HIDE);
|
||||
if (IsWindowVisible(h)) {
|
||||
Balloon(L"DeskIcons", L"That window could not be hidden. It probably belongs to a program running as administrator.");
|
||||
return;
|
||||
}
|
||||
CreateItem(h, 0, 0, FALSE);
|
||||
SaveState();
|
||||
UpdateTrayTip();
|
||||
}
|
||||
|
||||
static void RestoreItem(Item *it, BOOL activate) {
|
||||
if (!it->used) return;
|
||||
HWND t = it->target;
|
||||
if (IsWindow(t)) {
|
||||
if (activate) {
|
||||
ShowWindow(t, SW_SHOWMINNOACTIVE);
|
||||
ShowWindow(t, SW_RESTORE);
|
||||
SetForegroundWindow(t);
|
||||
} else {
|
||||
ShowWindow(t, SW_SHOWMINNOACTIVE); // back on the taskbar, still minimized
|
||||
}
|
||||
}
|
||||
RemoveItem(it);
|
||||
}
|
||||
|
||||
static void RestoreAll(BOOL activate) {
|
||||
for (int i = 0; i < MAX_ITEMS; i++) if (g_items[i].used) RestoreItem(&g_items[i], activate);
|
||||
}
|
||||
|
||||
static void RecoverFromStateFile(void) {
|
||||
FILE *f = _wfopen(g_stateFile, L"r");
|
||||
if (!f) return;
|
||||
unsigned long long h; unsigned long pid; long x, y;
|
||||
while (fscanf(f, "%llx %lu %ld %ld", &h, &pid, &x, &y) == 4) {
|
||||
HWND hw = (HWND)(ULONG_PTR)h;
|
||||
DWORD realPid = 0;
|
||||
if (!IsWindow(hw)) continue;
|
||||
GetWindowThreadProcessId(hw, &realPid);
|
||||
if (realPid != pid || IsWindowVisible(hw) || FindItemByTarget(hw)) continue;
|
||||
CreateItem(hw, x, y, TRUE);
|
||||
}
|
||||
fclose(f);
|
||||
SaveState();
|
||||
}
|
||||
|
||||
/* ---------------- menus ---------------- */
|
||||
|
||||
static void ShowTrayMenu(void) {
|
||||
HMENU m = CreatePopupMenu();
|
||||
AppendMenuW(m, MF_STRING | MF_GRAYED, 0, L"DeskIcons");
|
||||
AppendMenuW(m, MF_SEPARATOR, 0, NULL);
|
||||
AppendMenuW(m, MF_STRING | (CountItems() ? 0 : MF_GRAYED), IDM_RESTOREALL, L"Restore all windows");
|
||||
AppendMenuW(m, MF_STRING | (g_showIcons ? MF_CHECKED : 0), IDM_SHOWICONS, L"Show program icons");
|
||||
AppendMenuW(m, MF_STRING | (g_onTop ? MF_CHECKED : 0), IDM_ONTOP, L"Keep icons on top of 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 (brings windows back)");
|
||||
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);
|
||||
}
|
||||
|
||||
static void ShowHelp(void) {
|
||||
MessageBoxW(NULL,
|
||||
L"Send a window to the desktop:\n"
|
||||
L" Hold Shift and click the window's minimize button,\n"
|
||||
L" or press Ctrl+Alt+I while the window is active.\n\n"
|
||||
L"The window leaves the taskbar and shows up as an icon on your desktop.\n\n"
|
||||
L"Drag icons anywhere you like.\n"
|
||||
L"Double-click an icon to bring its window back.\n"
|
||||
L"Right-click an icon to restore or close that window.\n\n"
|
||||
L"Exiting DeskIcons brings back every window it was holding.",
|
||||
L"How to use DeskIcons", MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
|
||||
/* ---------------- window procs ---------------- */
|
||||
|
||||
static LRESULT CALLBACK ItemProc(HWND h, UINT msg, WPARAM wp, LPARAM lp) {
|
||||
Item *it = (Item *)GetWindowLongPtrW(h, GWLP_USERDATA);
|
||||
switch (msg) {
|
||||
case WM_NCCREATE: {
|
||||
CREATESTRUCTW *cs = (CREATESTRUCTW *)lp;
|
||||
SetWindowLongPtrW(h, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams);
|
||||
break;
|
||||
}
|
||||
case WM_MOUSEACTIVATE:
|
||||
return MA_NOACTIVATE;
|
||||
case WM_WINDOWPOSCHANGING:
|
||||
if (!g_onTop) {
|
||||
WINDOWPOS *pos = (WINDOWPOS *)lp;
|
||||
if (!(pos->flags & SWP_NOZORDER)) {
|
||||
HWND host = FindDesktopHost();
|
||||
if (host) {
|
||||
HWND above = GetWindow(host, GW_HWNDPREV);
|
||||
if (above == h) pos->flags |= SWP_NOZORDER;
|
||||
else pos->hwndInsertAfter = above ? above : HWND_TOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_LBUTTONDOWN: {
|
||||
RECT r; GetWindowRect(h, &r);
|
||||
g_dragOrig.x = r.left; g_dragOrig.y = r.top;
|
||||
GetCursorPos(&g_dragStart);
|
||||
g_pressed = TRUE; g_dragging = FALSE;
|
||||
SetCapture(h);
|
||||
return 0;
|
||||
}
|
||||
case WM_MOUSEMOVE: {
|
||||
if (it && !it->hover) {
|
||||
it->hover = TRUE;
|
||||
TRACKMOUSEEVENT t = { sizeof(t), TME_LEAVE, h, 0 };
|
||||
TrackMouseEvent(&t);
|
||||
RenderItem(it);
|
||||
}
|
||||
if (g_pressed && GetCapture() == h) {
|
||||
POINT p; GetCursorPos(&p);
|
||||
int dx = p.x - g_dragStart.x, dy = p.y - g_dragStart.y;
|
||||
if (!g_dragging && (abs(dx) > GetSystemMetrics(SM_CXDRAG) || abs(dy) > GetSystemMetrics(SM_CYDRAG)))
|
||||
g_dragging = TRUE;
|
||||
if (g_dragging)
|
||||
SetWindowPos(h, NULL, g_dragOrig.x + dx, g_dragOrig.y + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_LBUTTONUP: {
|
||||
BOOL wasDrag = g_dragging;
|
||||
g_pressed = FALSE; g_dragging = FALSE;
|
||||
if (GetCapture() == h) ReleaseCapture();
|
||||
if (wasDrag) SaveState();
|
||||
return 0;
|
||||
}
|
||||
case WM_CAPTURECHANGED:
|
||||
g_pressed = FALSE; g_dragging = FALSE;
|
||||
return 0;
|
||||
case WM_LBUTTONDBLCLK:
|
||||
if (it) PostMessageW(g_main, WM_RESTOREIDX, (WPARAM)(it - g_items), 0);
|
||||
return 0;
|
||||
case WM_MOUSELEAVE:
|
||||
if (it && it->hover) { it->hover = FALSE; RenderItem(it); }
|
||||
return 0;
|
||||
case WM_RBUTTONUP: {
|
||||
if (!it) return 0;
|
||||
HMENU m = CreatePopupMenu();
|
||||
AppendMenuW(m, MF_STRING | MF_DEFAULT, IDM_I_RESTORE, L"Restore");
|
||||
AppendMenuW(m, MF_STRING, IDM_I_CLOSE, L"Close window");
|
||||
POINT p; GetCursorPos(&p);
|
||||
SetForegroundWindow(g_main);
|
||||
int cmd = TrackPopupMenu(m, TPM_RIGHTBUTTON | TPM_RETURNCMD, p.x, p.y, 0, g_main, NULL);
|
||||
PostMessageW(g_main, WM_NULL, 0, 0);
|
||||
DestroyMenu(m);
|
||||
if (cmd == IDM_I_RESTORE) PostMessageW(g_main, WM_RESTOREIDX, (WPARAM)(it - g_items), 0);
|
||||
else if (cmd == IDM_I_CLOSE) PostMessageW(g_main, WM_CLOSEIDX, (WPARAM)(it - g_items), 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return DefWindowProcW(h, msg, wp, lp);
|
||||
}
|
||||
|
||||
static void AddTrayIcon(void) { }
|
||||
|
||||
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_HOTKEY:
|
||||
if (wp == HOTKEY_ID) Iconify(GetForegroundWindow());
|
||||
return 0;
|
||||
case WM_ICONIFY:
|
||||
Iconify((HWND)wp);
|
||||
return 0;
|
||||
case WM_RESTOREIDX:
|
||||
if (wp < MAX_ITEMS) RestoreItem(&g_items[wp], TRUE);
|
||||
return 0;
|
||||
case WM_CLOSEIDX:
|
||||
if (wp < MAX_ITEMS && g_items[wp].used) {
|
||||
HWND t = g_items[wp].target;
|
||||
RestoreItem(&g_items[wp], TRUE);
|
||||
if (IsWindow(t)) PostMessageW(t, WM_SYSCOMMAND, SC_CLOSE, 0);
|
||||
}
|
||||
return 0;
|
||||
case WM_TIMER:
|
||||
if (wp == TIMER_CHECK) {
|
||||
for (int i = 0; i < MAX_ITEMS; i++) {
|
||||
Item *it = &g_items[i];
|
||||
if (!it->used) continue;
|
||||
if (!IsWindow(it->target) || IsWindowVisible(it->target)) { RemoveItem(it); continue; }
|
||||
WCHAR t[256]; GetWindowTextW(it->target, t, 256);
|
||||
if (wcscmp(t, it->title)) { wcscpy(it->title, t); RenderItem(it); }
|
||||
if (IsIconic(it->iconWnd) || !IsWindowVisible(it->iconWnd)) ShowWindow(it->iconWnd, SW_SHOWNOACTIVATE);
|
||||
}
|
||||
// When the desktop gets focus (Win+D, clicking the wallpaper), make sure icons sit above it.
|
||||
HWND fg = GetForegroundWindow();
|
||||
if (fg != g_lastFg) {
|
||||
g_lastFg = fg;
|
||||
if (IsDesktopClass(fg))
|
||||
for (int i = 0; i < MAX_ITEMS; i++) if (g_items[i].used) ApplyZOrder(g_items[i].iconWnd);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wp)) {
|
||||
case IDM_RESTOREALL: RestoreAll(FALSE); break;
|
||||
case IDM_ONTOP:
|
||||
g_onTop = !g_onTop; RegSetDword(L"OnTop", g_onTop);
|
||||
for (int i = 0; i < MAX_ITEMS; i++) if (g_items[i].used) ApplyZOrder(g_items[i].iconWnd);
|
||||
break;
|
||||
case IDM_SHOWICONS:
|
||||
g_showIcons = !g_showIcons; RegSetDword(L"ShowIcons", g_showIcons);
|
||||
for (int i = 0; i < MAX_ITEMS; i++) {
|
||||
if (!g_items[i].used) continue;
|
||||
SetWindowPos(g_items[i].iconWnd, NULL, 0, 0, ItemW(), ItemH(), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
RenderItem(&g_items[i]);
|
||||
}
|
||||
SaveState();
|
||||
break;
|
||||
case IDM_STARTUP: SetStartup(!StartupEnabled()); break;
|
||||
case IDM_HELP: ShowHelp(); break;
|
||||
case IDM_EXIT: DestroyWindow(h); break;
|
||||
}
|
||||
return 0;
|
||||
case WM_QUERYENDSESSION:
|
||||
RestoreAll(FALSE);
|
||||
return TRUE;
|
||||
case WM_DESTROY:
|
||||
RestoreAll(FALSE);
|
||||
DeleteFileW(g_stateFile);
|
||||
KillTimer(h, TIMER_CHECK);
|
||||
if (g_hotkeyOk) UnregisterHotKey(h, HOTKEY_ID);
|
||||
if (g_mouseHook) UnhookWindowsHookEx(g_mouseHook);
|
||||
Shell_NotifyIconW(NIM_DELETE, &g_nid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcW(h, msg, wp, lp);
|
||||
}
|
||||
|
||||
/* ---------------- shift+click on minimize button ---------------- */
|
||||
|
||||
int DI_Mouse(WPARAM wp, MSLLHOOKSTRUCT *m) {
|
||||
if (!g_enabled) { g_swallowUp = FALSE; return 0; }
|
||||
if (wp == WM_LBUTTONDOWN && (GetAsyncKeyState(VK_SHIFT) & 0x8000) && !(m->flags & LLMHF_INJECTED)) {
|
||||
HWND hit = WindowFromPoint(m->pt);
|
||||
if (hit) {
|
||||
HWND root = GetAncestor(hit, GA_ROOT);
|
||||
LPARAM pt = MAKELPARAM((WORD)(SHORT)m->pt.x, (WORD)(SHORT)m->pt.y);
|
||||
DWORD_PTR r = 0;
|
||||
BOOL isMin = FALSE;
|
||||
if (SendMessageTimeoutW(hit, WM_NCHITTEST, 0, pt, SMTO_ABORTIFHUNG, 150, &r) && r == HTMINBUTTON) isMin = TRUE;
|
||||
else if (root && root != hit &&
|
||||
SendMessageTimeoutW(root, WM_NCHITTEST, 0, pt, SMTO_ABORTIFHUNG, 150, &r) && r == HTMINBUTTON) isMin = TRUE;
|
||||
if (isMin && root && !IsOurIconWindow(root)) {
|
||||
g_swallowUp = TRUE;
|
||||
PostMessageW(g_main, WM_ICONIFY, (WPARAM)root, 0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else if (wp == WM_LBUTTONUP && g_swallowUp) {
|
||||
g_swallowUp = FALSE;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------- entry ---------------- */
|
||||
|
||||
/* ---------------- DeskTools module interface ---------------- */
|
||||
|
||||
void DI_SetEnabled(BOOL on) {
|
||||
g_enabled = on;
|
||||
if (on) {
|
||||
if (!g_hotkeyOk) g_hotkeyOk = RegisterHotKey(g_main, HOTKEY_ID, MOD_CONTROL | MOD_ALT | MOD_NOREPEAT, 'I');
|
||||
} else {
|
||||
RestoreAll(FALSE);
|
||||
if (g_hotkeyOk) { UnregisterHotKey(g_main, HOTKEY_ID); g_hotkeyOk = FALSE; }
|
||||
}
|
||||
}
|
||||
BOOL DI_IsEnabled(void) { return g_enabled; }
|
||||
|
||||
void DI_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_font = CreateFontW(-MulDiv(9, g_dpi, 72), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
||||
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, L"Segoe UI");
|
||||
WCHAR dir[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, dir))) {
|
||||
wcscat(dir, L"\\DeskIcons");
|
||||
CreateDirectoryW(dir, NULL);
|
||||
swprintf(g_stateFile, MAX_PATH, L"%ls\\state.txt", dir);
|
||||
} else {
|
||||
GetTempPathW(MAX_PATH, dir);
|
||||
swprintf(g_stateFile, MAX_PATH, L"%lsDeskIcons_state.txt", dir);
|
||||
}
|
||||
g_onTop = RegGetDword(L"OnTop", 0) != 0;
|
||||
g_showIcons = RegGetDword(L"ShowIcons", 0) != 0;
|
||||
|
||||
WNDCLASSEXW wc; ZeroMemory(&wc, sizeof(wc));
|
||||
wc.cbSize = sizeof(wc); wc.lpfnWndProc = MainProc; wc.hInstance = inst; wc.lpszClassName = L"DeskIconsMain";
|
||||
RegisterClassExW(&wc);
|
||||
WNDCLASSEXW ic; ZeroMemory(&ic, sizeof(ic));
|
||||
ic.cbSize = sizeof(ic); ic.style = CS_DBLCLKS; ic.lpfnWndProc = ItemProc; ic.hInstance = inst;
|
||||
ic.hCursor = LoadCursorW(NULL, IDC_ARROW); ic.lpszClassName = L"DeskIconsItem";
|
||||
RegisterClassExW(&ic);
|
||||
|
||||
g_main = CreateWindowExW(0, L"DeskIconsMain", APP_NAME, 0, 0, 0, 0, 0, NULL, NULL, inst, NULL);
|
||||
SetTimer(g_main, TIMER_CHECK, 1000, NULL);
|
||||
RecoverFromStateFile();
|
||||
g_enabled = !enabled;
|
||||
DI_SetEnabled(enabled);
|
||||
}
|
||||
|
||||
void DI_Shutdown(void) {
|
||||
if (g_main) { DestroyWindow(g_main); g_main = NULL; }
|
||||
if (g_font) { DeleteObject(g_font); g_font = NULL; }
|
||||
}
|
||||
|
||||
void DI_AppendMenu(HMENU parent, UINT base) {
|
||||
HMENU m = CreatePopupMenu();
|
||||
UINT dis = g_enabled ? 0 : MF_GRAYED;
|
||||
int n = CountItems();
|
||||
AppendMenuW(m, MF_STRING | (g_enabled ? MF_CHECKED : 0), base + DT_ID_ENABLE, L"Turned on");
|
||||
AppendMenuW(m, MF_SEPARATOR, 0, NULL);
|
||||
AppendMenuW(m, MF_STRING | ((n && g_enabled) ? 0 : MF_GRAYED), base + IDM_RESTOREALL, L"Restore all windows");
|
||||
AppendMenuW(m, MF_STRING | dis | (g_showIcons ? MF_CHECKED : 0), base + IDM_SHOWICONS, L"Show program icons");
|
||||
AppendMenuW(m, MF_STRING | dis | (g_onTop ? MF_CHECKED : 0), base + IDM_ONTOP, L"Keep labels on top of windows");
|
||||
WCHAR label[64];
|
||||
if (n) swprintf(label, 64, L"DeskIcons (%d on desktop)", n); else wcscpy(label, L"DeskIcons");
|
||||
AppendMenuW(parent, MF_POPUP, (UINT_PTR)m, label);
|
||||
}
|
||||
|
||||
void DI_Command(UINT local) {
|
||||
if (local == DT_ID_ENABLE) { DI_SetEnabled(!g_enabled); return; }
|
||||
SendMessageW(g_main, WM_COMMAND, local, 0);
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user