windows-nt/Source/XPSP1/NT/sdktools/zoomin/zoomin.c
2020-09-26 16:20:57 +08:00

831 lines
21 KiB
C

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