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