/****************************************************************************/ /* */ /* Microsoft Confidential */ /* */ /* Copyright (c) Microsoft Corp. 1987, 1990 */ /* All Rights Reserved */ /* */ /****************************************************************************/ /****************************** Module Header ******************************* * Module Name: zoomin.c * * Microsoft ZoomIn utility. This tool magnifies a portion of the screen, * allowing you to see things at a pixel level. * * History: * 01/01/88 ToddLa Created. * 01/01/92 MarkTa Ported to NT. * 03/06/92 ByronD Cleanup. * ****************************************************************************/ #include "zoomin.h" TCHAR szAppName[] = TEXT("ZoomIn"); // Aplication name. HINSTANCE ghInst; // Instance handle. HWND ghwndApp; // Main window handle. HANDLE ghaccelTable; // Main accelerator table. INT gnZoom = 4; // Zoom (magnification) factor. HPALETTE ghpalPhysical; // Handle to the physical palette. INT gcxScreenMax; // Width of the screen (less 1). INT gcyScreenMax; // Height of the screen (less 1). INT gcxZoomed; // Client width in zoomed pixels. INT gcyZoomed; // Client height in zoomed pixels. BOOL gfRefEnable = FALSE; // TRUE if refresh is enabled. INT gnRefInterval = 20; // Refresh interval in 10ths of seconds. BOOL gfTracking = FALSE; // TRUE if tracking is in progress. POINT gptZoom = {100, 100}; // The center of the zoomed area. BOOL gShowGrid = FALSE; // Show a grid so you can see the pixels /************************************************************************ * WinMain * * Main entry point for the application. * * Arguments: * * History: * ************************************************************************/ INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, INT nCmdShow ) { MSG msg; if (!InitInstance(hInst, nCmdShow)) return FALSE; /* * Polling messages from event queue */ while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(ghwndApp, ghaccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (INT)msg.wParam; } /************************************************************************ * InitInstance * * Instance initialization for the app. * * Arguments: * * History: * ************************************************************************/ BOOL InitInstance( HINSTANCE hInst, INT cmdShow ) { WNDCLASS wc; INT dx; INT dy; DWORD flStyle; RECT rc; ghInst = hInst; /* * Register a class for the main application window. */ wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(hInst, TEXT("zoomin")); wc.lpszMenuName = MAKEINTRESOURCE(IDMENU_ZOOMIN); wc.lpszClassName = szAppName; wc.hbrBackground = GetStockObject(BLACK_BRUSH); wc.hInstance = hInst; wc.style = CS_VREDRAW | CS_HREDRAW; wc.lpfnWndProc = AppWndProc; wc.cbWndExtra = 0; wc.cbClsExtra = 0; if (!RegisterClass(&wc)) return FALSE; if (!(ghaccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDACCEL_ZOOMIN)))) return FALSE; if (!(ghpalPhysical = CreatePhysicalPalette())) return FALSE; /* Get the size of the screen. ** In NT 4.0 sp3 and NT 5.0 new system metrics would get the ** desktop area which may go across multiple monitors. If that ** doesn't work, fall back to the old method. */ #ifdef SM_CXVIRTUALSCREEN if( GetSystemMetrics(SM_CXVIRTUALSCREEN) ) { gcxScreenMax= GetSystemMetrics(SM_CXVIRTUALSCREEN) -1; gcyScreenMax= GetSystemMetrics(SM_CYVIRTUALSCREEN) -1; } else #endif { gcxScreenMax= GetSystemMetrics(SM_CXSCREEN) - 1; gcyScreenMax= GetSystemMetrics(SM_CYSCREEN) - 1; } flStyle = WS_CAPTION | WS_OVERLAPPED | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_VSCROLL; dx = 44 * gnZoom; dy = 36 * gnZoom; SetRect(&rc, 0, 0, dx, dy); AdjustWindowRect(&rc, flStyle, TRUE); ghwndApp = CreateWindow(szAppName, szAppName, flStyle, CW_USEDEFAULT, 0, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInst, NULL); if (!ghwndApp) return FALSE; ShowWindow(ghwndApp, cmdShow); return TRUE; } /************************************************************************ * CreatePhysicalPalette * * Creates a palette for the app to use. The palette references the * physical palette, so that it can properly display images grabbed * from palette managed apps. * * History: * ************************************************************************/ HPALETTE CreatePhysicalPalette( VOID ) { PLOGPALETTE ppal; HPALETTE hpal = NULL; INT i; ppal = (PLOGPALETTE)LocalAlloc(LPTR, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * NPAL); if (ppal) { ppal->palVersion = 0x300; ppal->palNumEntries = NPAL; for (i = 0; i < NPAL; i++) { ppal->palPalEntry[i].peFlags = (BYTE)PC_EXPLICIT; ppal->palPalEntry[i].peRed = (BYTE)i; ppal->palPalEntry[i].peGreen = (BYTE)0; ppal->palPalEntry[i].peBlue = (BYTE)0; } hpal = CreatePalette(ppal); LocalFree(ppal); } return hpal; } /************************************************************************ * AppWndProc * * Main window proc for the zoomin utility. * * Arguments: * Standard window proc args. * * History: * ************************************************************************/ INT_PTR APIENTRY AppWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { PAINTSTRUCT ps; PRECT prc; HCURSOR hcurOld; switch (msg) { case WM_CREATE: SetScrollRange(hwnd, SB_VERT, MIN_ZOOM, MAX_ZOOM, FALSE); SetScrollPos(hwnd, SB_VERT, gnZoom, FALSE); break; case WM_TIMER: /* * Update on every timer message. The cursor will be * flashed to the hourglash for some visual feedback * of when a snapshot is being taken. */ hcurOld = SetCursor(LoadCursor(NULL, IDC_WAIT)); DoTheZoomIn(NULL); SetCursor(hcurOld); break; case WM_PAINT: BeginPaint(hwnd, &ps); DoTheZoomIn(ps.hdc); EndPaint(hwnd, &ps); return 0L; case WM_SIZE: CalcZoomedSize(); break; case WM_LBUTTONDOWN: gptZoom.x= (SHORT) LOWORD(lParam); gptZoom.y= (SHORT) HIWORD(lParam); ClientToScreen(hwnd, &gptZoom); DrawZoomRect(); DoTheZoomIn(NULL); SetCapture(hwnd); gfTracking = TRUE; break; case WM_MOUSEMOVE: if (gfTracking) { DrawZoomRect(); gptZoom.x= (SHORT) LOWORD(lParam); gptZoom.y= (SHORT) HIWORD(lParam); ClientToScreen(hwnd, &gptZoom); DrawZoomRect(); DoTheZoomIn(NULL); } break; case WM_LBUTTONUP: if (gfTracking) { DrawZoomRect(); ReleaseCapture(); gfTracking = FALSE; } break; case WM_VSCROLL: switch (LOWORD(wParam)) { case SB_LINEDOWN: gnZoom++; break; case SB_LINEUP: gnZoom--; break; case SB_PAGEUP: gnZoom -= 2; break; case SB_PAGEDOWN: gnZoom += 2; break; case SB_THUMBPOSITION: case SB_THUMBTRACK: gnZoom = HIWORD(wParam); break; } gnZoom = BOUND(gnZoom, MIN_ZOOM, MAX_ZOOM); SetScrollPos(hwnd, SB_VERT, gnZoom, TRUE); CalcZoomedSize(); DoTheZoomIn(NULL); break; case WM_KEYDOWN: switch (wParam) { case VK_UP: case VK_DOWN: case VK_LEFT: case VK_RIGHT: MoveView((INT)wParam, GetKeyState(VK_SHIFT) & 0x8000, GetKeyState(VK_CONTROL) & 0x8000); break; } break; case WM_COMMAND: switch (LOWORD(wParam)) { case MENU_EDIT_COPY: CopyToClipboard(); break; case MENU_EDIT_REFRESH: DoTheZoomIn(NULL); break; case MENU_OPTIONS_REFRESHRATE: DialogBox(ghInst, MAKEINTRESOURCE(DID_REFRESHRATE), hwnd, RefreshRateDlgProc); break; case MENU_HELP_ABOUT: DialogBox(ghInst, MAKEINTRESOURCE(DID_ABOUT), hwnd, AboutDlgProc); break; case MENU_OPTIONS_SHOWGRID: { HMENU hMenu = GetSubMenu(GetMenu(ghwndApp), 1); gShowGrid = !gShowGrid; InvalidateRect(ghwndApp, NULL, FALSE); CheckMenuItem(hMenu, GetMenuItemID(hMenu, 1), gShowGrid ? MF_CHECKED : MF_UNCHECKED); } break; default: break; } break; case WM_CLOSE: if (ghpalPhysical) DeleteObject(ghpalPhysical); DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0L; } /************************************************************************ * CalcZoomedSize * * Calculates some globals. This routine needs to be called any * time that the size of the app or the zoom factor changes. * * History: * ************************************************************************/ VOID CalcZoomedSize( VOID ) { RECT rc; GetClientRect(ghwndApp, &rc); gcxZoomed = (rc.right / gnZoom) + 1; gcyZoomed = (rc.bottom / gnZoom) + 1; } /************************************************************************ * DoTheZoomIn * * Does the actual paint of the zoomed image. * * Arguments: * HDC hdc - If not NULL, this hdc will be used to paint with. * If NULL, a dc for the apps window will be obtained. * * History: * ************************************************************************/ VOID DoTheZoomIn( HDC hdc ) { BOOL fRelease; HPALETTE hpalOld = NULL; HDC hdcScreen; INT x; INT y; if (!hdc) { hdc = GetDC(ghwndApp); fRelease = TRUE; } else { fRelease = FALSE; } if (ghpalPhysical) { hpalOld = SelectPalette(hdc, ghpalPhysical, FALSE); RealizePalette(hdc); } /* * The point must not include areas outside the screen dimensions. */ x = BOUND(gptZoom.x, gcxZoomed / 2, gcxScreenMax - (gcxZoomed / 2)); y = BOUND(gptZoom.y, gcyZoomed / 2, gcyScreenMax - (gcyZoomed / 2)); hdcScreen = GetDC(NULL); SetStretchBltMode(hdc, COLORONCOLOR); StretchBlt(hdc, 0, 0, gnZoom * gcxZoomed, gnZoom * gcyZoomed, hdcScreen, x - gcxZoomed / 2, y - gcyZoomed / 2, gcxZoomed, gcyZoomed, SRCCOPY); if (gShowGrid && gnZoom > 1) // don't bother if we're 1 to 1 { int i = 0, j = 0; // use gray for now. later we could get fancy about the colors // so that the line is visible when the pixels are gray HGDIOBJ hBrush = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_GRAYTEXT)); HGDIOBJ hOld = SelectObject(hdc, hBrush); // first draw the vertical lines... while (i < gcxZoomed*gnZoom) { MoveToEx(hdc, i, 0, NULL); LineTo(hdc, i, gcyZoomed*gnZoom); i += gnZoom; } // ... then draw the horizontal lines while (j < gcyZoomed*gnZoom) { MoveToEx(hdc, 0, j, NULL); LineTo(hdc, gcxZoomed*gnZoom, j); j += gnZoom; } DeleteObject(SelectObject(hdc, hOld)); } ReleaseDC(NULL, hdcScreen); if (hpalOld) SelectPalette(hdc, hpalOld, FALSE); if (fRelease) ReleaseDC(ghwndApp, hdc); } /************************************************************************ * MoveView * * This function moves the current view around. * * Arguments: * INT nDirectionCode - Direction to move. Must be VK_UP, VK_DOWN, * VK_LEFT or VK_RIGHT. * BOOL fFast - TRUE if the move should jump a larger increment. * If FALSE, the move is just one pixel. * BOOL fPeg - If TRUE, the view will be pegged to the screen * boundary in the specified direction. This overides * the fFast parameter. * * History: * ************************************************************************/ VOID MoveView( INT nDirectionCode, BOOL fFast, BOOL fPeg ) { INT delta; if (fFast) delta = FASTDELTA; else delta = 1; switch (nDirectionCode) { case VK_UP: if (fPeg) gptZoom.y = gcyZoomed / 2; else gptZoom.y -= delta; gptZoom.y = BOUND(gptZoom.y, 0, gcyScreenMax); break; case VK_DOWN: if (fPeg) gptZoom.y = gcyScreenMax - (gcyZoomed / 2); else gptZoom.y += delta; gptZoom.y = BOUND(gptZoom.y, 0, gcyScreenMax); break; case VK_LEFT: if (fPeg) gptZoom.x = gcxZoomed / 2; else gptZoom.x -= delta; gptZoom.x = BOUND(gptZoom.x, 0, gcxScreenMax); break; case VK_RIGHT: if (fPeg) gptZoom.x = gcxScreenMax - (gcxZoomed / 2); else gptZoom.x += delta; gptZoom.x = BOUND(gptZoom.x, 0, gcxScreenMax); break; } DoTheZoomIn(NULL); } /************************************************************************ * DrawZoomRect * * This function draws the tracking rectangle. The size and shape of * the rectangle will be proportional to the size and shape of the * app's client, and will be affected by the zoom factor as well. * * History: * ************************************************************************/ VOID DrawZoomRect( VOID ) { HDC hdc; RECT rc; INT x; INT y; x = BOUND(gptZoom.x, gcxZoomed / 2, gcxScreenMax - (gcxZoomed / 2)); y = BOUND(gptZoom.y, gcyZoomed / 2, gcyScreenMax - (gcyZoomed / 2)); rc.left = x - gcxZoomed / 2; rc.top = y - gcyZoomed / 2; rc.right = rc.left + gcxZoomed; rc.bottom = rc.top + gcyZoomed; InflateRect(&rc, 1, 1); hdc = GetDC(NULL); PatBlt(hdc, rc.left, rc.top, rc.right-rc.left, 1, DSTINVERT); PatBlt(hdc, rc.left, rc.bottom, 1, -(rc.bottom-rc.top), DSTINVERT); PatBlt(hdc, rc.right-1, rc.top, 1, rc.bottom-rc.top, DSTINVERT); PatBlt(hdc, rc.right, rc.bottom-1, -(rc.right-rc.left), 1, DSTINVERT); ReleaseDC(NULL, hdc); } /************************************************************************ * EnableRefresh * * This function turns on or off the auto-refresh feature. * * Arguments: * BOOL fEnable - TRUE to turn the refresh feature on, FALSE to * turn it off. * * History: * ************************************************************************/ VOID EnableRefresh( BOOL fEnable ) { if (fEnable) { /* * Already enabled. Do nothing. */ if (gfRefEnable) return; if (SetTimer(ghwndApp, IDTIMER_ZOOMIN, gnRefInterval * 100, NULL)) gfRefEnable = TRUE; } else { /* * Not enabled yet. Do nothing. */ if (!gfRefEnable) return; KillTimer(ghwndApp, IDTIMER_ZOOMIN); gfRefEnable = FALSE; } } /************************************************************************ * CopyToClipboard * * This function copies the client area image of the app into the * clipboard. * * History: * ************************************************************************/ VOID CopyToClipboard( VOID ) { HDC hdcSrc; HDC hdcDst; RECT rc; HBITMAP hbm; if (OpenClipboard(ghwndApp)) { EmptyClipboard(); if (hdcSrc = GetDC(ghwndApp)) { GetClientRect(ghwndApp, &rc); if (hbm = CreateCompatibleBitmap(hdcSrc, rc.right - rc.left, rc.bottom - rc.top)) { if (hdcDst = CreateCompatibleDC(hdcSrc)) { /* * Calculate the dimensions of the bitmap and * convert them to tenths of a millimeter for * setting the size with the SetBitmapDimensionEx * call. This allows programs like WinWord to * retrieve the bitmap and know what size to * display it as. */ SetBitmapDimensionEx(hbm, (DWORD)(((DWORD)(rc.right - rc.left) * MM10PERINCH) / (DWORD)GetDeviceCaps(hdcSrc, LOGPIXELSX)), (DWORD)(((DWORD)(rc.bottom - rc.top) * MM10PERINCH) / (DWORD)GetDeviceCaps(hdcSrc, LOGPIXELSY)), NULL); SelectObject(hdcDst, hbm); BitBlt(hdcDst, 0, 0, rc.right - rc.left, rc.bottom - rc.top, hdcSrc, rc.left, rc.top, SRCCOPY); DeleteDC(hdcDst); SetClipboardData(CF_BITMAP, hbm); } else { DeleteObject(hbm); } } ReleaseDC(ghwndApp, hdcSrc); } CloseClipboard(); } else { MessageBeep(0); } } /************************************************************************ * AboutDlgProc * * This is the About Box dialog procedure. * * History: * ************************************************************************/ INT_PTR APIENTRY AboutDlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch (msg) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: EndDialog(hwnd, IDOK); return TRUE; default: return FALSE; } } /************************************************************************ * RefreshRateDlgProc * * This is the Refresh Rate dialog procedure. * * History: * ************************************************************************/ INT_PTR APIENTRY RefreshRateDlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { BOOL fTranslated; switch (msg) { case WM_INITDIALOG: SendDlgItemMessage(hwnd, DID_REFRESHRATEINTERVAL, EM_LIMITTEXT, 3, 0L); SetDlgItemInt(hwnd, DID_REFRESHRATEINTERVAL, gnRefInterval, FALSE); CheckDlgButton(hwnd, DID_REFRESHRATEENABLE, gfRefEnable ? 1 : 0); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: gnRefInterval = GetDlgItemInt(hwnd, DID_REFRESHRATEINTERVAL, &fTranslated, FALSE); /* * Stop any existing timers then start one with the * new interval if requested to. */ EnableRefresh(FALSE); EnableRefresh( IsDlgButtonChecked(hwnd, DID_REFRESHRATEENABLE)); EndDialog(hwnd, IDOK); break; case IDCANCEL: EndDialog(hwnd, IDCANCEL); break; } break; } return FALSE; }