1 /*
2  * PROJECT:     ReactOS Magnify
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Magnification of parts of the screen.
5  * COPYRIGHT:   Copyright 2007-2019 Marc Piulachs <marc.piulachs@codexchange.net>
6  *              Copyright 2015-2019 David Quintana <gigaherz@gmail.com>
7  */
8 
9 /* TODO: Support AppBar types other than ABE_TOP */
10 
11 #include "magnifier.h"
12 
13 #include <winbase.h>
14 #include <winuser.h>
15 #include <wingdi.h>
16 #include <winnls.h>
17 #include <commctrl.h>
18 #include <shellapi.h>
19 #include <windowsx.h>
20 #include <stdlib.h>
21 #include <tchar.h>
22 
23 #include "resource.h"
24 
25 #define APPMSG_NOTIFYICON (WM_APP+1)
26 #define APPMSG_APPBAR     (WM_APP+2)
27 
28 const TCHAR szWindowClass[] = TEXT("MAGNIFIER");
29 
30 /* Global Variables */
31 HINSTANCE hInst;
32 HWND hMainWnd;
33 
34 #define MAX_LOADSTRING 100
35 TCHAR szTitle[MAX_LOADSTRING];
36 
37 #define TIMER_SPEED   1
38 #define REPAINT_SPEED 100
39 
40 DWORD lastTicks = 0;
41 
42 HWND hDesktopWindow = NULL;
43 
44 NOTIFYICONDATA nid;
45 HICON notifyIcon;
46 HMENU notifyMenu;
47 HWND  hOptionsDialog;
48 BOOL  bOptionsDialog = FALSE;
49 BOOL  bRecreateOffscreenDC = TRUE;
50 LONG  sourceWidth = 0;
51 LONG  sourceHeight = 0;
52 HDC     hdcOffscreen = NULL;
53 HBITMAP hbmpOffscreen = NULL;
54 HANDLE  hbmpOld;
55 POINT ptDragOffset;
56 INT nearEdge;
57 
58 /* Current magnified area */
59 POINT cp;
60 
61 /* Last positions */
62 POINT pMouse;
63 POINT pCaret;
64 POINT pFocus;
65 HWND pCaretWnd;
66 HWND pFocusWnd;
67 
68 ATOM                MyRegisterClass(HINSTANCE hInstance);
69 BOOL                InitInstance(HINSTANCE, int);
70 LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
71 INT_PTR CALLBACK    AboutProc(HWND, UINT, WPARAM, LPARAM);
72 INT_PTR CALLBACK    OptionsProc(HWND, UINT, WPARAM, LPARAM);
73 INT_PTR CALLBACK    WarningProc(HWND, UINT, WPARAM, LPARAM);
74 
75 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
76 {
77     MSG msg;
78     HACCEL hAccelTable;
79     INITCOMMONCONTROLSEX iccex;
80 
81     UNREFERENCED_PARAMETER(hPrevInstance);
82     UNREFERENCED_PARAMETER(lpCmdLine);
83 
84     switch (GetUserDefaultUILanguage())
85     {
86         case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
87             SetProcessDefaultLayout(LAYOUT_RTL);
88             break;
89 
90         default:
91             break;
92     }
93 
94     /* Initialize global strings */
95     LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
96     MyRegisterClass(hInstance);
97 
98     /* Perform application initialization */
99     if (!InitInstance(hInstance, nCmdShow))
100         return FALSE;
101 
102     hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MAGNIFIER));
103 
104     /* Main message loop */
105     while (GetMessage(&msg, NULL, 0, 0))
106     {
107         if (!TranslateAccelerator(hMainWnd, hAccelTable, &msg))
108         {
109             TranslateMessage(&msg);
110             DispatchMessage(&msg);
111         }
112     }
113 
114     /* Load the common controls */
115     iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
116     iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES;
117     InitCommonControlsEx(&iccex);
118 
119     SelectObject(hdcOffscreen, hbmpOld);
120     DeleteObject (hbmpOffscreen);
121     DeleteDC(hdcOffscreen);
122 
123     return (int) msg.wParam;
124 }
125 
126 ATOM MyRegisterClass(HINSTANCE hInstance)
127 {
128     WNDCLASS wc;
129 
130     wc.style            = CS_HREDRAW | CS_VREDRAW;
131     wc.lpfnWndProc      = WndProc;
132     wc.cbClsExtra       = 0;
133     wc.cbWndExtra       = 0;
134     wc.hInstance        = hInstance;
135     wc.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
136     wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
137     wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
138     wc.lpszMenuName     = NULL; //MAKEINTRESOURCE(IDC_MAGNIFIER);
139     wc.lpszClassName    = szWindowClass;
140 
141     return RegisterClass(&wc);
142 }
143 
144 void DoAppBarStuff(DWORD mode)
145 {
146     UINT uState;
147     APPBARDATA data = {0};
148     data.cbSize = sizeof(data);
149     data.hWnd = hMainWnd;
150     data.uCallbackMessage = APPMSG_APPBAR;
151 
152     if (mode == ABM_NEW || mode == ABM_SETPOS)
153     {
154         HWND hWndOrder = HWND_BOTTOM;
155         int rcw, rch;
156         RECT rcWorkArea;
157         SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
158 
159         if (mode == ABM_NEW)
160         {
161             SHAppBarMessage(ABM_NEW, &data);
162 
163             switch(AppBarConfig.uEdge)
164             {
165                 case ABE_LEFT:
166                     data.rc.top = rcWorkArea.top;
167                     data.rc.bottom = rcWorkArea.bottom;
168                     data.rc.left = rcWorkArea.left;
169                     data.rc.right = data.rc.left + AppBarConfig.appBarSizes.left;
170                     break;
171                 case ABE_TOP:
172                     data.rc.left = rcWorkArea.left;
173                     data.rc.right = rcWorkArea.right;
174                     data.rc.top = rcWorkArea.top;
175                     data.rc.bottom = data.rc.top + AppBarConfig.appBarSizes.top;
176                     break;
177                 case ABE_RIGHT:
178                     data.rc.top = rcWorkArea.top;
179                     data.rc.bottom = rcWorkArea.bottom;
180                     data.rc.right = rcWorkArea.left;
181                     data.rc.left = data.rc.right - AppBarConfig.appBarSizes.right;
182                     break;
183                 case ABE_BOTTOM:
184                     data.rc.left = rcWorkArea.left;
185                     data.rc.right = rcWorkArea.right;
186                     data.rc.bottom = rcWorkArea.bottom;
187                     data.rc.top = data.rc.bottom - AppBarConfig.appBarSizes.bottom;
188                     break;
189             }
190         }
191         else
192         {
193             GetWindowRect(hMainWnd, &data.rc);
194         }
195 
196         data.uEdge = AppBarConfig.uEdge;
197         uState = SHAppBarMessage(ABM_QUERYPOS, &data);
198         uState = SHAppBarMessage(ABM_SETPOS, &data);
199 
200         rcw = data.rc.right-data.rc.left;
201         rch = data.rc.bottom-data.rc.top;
202 
203         uState = SHAppBarMessage(ABM_GETSTATE, &data);
204         if (uState & ABS_ALWAYSONTOP)
205             hWndOrder = HWND_TOPMOST;
206 
207         SetWindowPos(hMainWnd, hWndOrder, data.rc.left, data.rc.top, rcw, rch, SWP_SHOWWINDOW | SWP_NOCOPYBITS);
208 
209     }
210     else if (mode == ABM_GETSTATE)
211     {
212         HWND hWndOrder = HWND_BOTTOM;
213         uState = SHAppBarMessage(ABM_GETSTATE, &data);
214         if (uState & ABS_ALWAYSONTOP)
215             hWndOrder = HWND_TOPMOST;
216         SetWindowPos(hMainWnd, hWndOrder, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
217     }
218     else if (mode == ABM_ACTIVATE)
219     {
220         SHAppBarMessage(ABM_ACTIVATE, &data);
221     }
222     else if (mode == ABM_WINDOWPOSCHANGED)
223     {
224         SHAppBarMessage(ABM_WINDOWPOSCHANGED, &data);
225     }
226     else if (mode == ABM_REMOVE)
227     {
228         SHAppBarMessage(ABM_REMOVE, &data);
229     }
230 }
231 
232 void AttachAppBar(INT uEdge)
233 {
234     if (AppBarConfig.uEdge == uEdge)
235         return;
236 
237     if (AppBarConfig.uEdge < 0 && uEdge >= 0)
238     {
239         SetWindowLongPtr(hMainWnd, GWL_STYLE, GetWindowLongPtr(hMainWnd, GWL_STYLE) & (~WS_CAPTION));
240     }
241     else if (uEdge < 0 && AppBarConfig.uEdge >= 0)
242     {
243         SetWindowLongPtr(hMainWnd, GWL_STYLE, GetWindowLongPtr(hMainWnd, GWL_STYLE) | WS_CAPTION);
244         SetWindowPos(hMainWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
245     }
246 
247     if (AppBarConfig.uEdge >= 0)
248     {
249         DoAppBarStuff(ABM_REMOVE);
250     }
251 
252     if (uEdge >= 0)
253     {
254         AppBarConfig.uEdge = uEdge;
255         DoAppBarStuff(ABM_NEW);
256     }
257     else
258     {
259         RECT rc = AppBarConfig.rcFloating;
260         SetWindowPos(hMainWnd, HWND_TOPMOST, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0);
261     }
262 
263     AppBarConfig.uEdge = uEdge;
264 }
265 
266 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
267 {
268     RECT rc;
269     DWORD exStyles = WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT;
270     DWORD dwStyles = WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
271 
272     /* Load settings from registry */
273     LoadSettings();
274 
275     rc = AppBarConfig.rcFloating;
276 
277     hInst = hInstance; // Store instance handle in our global variable
278 
279     if (AppBarConfig.uEdge <0 )
280     {
281         dwStyles |= WS_CAPTION;
282         exStyles |= WS_EX_TOPMOST;
283     }
284 
285     /* Create the Window */
286     hMainWnd = CreateWindowEx(exStyles,
287                               szWindowClass,
288                               szTitle,
289                               dwStyles,
290                               rc.left,
291                               rc.top,
292                               rc.right-rc.left,
293                               rc.bottom-rc.top,
294                               NULL,
295                               NULL,
296                               hInstance,
297                               NULL);
298     if (!hMainWnd)
299         return FALSE;
300 
301     if (AppBarConfig.uEdge >= 0)
302         DoAppBarStuff(ABM_NEW);
303     else
304         SetWindowPos(hMainWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
305 
306     // In Windows 2003's Magnifier, the "Start Minimized" setting
307     // refers exclusively to the options dialog, not the main window itself.
308     hOptionsDialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOGOPTIONS), hMainWnd, OptionsProc);
309     ShowWindow(hOptionsDialog, (bStartMinimized ? SW_HIDE : SW_SHOW));
310 
311     if (bShowWarning)
312         DialogBox(hInstance, MAKEINTRESOURCE(IDD_WARNINGDIALOG), hMainWnd, WarningProc);
313 
314     return TRUE;
315 }
316 
317 void Refresh(void)
318 {
319     if (!IsIconic(hMainWnd))
320     {
321         /* Invalidate the client area forcing a WM_PAINT message */
322         InvalidateRgn(hMainWnd, NULL, TRUE);
323     }
324 }
325 
326 void GetBestOverlapWithMonitors(LPRECT rect)
327 {
328     int rcLeft, rcTop;
329     int rcWidth, rcHeight;
330     RECT rcMon;
331     HMONITOR hMon = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST);
332     MONITORINFO info;
333     info.cbSize = sizeof(info);
334 
335     GetMonitorInfo(hMon, &info);
336 
337     rcMon = info.rcMonitor;
338 
339     rcLeft = rect->left;
340     rcTop = rect->top;
341     rcWidth  = (rect->right - rect->left);
342     rcHeight = (rect->bottom - rect->top);
343 
344     if (rcLeft < rcMon.left)
345        rcLeft = rcMon.left;
346 
347     if (rcTop < rcMon.top)
348        rcTop = rcMon.top;
349 
350     if (rcLeft > (rcMon.right - rcWidth))
351         rcLeft = (rcMon.right - rcWidth);
352 
353     if (rcTop > (rcMon.bottom - rcHeight))
354         rcTop = (rcMon.bottom - rcHeight);
355 
356     OffsetRect(rect, (rcLeft-rect->left), (rcTop-rect->top));
357 }
358 
359 void Draw(HDC aDc)
360 {
361     HDC desktopHdc = NULL;
362 
363     RECT sourceRect, intersectedRect;
364     RECT targetRect, appRect;
365     DWORD rop = SRCCOPY;
366     CURSORINFO cinfo;
367     ICONINFO iinfo;
368 
369     int AppWidth, AppHeight;
370 
371     if (bInvertColors)
372         rop = NOTSRCCOPY;
373 
374     desktopHdc = GetDC(0);
375 
376     GetClientRect(hMainWnd, &appRect);
377     AppWidth  = (appRect.right - appRect.left);
378     AppHeight = (appRect.bottom - appRect.top);
379 
380     ZeroMemory(&cinfo, sizeof(cinfo));
381     ZeroMemory(&iinfo, sizeof(iinfo));
382     cinfo.cbSize = sizeof(cinfo);
383     GetCursorInfo(&cinfo);
384     GetIconInfo(cinfo.hCursor, &iinfo);
385 
386     targetRect = appRect;
387     ClientToScreen(hMainWnd, (POINT*)&targetRect.left);
388     ClientToScreen(hMainWnd, (POINT*)&targetRect.right);
389 
390     if (bRecreateOffscreenDC || !hdcOffscreen)
391     {
392         bRecreateOffscreenDC = FALSE;
393 
394         if (hdcOffscreen)
395         {
396             SelectObject(hdcOffscreen, hbmpOld);
397             DeleteObject (hbmpOffscreen);
398             DeleteDC(hdcOffscreen);
399         }
400 
401         sourceWidth  = AppWidth / uiZoom;
402         sourceHeight = AppHeight / uiZoom;
403 
404          /* Create a memory DC compatible with client area DC */
405         hdcOffscreen = CreateCompatibleDC(desktopHdc);
406 
407         /* Create a bitmap compatible with the client area DC */
408         hbmpOffscreen = CreateCompatibleBitmap(desktopHdc,
409                                                sourceWidth,
410                                                sourceHeight);
411 
412         /* Select our bitmap in memory DC and save the old one */
413         hbmpOld = SelectObject(hdcOffscreen, hbmpOffscreen);
414     }
415 
416     GetWindowRect(hDesktopWindow, &sourceRect);
417     sourceRect.left = (cp.x) - (sourceWidth /2);
418     sourceRect.top = (cp.y) - (sourceHeight /2);
419     sourceRect.right = sourceRect.left + sourceWidth;
420     sourceRect.bottom = sourceRect.top + sourceHeight;
421 
422     GetBestOverlapWithMonitors(&sourceRect);
423 
424     /* Paint the screen bitmap to our in memory DC */
425     BitBlt(hdcOffscreen,
426            0,
427            0,
428            sourceWidth,
429            sourceHeight,
430            desktopHdc,
431            sourceRect.left,
432            sourceRect.top,
433            rop);
434 
435     if (IntersectRect(&intersectedRect, &sourceRect, &targetRect))
436     {
437         OffsetRect(&intersectedRect, -sourceRect.left, -sourceRect.top);
438         FillRect(hdcOffscreen, &intersectedRect, GetStockObject(DC_BRUSH));
439     }
440 
441     /* Draw the mouse pointer in the right position */
442     DrawIcon(hdcOffscreen,
443              pMouse.x - iinfo.xHotspot - sourceRect.left, // - 10,
444              pMouse.y - iinfo.yHotspot - sourceRect.top, // - 10,
445              cinfo.hCursor);
446 
447     /* Blast the stretched image from memory DC to window DC */
448     StretchBlt(aDc,
449                0,
450                0,
451                AppWidth,
452                AppHeight,
453                hdcOffscreen,
454                0,
455                0,
456                sourceWidth,
457                sourceHeight,
458                SRCCOPY | NOMIRRORBITMAP);
459 
460     /* Cleanup */
461     if (iinfo.hbmMask)
462         DeleteObject(iinfo.hbmMask);
463     if (iinfo.hbmColor)
464         DeleteObject(iinfo.hbmColor);
465     ReleaseDC(hDesktopWindow, desktopHdc);
466 }
467 
468 void HandleNotifyIconMessage(HWND hWnd, WPARAM wParam, LPARAM lParam)
469 {
470     POINT pt;
471 
472     switch(lParam)
473     {
474         case WM_LBUTTONUP:
475             PostMessage(hMainWnd, WM_COMMAND, IDM_OPTIONS, 0);
476             break;
477         case WM_RBUTTONUP:
478             GetCursorPos(&pt);
479             TrackPopupMenu(notifyMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
480             break;
481     }
482 }
483 
484 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
485 {
486     int wmId;
487 
488     switch (message)
489     {
490         case WM_TIMER:
491         {
492             BOOL hasMoved = FALSE;
493 
494             GUITHREADINFO guiInfo;
495             guiInfo.cbSize = sizeof(guiInfo);
496 
497             GetGUIThreadInfo(0, &guiInfo);
498 
499             if (bFollowMouse)
500             {
501                 POINT pNewMouse;
502 
503                 //Get current mouse position
504                 GetCursorPos (&pNewMouse);
505 
506 #define PointsAreEqual(pt1, pt2) (((pt1).x == (pt2).x) && ((pt1).y == (pt2).y))
507 
508                 //If mouse has moved ...
509                 if (!PointsAreEqual(pMouse, pNewMouse))
510                 {
511                     //Update to new position
512                     pMouse = pNewMouse;
513                     cp = pNewMouse;
514                     hasMoved = TRUE;
515                 }
516             }
517 
518             if (guiInfo.hwndActive != hMainWnd)
519             {
520                 if (bFollowCaret)
521                 {
522                     if (guiInfo.hwndCaret)
523                     {
524                         POINT ptCaret;
525                         ptCaret.x = (guiInfo.rcCaret.left + guiInfo.rcCaret.right) / 2;
526                         ptCaret.y = (guiInfo.rcCaret.top + guiInfo.rcCaret.bottom) / 2;
527 
528                         if ((pCaretWnd != guiInfo.hwndCaret) || !PointsAreEqual(pCaret, ptCaret))
529                         {
530                             //Update to new position
531                             pCaret = ptCaret;
532                             pCaretWnd = guiInfo.hwndCaret;
533                             if (!hasMoved)
534                             {
535                                 ClientToScreen (guiInfo.hwndCaret, (LPPOINT) &ptCaret);
536                                 cp = ptCaret;
537                                 hasMoved = TRUE;
538                             }
539                         }
540                     }
541                     else
542                     {
543                         pCaretWnd = NULL;
544                     }
545                 }
546 
547                 if (bFollowFocus)
548                 {
549                     if (guiInfo.hwndFocus && !guiInfo.hwndCaret)
550                     {
551                         POINT ptFocus;
552                         RECT activeRect;
553 
554                         //Get current control focus
555                         GetWindowRect(guiInfo.hwndFocus, &activeRect);
556                         ptFocus.x = (activeRect.left + activeRect.right) / 2;
557                         ptFocus.y = (activeRect.top + activeRect.bottom) / 2;
558 
559                         if ((guiInfo.hwndFocus != pFocusWnd) || !PointsAreEqual(pFocus, ptFocus))
560                         {
561                             //Update to new position
562                             pFocus = ptFocus;
563                             pFocusWnd = guiInfo.hwndFocus;
564                             if (!hasMoved)
565                             {
566                                 cp = ptFocus;
567                                 hasMoved = TRUE;
568                             }
569                         }
570                     }
571                     else
572                     {
573                         pFocusWnd = NULL;
574                     }
575                 }
576             }
577 
578             if (!hasMoved)
579             {
580                 DWORD newTicks = GetTickCount();
581                 DWORD elapsed = (newTicks - lastTicks);
582                 if (elapsed > REPAINT_SPEED)
583                 {
584                     hasMoved = TRUE;
585                 }
586             }
587 
588             if (hasMoved)
589             {
590                 lastTicks = GetTickCount();
591                 Refresh();
592             }
593 
594             return 0;
595         }
596 
597         case WM_COMMAND:
598         {
599             wmId = LOWORD(wParam);
600             /* Parse the menu selections */
601             switch (wmId)
602             {
603                 case IDM_OPTIONS:
604                     ShowWindow(hOptionsDialog, (bOptionsDialog ? SW_HIDE : SW_SHOW));
605                     break;
606                 case IDM_ABOUT:
607                     DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutProc);
608                     break;
609                 case IDM_EXIT:
610                     DestroyWindow(hWnd);
611                     break;
612                 default:
613                     return DefWindowProc(hWnd, message, wParam, lParam);
614             }
615             return 0;
616         }
617 
618         case WM_PAINT:
619         {
620             PAINTSTRUCT PaintStruct;
621             HDC dc;
622             dc = BeginPaint(hWnd, &PaintStruct);
623             Draw(dc);
624             EndPaint(hWnd, &PaintStruct);
625             return 0;
626         }
627 
628         case WM_CONTEXTMENU:
629             TrackPopupMenu(notifyMenu, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hWnd, NULL);
630             return 0;
631 
632         case WM_LBUTTONDOWN:
633         {
634             RECT rc;
635             POINT pt;
636             SetCapture(hWnd);
637 
638             GetCursorPos(&pt);
639             GetWindowRect(hWnd, &rc);
640             ptDragOffset.x = pt.x - rc.left;
641             ptDragOffset.y = pt.y - rc.top;
642 
643             nearEdge = AppBarConfig.uEdge;
644 
645             break;
646         }
647 
648         case WM_MOUSEMOVE:
649             if (GetCapture() == hWnd)
650             {
651                 RECT rc;
652                 POINT pt;
653                 RECT rcWorkArea;
654                 SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
655                 GetCursorPos(&pt);
656                 GetWindowRect(hWnd, &rc);
657 
658                 if (AppBarConfig.uEdge >= 0)
659                 {
660                     if (pt.x >= rcWorkArea.left && pt.x <= rcWorkArea.right &&
661                         pt.y >= rcWorkArea.top && pt.y <= rcWorkArea.bottom)
662                     {
663                         AttachAppBar(-2);
664 
665                         // Fixup offset
666                         GetWindowRect(hWnd, &rc);
667                         ptDragOffset.x = (rc.right-rc.left)/2;
668                         ptDragOffset.y = 2;
669 
670                         rc.left = pt.x - ptDragOffset.x;
671                         rc.top = pt.y - ptDragOffset.y;
672 
673                         SetWindowPos(hWnd, HWND_TOPMOST, rc.left, rc.top, 0, 0, SWP_NOSIZE);
674                     }
675                 }
676                 else
677                 {
678                     if (pt.x <= rcWorkArea.left+8 && nearEdge != ABE_LEFT)
679                     {
680                         AttachAppBar(ABE_LEFT);
681                         nearEdge = ABE_LEFT;
682                     }
683                     else if (pt.y <= rcWorkArea.top+8 && nearEdge != ABE_TOP)
684                     {
685                         AttachAppBar(ABE_TOP);
686                         nearEdge = ABE_TOP;
687                     }
688                     else if (pt.x >= rcWorkArea.right-8 && nearEdge != ABE_RIGHT)
689                     {
690                         AttachAppBar(ABE_RIGHT);
691                         nearEdge = ABE_RIGHT;
692                     }
693                     else if (pt.y >= rcWorkArea.bottom-8 && nearEdge != ABE_BOTTOM)
694                     {
695                         AttachAppBar(ABE_BOTTOM);
696                         nearEdge = ABE_BOTTOM;
697                     }
698                     else
699                     {
700                         rc.left = pt.x - ptDragOffset.x;
701                         rc.top = pt.y - ptDragOffset.y;
702 
703                         SetWindowPos(hWnd, HWND_TOPMOST, rc.left, rc.top, 0, 0, SWP_NOSIZE);
704                         nearEdge = -1;
705                     }
706                 }
707 
708                 pMouse = pt;
709                 Refresh();
710             }
711             break;
712 
713         case WM_LBUTTONUP:
714             if (GetCapture() == hWnd)
715             {
716                 if (AppBarConfig.uEdge >= 0)
717                     DoAppBarStuff(ABM_GETSTATE);
718                 else
719                     SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
720                 ReleaseCapture();
721             }
722             break;
723 
724         case WM_SIZE:
725             if (AppBarConfig.uEdge >= 0)
726                 DoAppBarStuff(ABM_SETPOS);
727             /* fallthrough */
728         case WM_DISPLAYCHANGE:
729             bRecreateOffscreenDC = TRUE;
730             Refresh();
731             break;
732 
733         case WM_ERASEBKGND:
734             // handle WM_ERASEBKGND by simply returning non-zero because we did all the drawing in WM_PAINT.
735             return 0;
736 
737         case WM_DESTROY:
738         {
739             if (AppBarConfig.uEdge >= 0)
740                 DoAppBarStuff(ABM_REMOVE);
741 
742             KillTimer(hWnd, 1);
743 
744             /* Save settings to registry */
745             SaveSettings();
746 
747             /* Cleanup notification icon */
748             ZeroMemory(&nid, sizeof(nid));
749             nid.cbSize = sizeof(nid);
750             nid.uFlags = NIF_MESSAGE;
751             nid.hWnd = hWnd;
752             nid.uCallbackMessage = APPMSG_NOTIFYICON;
753             Shell_NotifyIcon(NIM_DELETE, &nid);
754             DestroyIcon(notifyIcon);
755 
756             DestroyWindow(hOptionsDialog);
757 
758             PostQuitMessage(0);
759             return 0;
760         }
761 
762         case WM_CREATE:
763         {
764             HMENU tempMenu;
765 
766             /* Get the desktop window */
767             hDesktopWindow = GetDesktopWindow();
768 
769             /* Set the timer */
770             SetTimer(hWnd, 1, TIMER_SPEED, NULL);
771 
772             /* Notification icon */
773             notifyIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0);
774 
775             ZeroMemory(&nid, sizeof(nid));
776             nid.cbSize = sizeof(nid);
777             nid.uFlags = NIF_ICON | NIF_MESSAGE;
778             nid.hWnd = hWnd;
779             nid.uCallbackMessage = APPMSG_NOTIFYICON;
780             nid.hIcon = notifyIcon;
781             Shell_NotifyIcon(NIM_ADD, &nid);
782 
783             tempMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDC_MAGNIFIER));
784             notifyMenu = GetSubMenu(tempMenu, 0);
785             RemoveMenu(tempMenu, 0, MF_BYPOSITION);
786             DestroyMenu(tempMenu);
787             return 0;
788         }
789 
790         case APPMSG_APPBAR:
791         {
792             switch (wParam)
793             {
794                 case ABN_STATECHANGE:
795                     DoAppBarStuff(ABM_GETSTATE);
796                     break;
797                 case ABN_POSCHANGED:
798                     DoAppBarStuff(ABM_SETPOS);
799                     break;
800                 case ABN_FULLSCREENAPP:
801                 {
802                     if (!lParam)
803                     {
804                         DoAppBarStuff(ABM_GETSTATE);
805                         break;
806                     }
807 
808                     SetWindowPos(hMainWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
809                     break;
810                 }
811                case ABN_WINDOWARRANGE:
812                     ShowWindow(hMainWnd, (lParam ? SW_HIDE : SW_SHOW));
813                     break;
814             }
815             return 0;
816         }
817 
818         case APPMSG_NOTIFYICON:
819             HandleNotifyIconMessage(hWnd, wParam, lParam);
820             return 0;
821 
822         case WM_ACTIVATE:
823             if (AppBarConfig.uEdge >= 0)
824                 DoAppBarStuff(ABM_ACTIVATE);
825             break;
826 
827         case WM_WINDOWPOSCHANGED:
828             if (AppBarConfig.uEdge >= 0)
829                 DoAppBarStuff(ABM_WINDOWPOSCHANGED);
830             Refresh();
831             break;
832 
833         default:
834             break;
835     }
836 
837     return DefWindowProc(hWnd, message, wParam, lParam);
838 }
839 
840 INT_PTR CALLBACK AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
841 {
842     UNREFERENCED_PARAMETER(lParam);
843     switch (message)
844     {
845         case WM_INITDIALOG:
846             return (INT_PTR)TRUE;
847 
848         case WM_COMMAND:
849             if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
850             {
851                 EndDialog(hDlg, LOWORD(wParam));
852                 return (INT_PTR)TRUE;
853             }
854             break;
855     }
856 
857     return (INT_PTR)FALSE;
858 }
859 
860 INT_PTR CALLBACK OptionsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
861 {
862     UNREFERENCED_PARAMETER(lParam);
863 
864     switch (message)
865     {
866         case WM_INITDIALOG:
867         {
868             /* Add the zoom items */
869             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("1"));
870             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("2"));
871             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("3"));
872             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("4"));
873             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("5"));
874             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("6"));
875             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("7"));
876             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("8"));
877             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("9"));
878 
879             if (uiZoom >= 1 && uiZoom <= 9)
880                 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_SETCURSEL, uiZoom - 1, 0);
881 
882             if (bFollowMouse)
883                 SendDlgItemMessage(hDlg,IDC_FOLLOWMOUSECHECK,BM_SETCHECK, wParam, 0);
884 
885             if (bFollowFocus)
886                 SendDlgItemMessage(hDlg,IDC_FOLLOWKEYBOARDCHECK,BM_SETCHECK, wParam, 0);
887 
888             if (bFollowCaret)
889                 SendDlgItemMessage(hDlg,IDC_FOLLOWTEXTEDITINGCHECK,BM_SETCHECK, wParam, 0);
890 
891             if (bInvertColors)
892                 SendDlgItemMessage(hDlg,IDC_INVERTCOLORSCHECK,BM_SETCHECK, wParam, 0);
893 
894             if (bStartMinimized)
895                 SendDlgItemMessage(hDlg,IDC_STARTMINIMIZEDCHECK,BM_SETCHECK, wParam, 0);
896 
897             if (bShowMagnifier)
898                 SendDlgItemMessage(hDlg,IDC_SHOWMAGNIFIERCHECK,BM_SETCHECK, wParam, 0);
899 
900             return (INT_PTR)TRUE;
901         }
902 
903         case WM_SHOWWINDOW:
904             bOptionsDialog = wParam;
905             break;
906 
907         case WM_COMMAND:
908         switch (LOWORD(wParam))
909         {
910             case IDOK:
911             case IDCANCEL:
912                 ShowWindow(hDlg, SW_HIDE);
913                 return (INT_PTR)TRUE;
914 
915             case IDC_BUTTON_HELP:
916                 /* Unimplemented */
917                 MessageBox(hDlg, TEXT("Magnifier help not available yet!"), TEXT("Help"), MB_OK);
918                 break;
919 
920             case IDC_ZOOM:
921                 if (HIWORD(wParam) == CBN_SELCHANGE)
922                 {
923                     HWND hCombo = GetDlgItem(hDlg,IDC_ZOOM);
924                     TCHAR currentZoomValue[2] = TEXT("");
925 
926                     /* Get index of current selection and the text of that selection */
927                     int currentSelectionIndex = ComboBox_GetCurSel(hCombo);
928                     ComboBox_GetLBText(hCombo, currentSelectionIndex, currentZoomValue);
929                     uiZoom = (UINT)_ttoi(currentZoomValue);
930                     /* The zoom factor cannot be smaller than 1 (and not zero) or greater than 9 */
931                     if (uiZoom < 1 || uiZoom > 9)
932                         uiZoom = 1;
933 
934                     /* Trigger the Draw function to rezoom (which will be set false automatically after rezooming) */
935                     bRecreateOffscreenDC = TRUE;
936 
937                     /* Update the magnifier UI */
938                     Refresh();
939                 }
940                 break;
941 
942             case IDC_INVERTCOLORSCHECK:
943                 bInvertColors = IsDlgButtonChecked(hDlg, IDC_INVERTCOLORSCHECK);
944                 Refresh();
945                 break;
946             case IDC_FOLLOWMOUSECHECK:
947                 bFollowMouse = IsDlgButtonChecked(hDlg, IDC_FOLLOWMOUSECHECK);
948                 break;
949             case IDC_FOLLOWKEYBOARDCHECK:
950                 bFollowFocus = IsDlgButtonChecked(hDlg, IDC_FOLLOWKEYBOARDCHECK);
951                 break;
952             case IDC_FOLLOWTEXTEDITINGCHECK:
953                 bFollowCaret = IsDlgButtonChecked(hDlg, IDC_FOLLOWTEXTEDITINGCHECK);
954                 break;
955             case IDC_STARTMINIMIZEDCHECK:
956                 bStartMinimized = IsDlgButtonChecked(hDlg, IDC_STARTMINIMIZEDCHECK);
957                 break;
958             case IDC_SHOWMAGNIFIER:
959                 bShowMagnifier = IsDlgButtonChecked(hDlg, IDC_SHOWMAGNIFIERCHECK);
960                 ShowWindow(hMainWnd, (bShowMagnifier ? SW_SHOW : SW_HIDE));
961                 break;
962         }
963     }
964 
965     return (INT_PTR)FALSE;
966 }
967 
968 INT_PTR CALLBACK WarningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
969 {
970     UNREFERENCED_PARAMETER(lParam);
971 
972     switch (message)
973     {
974         case WM_INITDIALOG:
975             return (INT_PTR)TRUE;
976 
977         case WM_COMMAND:
978             switch (LOWORD(wParam))
979             {
980                 case IDC_SHOWWARNINGCHECK:
981                     bShowWarning = !IsDlgButtonChecked(hDlg, IDC_SHOWWARNINGCHECK);
982                     break;
983             }
984             if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
985             {
986                 EndDialog(hDlg, LOWORD(wParam));
987                 return (INT_PTR)TRUE;
988             }
989             break;
990     }
991 
992     return (INT_PTR)FALSE;
993 }
994