Files

262 lines
11 KiB
C

// 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;
}