// Tray Color Picker - single-file Win32 app #define UNICODE #define _UNICODE #include #include #include #include #define WM_TRAYICON (WM_APP + 1) #define ID_TRAY 1 #define IDM_PICK 100 #define IDM_EXIT 101 #define IDM_CLEARHIST 102 #define IDM_HISTORY0 200 // 200..219 #define HISTORY_CAP 20 // absolute max storage // settings menu ids #define IDM_ZOOM_BASE 300 #define IDM_CELL_BASE 320 #define IDM_INFO_HEX 340 #define IDM_INFO_RGB 341 #define IDM_INFO_BOTH 342 #define IDM_GRID 350 #define IDM_COPY_HEX 360 // "#RRGGBB" #define IDM_COPY_RGB 361 // "rgb(r, g, b)" #define IDM_COPY_RAW 362 // "RRGGBB" #define IDM_COPY_NONE 363 // don't copy #define IDM_HSIZE_BASE 380 // + index into HSIZES static const int ZOOMS[] = { 4, 8, 12, 16 }; static const int CELLS[] = { 11, 15, 21, 27 }; static const int HSIZES[] = { 0, 5, 10, 20 }; #define NZOOMS 4 #define NCELLS 4 #define NHSIZES 4 // ---- settings (persisted) ---- static int g_zoom = 8; static int g_cells = 15; static int g_infoMode = 2; // 0=hex, 1=rgb, 2=both static BOOL g_grid = FALSE; static int g_copyFmt = 0; // 0=#RRGGBB, 1=rgb(), 2=RRGGBB, 3=none static int g_histSize = 10; // 0, 5, 10, 20 static HWND g_hwndMain, g_hwndLoupe; static HHOOK g_mouseHook, g_kbdHook; static BOOL g_picking = FALSE; static COLORREF g_history[HISTORY_CAP]; static int g_historyCount = 0; static NOTIFYICONDATA g_nid = {0}; static HDC g_screenDC; static HFONT g_fontBig, g_fontSmall, g_fontMenu; // ---------------- settings persistence ---------------- static void RegGetInt(HKEY k, const wchar_t *name, int *out) { DWORD v, sz = sizeof(DWORD); if (RegQueryValueEx(k, name, NULL, NULL, (BYTE*)&v, &sz) == ERROR_SUCCESS) *out = (int)v; } static void LoadSettings(void) { HKEY k; if (RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\TrayColorPicker", 0, KEY_READ, &k) != ERROR_SUCCESS) return; int g = g_grid; RegGetInt(k, L"Zoom", &g_zoom); RegGetInt(k, L"Cells", &g_cells); RegGetInt(k, L"InfoMode", &g_infoMode); RegGetInt(k, L"Grid", &g); g_grid = (BOOL)g; RegGetInt(k, L"CopyFmt", &g_copyFmt); RegGetInt(k, L"HistSize", &g_histSize); if (g_histSize < 0 || g_histSize > HISTORY_CAP) g_histSize = 10; RegCloseKey(k); } static void SaveSettings(void) { HKEY k; if (RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\TrayColorPicker", 0, NULL, 0, KEY_WRITE, NULL, &k, NULL) != ERROR_SUCCESS) return; DWORD v; v = g_zoom; RegSetValueEx(k, L"Zoom", 0, REG_DWORD, (BYTE*)&v, sizeof(v)); v = g_cells; RegSetValueEx(k, L"Cells", 0, REG_DWORD, (BYTE*)&v, sizeof(v)); v = g_infoMode; RegSetValueEx(k, L"InfoMode", 0, REG_DWORD, (BYTE*)&v, sizeof(v)); v = g_grid; RegSetValueEx(k, L"Grid", 0, REG_DWORD, (BYTE*)&v, sizeof(v)); v = g_copyFmt; RegSetValueEx(k, L"CopyFmt", 0, REG_DWORD, (BYTE*)&v, sizeof(v)); v = g_histSize; RegSetValueEx(k, L"HistSize", 0, REG_DWORD, (BYTE*)&v, sizeof(v)); RegCloseKey(k); } // ---------------- geometry ---------------- static int LoupePx(void) { return g_zoom * g_cells; } static int InfoH(void) { return (g_infoMode == 2) ? 46 : 30; } static int LoupeW(void) { int w = LoupePx() + 2; return w < 168 ? 168 : w; } static int LoupeH(void) { return LoupePx() + InfoH() + 2; } // ---------------- rainbow icon (in-memory + .ico export) ---------------- static void HsvToRgb(double h, double s, double v, BYTE *r, BYTE *g, BYTE *b) { double c = v * s, hp = h / 60.0; double x = c * (1.0 - fabs(fmod(hp, 2.0) - 1.0)); double rr = 0, gg = 0, bb = 0; if (hp < 1) { rr = c; gg = x; } else if (hp < 2) { rr = x; gg = c; } else if (hp < 3) { gg = c; bb = x; } else if (hp < 4) { gg = x; bb = c; } else if (hp < 5) { rr = x; bb = c; } else { rr = c; bb = x; } double m = v - c; *r = (BYTE)((rr + m) * 255.0); *g = (BYTE)((gg + m) * 255.0); *b = (BYTE)((bb + m) * 255.0); } // fill a top-down 32bpp ARGB pixel buffer with the rainbow wheel static void FillRainbowPixels(DWORD *px, int size) { double cx = (size - 1) / 2.0, cy = (size - 1) / 2.0; double radius = size / 2.0 - 1.0; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { double dx = x - cx, dy = y - cy; double dist = sqrt(dx*dx + dy*dy); DWORD out = 0; if (dist <= radius + 0.5) { double hue = fmod(atan2(dy, dx) * 180.0 / 3.14159265358979 + 360.0, 360.0); double sat = dist / radius; if (sat > 1.0) sat = 1.0; BYTE r, g, b; HsvToRgb(hue, sat, 1.0, &r, &g, &b); double a = (radius + 0.5 - dist); if (a > 1.0) a = 1.0; if (a < 0.0) a = 0.0; BYTE alpha = (BYTE)(a * 255.0); if (alpha < 255) { r = (BYTE)(r * a); g = (BYTE)(g * a); b = (BYTE)(b * a); } out = ((DWORD)alpha << 24) | ((DWORD)r << 16) | ((DWORD)g << 8) | b; } px[y * size + x] = out; } } } static HICON CreateRainbowIcon(int size) { BITMAPV5HEADER bi = {0}; bi.bV5Size = sizeof(bi); bi.bV5Width = size; bi.bV5Height = -size; bi.bV5Planes = 1; bi.bV5BitCount = 32; bi.bV5Compression = BI_BITFIELDS; bi.bV5RedMask = 0x00FF0000; bi.bV5GreenMask = 0x0000FF00; bi.bV5BlueMask = 0x000000FF; bi.bV5AlphaMask = 0xFF000000; void *bits = NULL; HDC hdc = GetDC(NULL); HBITMAP color = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, &bits, NULL, 0); ReleaseDC(NULL, hdc); if (!color) return NULL; FillRainbowPixels((DWORD*)bits, size); HBITMAP mask = CreateBitmap(size, size, 1, 1, NULL); ICONINFO ii = {0}; ii.fIcon = TRUE; ii.hbmColor = color; ii.hbmMask = mask; HICON icon = CreateIconIndirect(&ii); DeleteObject(color); DeleteObject(mask); return icon; } // write rainbow.ico with 16/32/48/256 px images (for embedding via windres) static BOOL WriteRainbowIco(const wchar_t *path) { static const int sizes[] = { 16, 32, 48, 256 }; const int n = 4; FILE *f = _wfopen(path, L"wb"); if (!f) return FALSE; // ICONDIR WORD zero = 0, one = 1, count = (WORD)n; fwrite(&zero, 2, 1, f); fwrite(&one, 2, 1, f); fwrite(&count, 2, 1, f); DWORD offset = 6 + 16 * n; // write directory entries for (int i = 0; i < n; i++) { int s = sizes[i]; DWORD imgSize = 40 + s*s*4 + ((s+31)/32)*4 * s; // BITMAPINFOHEADER + XOR + AND mask BYTE w = (s == 256) ? 0 : (BYTE)s, h = w; BYTE colors = 0, res = 0; WORD planes = 1, bpp = 32; fwrite(&w, 1, 1, f); fwrite(&h, 1, 1, f); fwrite(&colors, 1, 1, f); fwrite(&res, 1, 1, f); fwrite(&planes, 2, 1, f); fwrite(&bpp, 2, 1, f); fwrite(&imgSize, 4, 1, f); fwrite(&offset, 4, 1, f); offset += imgSize; } // write images for (int i = 0; i < n; i++) { int s = sizes[i]; BITMAPINFOHEADER bih = {0}; bih.biSize = 40; bih.biWidth = s; bih.biHeight = s * 2; // *2: XOR+AND convention bih.biPlanes = 1; bih.biBitCount = 32; bih.biCompression = BI_RGB; fwrite(&bih, 40, 1, f); DWORD *px = (DWORD*)malloc(s * s * 4); if (!px) { fclose(f); return FALSE; } FillRainbowPixels(px, s); // ICO stores rows bottom-up for (int y = s - 1; y >= 0; y--) fwrite(&px[y * s], 4, s, f); free(px); // AND mask: all zero (alpha channel handles transparency) int maskRow = ((s + 31) / 32) * 4; BYTE *zeros = (BYTE*)calloc(maskRow, 1); for (int y = 0; y < s; y++) fwrite(zeros, 1, maskRow, f); free(zeros); } fclose(f); return TRUE; } // ---------------- clipboard / notifications ---------------- static void FormatColor(COLORREF c, wchar_t *buf, int n) { if (g_copyFmt == 1) swprintf(buf, n, L"rgb(%d, %d, %d)", GetRValue(c), GetGValue(c), GetBValue(c)); else if (g_copyFmt == 2) swprintf(buf, n, L"%02X%02X%02X", GetRValue(c), GetGValue(c), GetBValue(c)); else swprintf(buf, n, L"#%02X%02X%02X", GetRValue(c), GetGValue(c), GetBValue(c)); } static void CopyColorToClipboard(COLORREF c, HWND owner) { if (g_copyFmt == 3) return; // None wchar_t buf[32]; FormatColor(c, buf, 32); if (OpenClipboard(owner)) { EmptyClipboard(); size_t bytes = (wcslen(buf) + 1) * sizeof(wchar_t); HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, bytes); if (h) { memcpy(GlobalLock(h), buf, bytes); GlobalUnlock(h); SetClipboardData(CF_UNICODETEXT, h); } CloseClipboard(); } } static void ShowBalloon(COLORREF c) { wchar_t fmt[32]; swprintf(fmt, 32, L"#%02X%02X%02X", GetRValue(c), GetGValue(c), GetBValue(c)); if (g_copyFmt == 3) swprintf(g_nid.szInfo, 256, L"%s (RGB %d, %d, %d)", fmt, GetRValue(c), GetGValue(c), GetBValue(c)); else { wchar_t copied[32]; FormatColor(c, copied, 32); swprintf(g_nid.szInfo, 256, L"%s copied to clipboard (RGB %d, %d, %d)", copied, GetRValue(c), GetGValue(c), GetBValue(c)); } wcscpy(g_nid.szInfoTitle, L"Color picked"); g_nid.uFlags = NIF_INFO; g_nid.dwInfoFlags = NIIF_INFO | NIIF_NOSOUND; Shell_NotifyIcon(NIM_MODIFY, &g_nid); } static void AddHistory(COLORREF c) { if (g_histSize == 0) return; for (int i = g_histSize - 1; i > 0; i--) g_history[i] = g_history[i-1]; g_history[0] = c; // newest at top if (g_historyCount < g_histSize) g_historyCount++; } // ---------------- picking ---------------- static void PositionLoupe(POINT pt) { int w = LoupeW(), h = LoupeH(); int x = pt.x + 24, y = pt.y + 24; RECT wa; SystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0); if (x + w > wa.right) x = pt.x - w - 24; if (y + h > wa.bottom) y = pt.y - h - 24; SetWindowPos(g_hwndLoupe, HWND_TOPMOST, x, y, w, h, SWP_NOACTIVATE | SWP_SHOWWINDOW); InvalidateRect(g_hwndLoupe, NULL, FALSE); } static void StopPicking(void) { if (!g_picking) return; g_picking = FALSE; if (g_mouseHook) { UnhookWindowsHookEx(g_mouseHook); g_mouseHook = NULL; } if (g_kbdHook) { UnhookWindowsHookEx(g_kbdHook); g_kbdHook = NULL; } ShowWindow(g_hwndLoupe, SW_HIDE); } static void PickColorAt(POINT pt) { COLORREF c = GetPixel(g_screenDC, pt.x, pt.y); StopPicking(); AddHistory(c); CopyColorToClipboard(c, g_hwndMain); ShowBalloon(c); } static LRESULT CALLBACK MouseHook(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION && g_picking) { MSLLHOOKSTRUCT *m = (MSLLHOOKSTRUCT*)lParam; if (wParam == WM_MOUSEMOVE) { PositionLoupe(m->pt); } else if (wParam == WM_LBUTTONDOWN) { PickColorAt(m->pt); return 1; } else if (wParam == WM_LBUTTONUP) { return 1; } else if (wParam == WM_RBUTTONDOWN || wParam == WM_RBUTTONUP) { StopPicking(); return 1; } } return CallNextHookEx(NULL, nCode, wParam, lParam); } static LRESULT CALLBACK KbdHook(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION && g_picking && wParam == WM_KEYDOWN) { KBDLLHOOKSTRUCT *k = (KBDLLHOOKSTRUCT*)lParam; if (k->vkCode == VK_ESCAPE) { StopPicking(); return 1; } } return CallNextHookEx(NULL, nCode, wParam, lParam); } static void StartPicking(void) { if (g_picking) return; g_picking = TRUE; g_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHook, GetModuleHandle(NULL), 0); g_kbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, KbdHook, GetModuleHandle(NULL), 0); POINT pt; GetCursorPos(&pt); PositionLoupe(pt); } // ---------------- loupe painting ---------------- static void PaintLoupe(HWND hwnd) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); RECT rc; GetClientRect(hwnd, &rc); HDC mem = CreateCompatibleDC(hdc); HBITMAP bmp = CreateCompatibleBitmap(hdc, rc.right, rc.bottom); HBITMAP old = SelectObject(mem, bmp); HBRUSH dark = CreateSolidBrush(RGB(32,32,32)); FillRect(mem, &rc, dark); DeleteObject(dark); POINT pt; GetCursorPos(&pt); int half = g_cells / 2; int lp = LoupePx(); int ox = (rc.right - lp) / 2; SetStretchBltMode(mem, COLORONCOLOR); StretchBlt(mem, ox, 1, lp, lp, g_screenDC, pt.x - half, pt.y - half, g_cells, g_cells, SRCCOPY); if (g_grid && g_zoom >= 6) { HPEN gpen = CreatePen(PS_SOLID, 1, RGB(70,70,70)); HPEN op = SelectObject(mem, gpen); for (int i = 1; i < g_cells; i++) { MoveToEx(mem, ox + i*g_zoom, 1, NULL); LineTo(mem, ox + i*g_zoom, 1 + lp); MoveToEx(mem, ox, 1 + i*g_zoom, NULL); LineTo(mem, ox + lp, 1 + i*g_zoom); } SelectObject(mem, op); DeleteObject(gpen); } COLORREF c = GetPixel(g_screenDC, pt.x, pt.y); HPEN pen = CreatePen(PS_SOLID, 2, RGB(255,60,60)); HPEN oldPen = SelectObject(mem, pen); SelectObject(mem, GetStockObject(NULL_BRUSH)); Rectangle(mem, ox + half*g_zoom - 1, 1 + half*g_zoom - 1, ox + half*g_zoom + g_zoom + 1, 1 + half*g_zoom + g_zoom + 1); SelectObject(mem, oldPen); DeleteObject(pen); int iy = lp + 2; int swSize = InfoH() - 14; HBRUSH sw = CreateSolidBrush(c); RECT swr = { 8, iy + 7, 8 + swSize, iy + 7 + swSize }; FillRect(mem, &swr, sw); DeleteObject(sw); FrameRect(mem, &swr, GetStockObject(WHITE_BRUSH)); wchar_t hex[16], rgb[32]; swprintf(hex, 16, L"#%02X%02X%02X", GetRValue(c), GetGValue(c), GetBValue(c)); swprintf(rgb, 32, L"RGB %d, %d, %d", GetRValue(c), GetGValue(c), GetBValue(c)); SetBkMode(mem, TRANSPARENT); int tx = swr.right + 8; if (g_infoMode == 2) { SelectObject(mem, g_fontBig); SetTextColor(mem, RGB(255,255,255)); TextOut(mem, tx, iy + 5, hex, (int)wcslen(hex)); SelectObject(mem, g_fontSmall); SetTextColor(mem, RGB(190,190,190)); TextOut(mem, tx, iy + 25, rgb, (int)wcslen(rgb)); } else { const wchar_t *s = (g_infoMode == 1) ? rgb : hex; SelectObject(mem, g_fontBig); SetTextColor(mem, RGB(255,255,255)); TextOut(mem, tx, iy + (InfoH() - 20) / 2, s, (int)wcslen(s)); } FrameRect(mem, &rc, GetStockObject(BLACK_BRUSH)); BitBlt(hdc, 0, 0, rc.right, rc.bottom, mem, 0, 0, SRCCOPY); SelectObject(mem, old); DeleteObject(bmp); DeleteDC(mem); EndPaint(hwnd, &ps); } // ---------------- menus ---------------- static void ShowTrayMenu(HWND hwnd) { HMENU settings = CreatePopupMenu(); HMENU zoomMenu = CreatePopupMenu(); for (int i = 0; i < NZOOMS; i++) { wchar_t b[16]; swprintf(b, 16, L"%d\u00D7", ZOOMS[i]); AppendMenu(zoomMenu, MF_STRING | (g_zoom == ZOOMS[i] ? MF_CHECKED : 0), IDM_ZOOM_BASE + i, b); } AppendMenu(settings, MF_POPUP, (UINT_PTR)zoomMenu, L"Magnification"); HMENU cellMenu = CreatePopupMenu(); for (int i = 0; i < NCELLS; i++) { wchar_t b[24]; swprintf(b, 24, L"%d \u00D7 %d pixels", CELLS[i], CELLS[i]); AppendMenu(cellMenu, MF_STRING | (g_cells == CELLS[i] ? MF_CHECKED : 0), IDM_CELL_BASE + i, b); } AppendMenu(settings, MF_POPUP, (UINT_PTR)cellMenu, L"Capture area"); HMENU infoMenu = CreatePopupMenu(); AppendMenu(infoMenu, MF_STRING | (g_infoMode == 0 ? MF_CHECKED : 0), IDM_INFO_HEX, L"Hex only"); AppendMenu(infoMenu, MF_STRING | (g_infoMode == 1 ? MF_CHECKED : 0), IDM_INFO_RGB, L"RGB only"); AppendMenu(infoMenu, MF_STRING | (g_infoMode == 2 ? MF_CHECKED : 0), IDM_INFO_BOTH, L"Hex + RGB"); AppendMenu(settings, MF_POPUP, (UINT_PTR)infoMenu, L"Readout"); AppendMenu(settings, MF_STRING | (g_grid ? MF_CHECKED : 0), IDM_GRID, L"Pixel grid"); HMENU copyMenu = CreatePopupMenu(); AppendMenu(copyMenu, MF_STRING | (g_copyFmt == 0 ? MF_CHECKED : 0), IDM_COPY_HEX, L"#RRGGBB"); AppendMenu(copyMenu, MF_STRING | (g_copyFmt == 2 ? MF_CHECKED : 0), IDM_COPY_RAW, L"RRGGBB"); AppendMenu(copyMenu, MF_STRING | (g_copyFmt == 1 ? MF_CHECKED : 0), IDM_COPY_RGB, L"rgb(r, g, b)"); AppendMenu(copyMenu, MF_STRING | (g_copyFmt == 3 ? MF_CHECKED : 0), IDM_COPY_NONE, L"None (don't copy)"); AppendMenu(settings, MF_POPUP, (UINT_PTR)copyMenu, L"Copy format"); HMENU histMenu = CreatePopupMenu(); for (int i = 0; i < NHSIZES; i++) { wchar_t b[24]; if (HSIZES[i] == 0) wcscpy(b, L"Off"); else swprintf(b, 24, L"%d colors", HSIZES[i]); AppendMenu(histMenu, MF_STRING | (g_histSize == HSIZES[i] ? MF_CHECKED : 0), IDM_HSIZE_BASE + i, b); } AppendMenu(settings, MF_POPUP, (UINT_PTR)histMenu, L"History size"); HMENU menu = CreatePopupMenu(); AppendMenu(menu, MF_STRING, IDM_PICK, L"Pick Color"); AppendMenu(menu, MF_POPUP, (UINT_PTR)settings, L"Settings"); if (g_historyCount > 0) { AppendMenu(menu, MF_SEPARATOR, 0, NULL); // non-clickable hint header AppendMenu(menu, MF_STRING | MF_GRAYED | MF_DISABLED, 0, L"Recent colors (newest first)"); for (int i = 0; i < g_historyCount; i++) { // owner-drawn: swatch is painted in WM_DRAWITEM; itemData = history index AppendMenu(menu, MF_OWNERDRAW, IDM_HISTORY0 + i, (LPCWSTR)(UINT_PTR)i); } AppendMenu(menu, MF_STRING, IDM_CLEARHIST, L"Clear history"); } AppendMenu(menu, MF_SEPARATOR, 0, NULL); AppendMenu(menu, MF_STRING, IDM_EXIT, L"Exit"); POINT pt; GetCursorPos(&pt); SetForegroundWindow(hwnd); TrackPopupMenu(menu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, NULL); DestroyMenu(menu); } // owner-drawn history rows: [swatch] #RRGGBB (r, g, b) #define HITEM_H 24 #define HITEM_W 190 static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_TRAYICON: if (lParam == WM_LBUTTONUP) StartPicking(); else if (lParam == WM_RBUTTONUP) ShowTrayMenu(hwnd); return 0; case WM_MEASUREITEM: { MEASUREITEMSTRUCT *mi = (MEASUREITEMSTRUCT*)lParam; if (mi->CtlType == ODT_MENU) { mi->itemWidth = HITEM_W; mi->itemHeight = HITEM_H; return TRUE; } break; } case WM_DRAWITEM: { DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT*)lParam; if (di->CtlType == ODT_MENU) { int idx = (int)(UINT_PTR)di->itemData; if (idx < 0 || idx >= g_historyCount) return TRUE; COLORREF c = g_history[idx]; BOOL sel = (di->itemState & ODS_SELECTED) != 0; // background HBRUSH bg = CreateSolidBrush(sel ? GetSysColor(COLOR_HIGHLIGHT) : GetSysColor(COLOR_MENU)); FillRect(di->hDC, &di->rcItem, bg); DeleteObject(bg); // swatch with border RECT swr = { di->rcItem.left + 6, di->rcItem.top + 4, di->rcItem.left + 6 + (HITEM_H - 8), di->rcItem.bottom - 4 }; HBRUSH sw = CreateSolidBrush(c); FillRect(di->hDC, &swr, sw); DeleteObject(sw); FrameRect(di->hDC, &swr, (HBRUSH)GetStockObject(GRAY_BRUSH)); // text wchar_t buf[48]; swprintf(buf, 48, L"#%02X%02X%02X (%d, %d, %d)", GetRValue(c), GetGValue(c), GetBValue(c), GetRValue(c), GetGValue(c), GetBValue(c)); SetBkMode(di->hDC, TRANSPARENT); SetTextColor(di->hDC, sel ? GetSysColor(COLOR_HIGHLIGHTTEXT) : GetSysColor(COLOR_MENUTEXT)); HFONT of = SelectObject(di->hDC, g_fontMenu); RECT tr = di->rcItem; tr.left = swr.right + 8; DrawText(di->hDC, buf, -1, &tr, DT_SINGLELINE | DT_VCENTER | DT_LEFT); SelectObject(di->hDC, of); return TRUE; } break; } case WM_COMMAND: { WORD id = LOWORD(wParam); if (id == IDM_PICK) StartPicking(); else if (id == IDM_EXIT) DestroyWindow(hwnd); else if (id == IDM_CLEARHIST) g_historyCount = 0; else if (id >= IDM_ZOOM_BASE && id < IDM_ZOOM_BASE + NZOOMS) { g_zoom = ZOOMS[id - IDM_ZOOM_BASE]; SaveSettings(); } else if (id >= IDM_CELL_BASE && id < IDM_CELL_BASE + NCELLS) { g_cells = CELLS[id - IDM_CELL_BASE]; SaveSettings(); } else if (id == IDM_INFO_HEX) { g_infoMode = 0; SaveSettings(); } else if (id == IDM_INFO_RGB) { g_infoMode = 1; SaveSettings(); } else if (id == IDM_INFO_BOTH) { g_infoMode = 2; SaveSettings(); } else if (id == IDM_GRID) { g_grid = !g_grid; SaveSettings(); } else if (id == IDM_COPY_HEX) { g_copyFmt = 0; SaveSettings(); } else if (id == IDM_COPY_RGB) { g_copyFmt = 1; SaveSettings(); } else if (id == IDM_COPY_RAW) { g_copyFmt = 2; SaveSettings(); } else if (id == IDM_COPY_NONE) { g_copyFmt = 3; SaveSettings(); } else if (id >= IDM_HSIZE_BASE && id < IDM_HSIZE_BASE + NHSIZES) { g_histSize = HSIZES[id - IDM_HSIZE_BASE]; if (g_historyCount > g_histSize) g_historyCount = g_histSize; // trim SaveSettings(); } else if (id >= IDM_HISTORY0 && id < IDM_HISTORY0 + HISTORY_CAP) { COLORREF c = g_history[id - IDM_HISTORY0]; if (g_copyFmt == 3) { // Copy format is None; still copy hex on explicit history click wchar_t buf[16]; swprintf(buf, 16, L"#%02X%02X%02X", GetRValue(c), GetGValue(c), GetBValue(c)); if (OpenClipboard(hwnd)) { EmptyClipboard(); size_t bytes = (wcslen(buf) + 1) * sizeof(wchar_t); HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, bytes); if (h) { memcpy(GlobalLock(h), buf, bytes); GlobalUnlock(h); SetClipboardData(CF_UNICODETEXT, h); } CloseClipboard(); } } else { CopyColorToClipboard(c, hwnd); } ShowBalloon(c); } return 0; } case WM_DESTROY: StopPicking(); Shell_NotifyIcon(NIM_DELETE, &g_nid); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } static LRESULT CALLBACK LoupeProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == WM_PAINT) { PaintLoupe(hwnd); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR cmd, int show) { // "--make-ico" mode: write rainbow.ico next to the exe and exit if (cmd && wcsstr(cmd, L"--make-ico")) { if (WriteRainbowIco(L"rainbow.ico")) MessageBox(NULL, L"rainbow.ico written to the current folder.", L"Color Picker", MB_OK | MB_ICONINFORMATION); else MessageBox(NULL, L"Failed to write rainbow.ico.", L"Color Picker", MB_OK | MB_ICONERROR); return 0; } SetProcessDPIAware(); LoadSettings(); g_screenDC = GetDC(NULL); g_fontBig = CreateFont(-15, 0, 0, 0, FW_SEMIBOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI"); g_fontSmall = CreateFont(-12, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI"); g_fontMenu = CreateFont(-13, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI"); WNDCLASS wc = {0}; wc.lpfnWndProc = WndProc; wc.hInstance = hInst; wc.lpszClassName = L"TrayColorPickerMain"; RegisterClass(&wc); g_hwndMain = CreateWindow(wc.lpszClassName, L"", 0, 0,0,0,0, NULL, NULL, hInst, NULL); WNDCLASS lc = {0}; lc.lpfnWndProc = LoupeProc; lc.hInstance = hInst; lc.lpszClassName = L"TrayColorPickerLoupe"; lc.hCursor = LoadCursor(NULL, IDC_CROSS); RegisterClass(&lc); g_hwndLoupe = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE, lc.lpszClassName, L"", WS_POPUP, 0, 0, LoupeW(), LoupeH(), NULL, NULL, hInst, NULL); // Prefer the embedded icon resource (id 1, added via windres); fall back to generated HICON trayIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(1), IMAGE_ICON, 16, 16, 0); HICON generated = NULL; if (!trayIcon) { generated = CreateRainbowIcon(32); trayIcon = generated; } g_nid.cbSize = sizeof(g_nid); g_nid.hWnd = g_hwndMain; g_nid.uID = ID_TRAY; g_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; g_nid.uCallbackMessage = WM_TRAYICON; g_nid.hIcon = trayIcon ? trayIcon : LoadIcon(NULL, IDI_APPLICATION); wcscpy(g_nid.szTip, L"Color Picker \u2014 click to pick a color"); Shell_NotifyIcon(NIM_ADD, &g_nid); MSG m; while (GetMessage(&m, NULL, 0, 0)) { TranslateMessage(&m); DispatchMessage(&m); } if (generated) DestroyIcon(generated); DeleteObject(g_fontBig); DeleteObject(g_fontSmall); DeleteObject(g_fontMenu); ReleaseDC(NULL, g_screenDC); return 0; }