2 * PROJECT: ReactOS Magnify
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/magnify/magnifier.c
5 * PURPOSE: Magnification of parts of the screen.
7 * Marc Piulachs <marc.piulachs@codexchange.net>
8 * David Quintana <gigaherz@gmail.com>
12 #include "magnifier.h"
23 const TCHAR szWindowClass
[] = TEXT("MAGNIFIER");
25 #define MAX_LOADSTRING 100
27 /* Global Variables */
31 TCHAR szTitle
[MAX_LOADSTRING
];
34 #define REPAINT_SPEED 100
38 HWND hDesktopWindow
= NULL
;
40 #define APPMSG_NOTIFYICON (WM_APP+1)
45 BOOL bOptionsDialog
= FALSE
;
47 BOOL bRecreateOffscreenDC
= TRUE
;
49 LONG sourceHeight
= 0;
50 HDC hdcOffscreen
= NULL
;
52 HBITMAP hbmpOffscreen
= NULL
;
54 /* Current magnified area */
64 ATOM
MyRegisterClass(HINSTANCE hInstance
);
65 BOOL
InitInstance(HINSTANCE
, int);
66 LRESULT CALLBACK
WndProc(HWND
, UINT
, WPARAM
, LPARAM
);
67 INT_PTR CALLBACK
AboutProc(HWND
, UINT
, WPARAM
, LPARAM
);
68 INT_PTR CALLBACK
OptionsProc(HWND
, UINT
, WPARAM
, LPARAM
);
69 INT_PTR CALLBACK
WarningProc(HWND
, UINT
, WPARAM
, LPARAM
);
71 int WINAPI
WinMain( HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPSTR lpCmdLine
, int nCmdShow
)
76 UNREFERENCED_PARAMETER(hPrevInstance
);
77 UNREFERENCED_PARAMETER(lpCmdLine
);
79 switch (GetUserDefaultUILanguage())
81 case MAKELANGID(LANG_HEBREW
, SUBLANG_DEFAULT
):
82 SetProcessDefaultLayout(LAYOUT_RTL
);
89 /* Initialize global strings */
90 LoadString(hInstance
, IDS_APP_TITLE
, szTitle
, MAX_LOADSTRING
);
91 MyRegisterClass(hInstance
);
93 /* Perform application initialization */
94 if (!InitInstance(hInstance
, nCmdShow
))
97 hAccelTable
= LoadAccelerators(hInstance
, MAKEINTRESOURCE(IDC_MAGNIFIER
));
99 /* Main message loop */
100 while (GetMessage(&msg
, NULL
, 0, 0))
102 if (!TranslateAccelerator(msg
.hwnd
, hAccelTable
, &msg
))
104 TranslateMessage(&msg
);
105 DispatchMessage(&msg
);
110 SelectObject(hdcOffscreen
, hbmpOld
);
111 DeleteObject (hbmpOffscreen
);
112 DeleteDC(hdcOffscreen
);
114 return (int) msg
.wParam
;
117 ATOM
MyRegisterClass(HINSTANCE hInstance
)
121 wc
.style
= CS_HREDRAW
| CS_VREDRAW
;
122 wc
.lpfnWndProc
= WndProc
;
125 wc
.hInstance
= hInstance
;
126 wc
.hIcon
= LoadIcon(hInstance
, MAKEINTRESOURCE(IDI_ICON
));
127 wc
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
128 wc
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+1);
129 wc
.lpszMenuName
= NULL
; //MAKEINTRESOURCE(IDC_MAGNIFIER);
130 wc
.lpszClassName
= szWindowClass
;
132 return RegisterClass(&wc
);
135 BOOL
InitInstance(HINSTANCE hInstance
, int nCmdShow
)
138 hInst
= hInstance
; // Store instance handle in our global variable
140 SystemParametersInfo(SPI_GETWORKAREA
, 0, &rcWorkArea
, 0);
142 /* Create the Window */
143 hMainWnd
= CreateWindowEx(
144 WS_EX_TOPMOST
| WS_EX_PALETTEWINDOW
,
150 (rcWorkArea
.right
- rcWorkArea
.left
) * 2 / 3,
160 ShowWindow(hMainWnd
, bStartMinimized
? SW_MINIMIZE
: nCmdShow
);
161 UpdateWindow(hMainWnd
);
163 // Windows 2003's Magnifier always shows this dialog, and exits when the dialog isclosed.
164 // Should we add a custom means to prevent opening it?
165 hOptionsDialog
= CreateDialog(hInstance
, MAKEINTRESOURCE(IDD_DIALOGOPTIONS
), hMainWnd
, OptionsProc
);
168 DialogBox(hInstance
, MAKEINTRESOURCE(IDD_WARNINGDIALOG
), hMainWnd
, WarningProc
);
175 if (!IsIconic(hMainWnd
))
177 /* Invalidate the client area forcing a WM_PAINT message */
178 InvalidateRgn(hMainWnd
, NULL
, TRUE
);
182 void GetBestOverlapWithMonitors(LPRECT rect
)
185 int rcWidth
, rcHeight
;
187 HMONITOR hMon
= MonitorFromRect(rect
, MONITOR_DEFAULTTONEAREST
);
189 info
.cbSize
= sizeof(info
);
191 GetMonitorInfo(hMon
, &info
);
193 rcMon
= info
.rcMonitor
;
197 rcWidth
= (rect
->right
- rect
->left
);
198 rcHeight
= (rect
->bottom
- rect
->top
);
200 if (rcLeft
< rcMon
.left
)
203 if (rcTop
< rcMon
.top
)
206 if (rcLeft
> (rcMon
.right
- rcWidth
))
207 rcLeft
= (rcMon
.right
- rcWidth
);
209 if (rcTop
> (rcMon
.bottom
- rcHeight
))
210 rcTop
= (rcMon
.bottom
- rcHeight
);
212 OffsetRect(rect
, (rcLeft
-rect
->left
), (rcTop
-rect
->top
));
217 HDC desktopHdc
= NULL
;
219 RECT sourceRect
, intersectedRect
;
220 RECT targetRect
, appRect
;
225 int AppWidth
, AppHeight
;
230 desktopHdc
= GetDC(0);
232 GetClientRect(hMainWnd
, &appRect
);
233 AppWidth
= (appRect
.right
- appRect
.left
);
234 AppHeight
= (appRect
.bottom
- appRect
.top
);
236 ZeroMemory(&cinfo
, sizeof(cinfo
));
237 ZeroMemory(&iinfo
, sizeof(iinfo
));
238 cinfo
.cbSize
= sizeof(cinfo
);
239 GetCursorInfo(&cinfo
);
240 GetIconInfo(cinfo
.hCursor
, &iinfo
);
242 targetRect
= appRect
;
243 ClientToScreen(hMainWnd
, (POINT
*)&targetRect
.left
);
244 ClientToScreen(hMainWnd
, (POINT
*)&targetRect
.right
);
246 if (bRecreateOffscreenDC
|| !hdcOffscreen
)
248 bRecreateOffscreenDC
= FALSE
;
252 SelectObject(hdcOffscreen
, hbmpOld
);
253 DeleteObject (hbmpOffscreen
);
254 DeleteDC(hdcOffscreen
);
257 sourceWidth
= AppWidth
/ iZoom
;
258 sourceHeight
= AppHeight
/ iZoom
;
260 /* Create a memory DC compatible with client area DC */
261 hdcOffscreen
= CreateCompatibleDC(desktopHdc
);
263 /* Create a bitmap compatible with the client area DC */
264 hbmpOffscreen
= CreateCompatibleBitmap(
269 /* Select our bitmap in memory DC and save the old one */
270 hbmpOld
= SelectObject(hdcOffscreen
, hbmpOffscreen
);
273 GetWindowRect(hDesktopWindow
, &sourceRect
);
274 sourceRect
.left
= (cp
.x
) - (sourceWidth
/2);
275 sourceRect
.top
= (cp
.y
) - (sourceHeight
/2);
276 sourceRect
.right
= sourceRect
.left
+ sourceWidth
;
277 sourceRect
.bottom
= sourceRect
.top
+ sourceHeight
;
279 GetBestOverlapWithMonitors(&sourceRect
);
281 /* Paint the screen bitmap to our in memory DC */
293 if (IntersectRect(&intersectedRect
, &sourceRect
, &targetRect
))
295 OffsetRect(&intersectedRect
, -sourceRect
.left
, -sourceRect
.top
);
296 FillRect(hdcOffscreen
, &intersectedRect
, GetStockObject(DC_BRUSH
));
299 /* Draw the mouse pointer in the right position */
302 pMouse
.x
- iinfo
.xHotspot
- sourceRect
.left
, // - 10,
303 pMouse
.y
- iinfo
.yHotspot
- sourceRect
.top
, // - 10,
306 /* Blast the stretched image from memory DC to window DC */
318 SRCCOPY
| NOMIRRORBITMAP
);
322 DeleteObject(iinfo
.hbmMask
);
324 DeleteObject(iinfo
.hbmColor
);
325 ReleaseDC(hDesktopWindow
, desktopHdc
);
328 void HandleNotifyIconMessage(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
335 PostMessage(hMainWnd
, WM_COMMAND
, IDM_OPTIONS
, 0);
339 TrackPopupMenu(notifyMenu
, 0, pt
.x
, pt
.y
, 0, hWnd
, NULL
);
344 LRESULT CALLBACK
WndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
352 BOOL hasMoved
= FALSE
;
353 HWND hwndForeground
= GetForegroundWindow ();
354 DWORD threadId
= GetWindowThreadProcessId(hwndForeground
, NULL
);
355 GUITHREADINFO guiInfo
;
356 guiInfo
.cbSize
= sizeof(guiInfo
);
358 GetGUIThreadInfo(threadId
, &guiInfo
);
364 //Get current mouse position
365 GetCursorPos (&pNewMouse
);
367 //If mouse has moved ...
368 if (((pMouse
.x
!= pNewMouse
.x
) || (pMouse
.y
!= pNewMouse
.y
)))
370 //Update to new position
377 if (bFollowCaret
&& hwndForeground
&& guiInfo
.hwndCaret
)
380 ptCaret
.x
= (guiInfo
.rcCaret
.left
+ guiInfo
.rcCaret
.right
) / 2;
381 ptCaret
.y
= (guiInfo
.rcCaret
.top
+ guiInfo
.rcCaret
.bottom
) / 2;
383 if (guiInfo
.hwndCaret
&& ((pCaretWnd
!= guiInfo
.hwndCaret
) || (pCaret
.x
!= ptCaret
.x
) || (pCaret
.y
!= ptCaret
.y
)))
385 //Update to new position
387 pCaretWnd
= guiInfo
.hwndCaret
;
390 ClientToScreen (guiInfo
.hwndCaret
, (LPPOINT
) &ptCaret
);
397 if (bFollowFocus
&& hwndForeground
&& guiInfo
.hwndFocus
)
402 //Get current control focus
403 GetWindowRect (guiInfo
.hwndFocus
, &activeRect
);
404 ptFocus
.x
= (activeRect
.left
+ activeRect
.right
) / 2;
405 ptFocus
.y
= (activeRect
.top
+ activeRect
.bottom
) / 2;
407 if(guiInfo
.hwndFocus
&& ((guiInfo
.hwndFocus
!= pFocusWnd
) || (pFocus
.x
!= ptFocus
.x
) || (pFocus
.y
!= ptFocus
.y
)))
409 //Update to new position
411 pFocusWnd
= guiInfo
.hwndFocus
;
420 DWORD newTicks
= GetTickCount();
421 DWORD elapsed
= (newTicks
- lastTicks
);
422 if(elapsed
> REPAINT_SPEED
)
430 lastTicks
= GetTickCount();
438 wmId
= LOWORD(wParam
);
439 /* Parse the menu selections */
445 ShowWindow(hOptionsDialog
, SW_HIDE
);
449 ShowWindow(hOptionsDialog
, SW_SHOW
);
453 DialogBox(hInst
, MAKEINTRESOURCE(IDD_ABOUTBOX
), hWnd
, AboutProc
);
459 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
466 PAINTSTRUCT PaintStruct
;
468 dc
= BeginPaint(hWnd
, &PaintStruct
);
470 EndPaint(hWnd
, &PaintStruct
);
475 TrackPopupMenu(notifyMenu
, 0, GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
), 0, hWnd
, NULL
);
479 case WM_DISPLAYCHANGE
:
480 bRecreateOffscreenDC
= TRUE
;
482 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
485 // handle WM_ERASEBKGND by simply returning non-zero because we did all the drawing in WM_PAINT.
489 /* Save settings to registry */
494 /* Cleanup notification icon */
495 ZeroMemory(&nid
, sizeof(nid
));
496 nid
.cbSize
= sizeof(nid
);
497 nid
.uFlags
= NIF_MESSAGE
;
499 nid
.uCallbackMessage
= APPMSG_NOTIFYICON
;
500 Shell_NotifyIcon(NIM_DELETE
, &nid
);
502 DestroyIcon(notifyIcon
);
504 DestroyWindow(hOptionsDialog
);
511 /* Load settings from registry */
514 /* Get the desktop window */
515 hDesktopWindow
= GetDesktopWindow();
518 SetTimer (hWnd
, 1, TIMER_SPEED
, NULL
);
520 /* Notification icon */
521 notifyIcon
= (HICON
)LoadImage(hInst
, MAKEINTRESOURCE(IDI_ICON
), IMAGE_ICON
, 16, 16, 0);
523 ZeroMemory(&nid
, sizeof(nid
));
524 nid
.cbSize
= sizeof(nid
);
525 nid
.uFlags
= NIF_ICON
| NIF_MESSAGE
;
527 nid
.uCallbackMessage
= APPMSG_NOTIFYICON
;
528 nid
.hIcon
= notifyIcon
;
529 Shell_NotifyIcon(NIM_ADD
, &nid
);
531 tempMenu
= LoadMenu(hInst
, MAKEINTRESOURCE(IDC_MAGNIFIER
));
532 notifyMenu
= GetSubMenu(tempMenu
, 0);
533 RemoveMenu(tempMenu
, 0, MF_BYPOSITION
);
534 DestroyMenu(tempMenu
);
539 case APPMSG_NOTIFYICON
:
540 HandleNotifyIconMessage(hWnd
, wParam
, lParam
);
545 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
551 INT_PTR CALLBACK
AboutProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
553 UNREFERENCED_PARAMETER(lParam
);
557 return (INT_PTR
)TRUE
;
560 if (LOWORD(wParam
) == IDOK
|| LOWORD(wParam
) == IDCANCEL
)
562 EndDialog(hDlg
, LOWORD(wParam
));
563 return (INT_PTR
)TRUE
;
568 return (INT_PTR
)FALSE
;
571 INT_PTR CALLBACK
OptionsProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
573 UNREFERENCED_PARAMETER(lParam
);
577 bOptionsDialog
= wParam
;
581 // Add the zoom items...
582 SendDlgItemMessage(hDlg
, IDC_ZOOM
, CB_ADDSTRING
, 0, (LPARAM
)("1"));
583 SendDlgItemMessage(hDlg
, IDC_ZOOM
, CB_ADDSTRING
, 0, (LPARAM
)("2"));
584 SendDlgItemMessage(hDlg
, IDC_ZOOM
, CB_ADDSTRING
, 0, (LPARAM
)("3"));
585 SendDlgItemMessage(hDlg
, IDC_ZOOM
, CB_ADDSTRING
, 0, (LPARAM
)("4"));
586 SendDlgItemMessage(hDlg
, IDC_ZOOM
, CB_ADDSTRING
, 0, (LPARAM
)("5"));
587 SendDlgItemMessage(hDlg
, IDC_ZOOM
, CB_ADDSTRING
, 0, (LPARAM
)("6"));
588 SendDlgItemMessage(hDlg
, IDC_ZOOM
, CB_ADDSTRING
, 0, (LPARAM
)("7"));
589 SendDlgItemMessage(hDlg
, IDC_ZOOM
, CB_ADDSTRING
, 0, (LPARAM
)("8"));
591 SendDlgItemMessage(hDlg
, IDC_ZOOM
, CB_SETCURSEL
, iZoom
- 1, 0);
594 SendDlgItemMessage(hDlg
,IDC_FOLLOWMOUSECHECK
,BM_SETCHECK
, wParam
,0);
597 SendDlgItemMessage(hDlg
,IDC_FOLLOWKEYBOARDCHECK
,BM_SETCHECK
, wParam
,0);
600 SendDlgItemMessage(hDlg
,IDC_FOLLOWTEXTEDITINGCHECK
,BM_SETCHECK
, wParam
,0);
603 SendDlgItemMessage(hDlg
,IDC_INVERTCOLORSCHECK
,BM_SETCHECK
, wParam
,0);
606 SendDlgItemMessage(hDlg
,IDC_STARTMINIMIZEDCHECK
,BM_SETCHECK
, wParam
,0);
609 SendDlgItemMessage(hDlg
,IDC_SHOWMAGNIFIERCHECK
,BM_SETCHECK
, wParam
,0);
611 return (INT_PTR
)TRUE
;
615 switch (LOWORD(wParam
))
619 ShowWindow(hDlg
, SW_HIDE
);
620 return (INT_PTR
)TRUE
;
622 case IDC_BUTTON_HELP
:
624 MessageBox(hDlg
, TEXT("Magnifier help not available yet!") , TEXT("Help") , MB_OK
);
627 if (HIWORD(wParam
) == CBN_SELCHANGE
)
629 HWND hCombo
= GetDlgItem(hDlg
,IDC_ZOOM
);
631 /* Get index of current selection and the text of that selection */
632 iZoom
= SendMessage( hCombo
, CB_GETCURSEL
, (WPARAM
) wParam
, (LPARAM
) lParam
) + 1;
634 /* Update the magnifier UI */
638 case IDC_INVERTCOLORSCHECK
:
639 bInvertColors
= IsDlgButtonChecked(hDlg
, IDC_INVERTCOLORSCHECK
);
642 case IDC_FOLLOWMOUSECHECK
:
643 bFollowMouse
= IsDlgButtonChecked(hDlg
, IDC_FOLLOWMOUSECHECK
);
645 case IDC_FOLLOWKEYBOARDCHECK
:
646 bFollowFocus
= IsDlgButtonChecked(hDlg
, IDC_FOLLOWKEYBOARDCHECK
);
648 case IDC_FOLLOWTEXTEDITINGCHECK
:
649 bFollowCaret
= IsDlgButtonChecked(hDlg
, IDC_FOLLOWTEXTEDITINGCHECK
);
651 case IDC_STARTMINIMIZEDCHECK
:
652 bStartMinimized
= IsDlgButtonChecked(hDlg
, IDC_STARTMINIMIZEDCHECK
);
654 case IDC_SHOWMAGNIFIER
:
655 bShowMagnifier
= IsDlgButtonChecked(hDlg
, IDC_SHOWMAGNIFIERCHECK
);
657 ShowWindow (hMainWnd
, SW_SHOW
);
659 ShowWindow (hMainWnd
, SW_HIDE
);
664 return (INT_PTR
)FALSE
;
667 INT_PTR CALLBACK
WarningProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
669 UNREFERENCED_PARAMETER(lParam
);
674 return (INT_PTR
)TRUE
;
677 switch (LOWORD(wParam
))
679 case IDC_SHOWWARNINGCHECK
:
680 bShowWarning
= !IsDlgButtonChecked(hDlg
, IDC_SHOWWARNINGCHECK
);
683 if (LOWORD(wParam
) == IDOK
|| LOWORD(wParam
) == IDCANCEL
)
685 EndDialog(hDlg
, LOWORD(wParam
));
686 return (INT_PTR
)TRUE
;
691 return (INT_PTR
)FALSE
;