1 /*
2  * PROJECT:     ReactOS Task Manager
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Graph plotting controls
5  * COPYRIGHT:   Copyright 2002 Robert Dickenson <robd@reactos.org>
6  *              Copyright 2021 Wu Haotian <rigoligo03@gmail.com>
7  */
8 
9 #include "precomp.h"
10 
11 #include <math.h>
12 
13 WNDPROC OldGraphCtrlWndProc;
14 
15 BOOL
16 GraphCtrl_Create(PTM_GRAPH_CONTROL inst, HWND hWnd, HWND hParentWnd, PTM_FORMAT fmt)
17 {
18     HDC     hdc, hdcg;
19     HBITMAP hbmOld;
20     UINT    Size;
21     INT     p;
22     RECT    rc;
23 
24     inst->hParentWnd = hParentWnd;
25     inst->hWnd = hWnd;
26 
27     Size = GetSystemMetrics(SM_CXSCREEN);
28     inst->BitmapWidth = Size;
29     Size /= PLOT_SHIFT;
30     inst->PointBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size * NUM_PLOTS);
31     if (!inst->PointBuffer)
32     {
33         goto fail;
34     }
35 
36     inst->NumberOfPoints = Size;
37     inst->CurrIndex = 0;
38 
39     /* Styling */
40     inst->hPenGrid = CreatePen(PS_SOLID, 0, fmt->clrGrid);
41     inst->hPen0 = CreatePen(PS_SOLID, 0, fmt->clrPlot0);
42     inst->hPen1 = CreatePen(PS_SOLID, 0, fmt->clrPlot1);
43     inst->hBrushBack = CreateSolidBrush(fmt->clrBack);
44 
45     if (!inst->hPenGrid ||
46         !inst->hPen0 ||
47         !inst->hPen1 ||
48         !inst->hBrushBack)
49     {
50         goto fail;
51     }
52 
53     if (fmt->GridCellWidth >= PLOT_SHIFT << 2)
54         inst->GridCellWidth = fmt->GridCellWidth;
55     else
56         inst->GridCellWidth = PLOT_SHIFT << 2;
57     if (fmt->GridCellHeight >= PLOT_SHIFT << 2)
58         inst->GridCellHeight = fmt->GridCellHeight;
59     else
60         inst->GridCellHeight = PLOT_SHIFT << 2;
61 
62     inst->DrawSecondaryPlot = fmt->DrawSecondaryPlot;
63 
64     GetClientRect(hWnd, &rc);
65     inst->BitmapHeight = rc.bottom;
66     inst->ftPixelsPerPercent = (FLOAT)(inst->BitmapHeight) / 100.00f;
67 
68     hdc = GetDC(hParentWnd);
69     hdcg = CreateCompatibleDC(hdc);
70     inst->hdcGraph = hdcg;
71     inst->hbmGraph = CreateCompatibleBitmap(hdc, inst->BitmapWidth, inst->BitmapHeight);
72 
73     if (!hdc ||
74         !hdcg ||
75         !inst->hbmGraph)
76     {
77         goto fail;
78     }
79 
80     ReleaseDC(hParentWnd, hdc);
81     hbmOld = (HBITMAP)SelectObject(hdcg, inst->hbmGraph);
82     DeleteObject(hbmOld);
83 
84     SetBkColor(hdcg, fmt->clrBack);
85     rc.right = inst->BitmapWidth;
86     FillRect(hdcg, &rc, inst->hBrushBack);
87 
88     inst->CurrShift = 0;
89     SelectObject(hdcg, inst->hPenGrid);
90     for (p = inst->GridCellHeight - 1;
91          p < inst->BitmapHeight;
92          p += inst->GridCellHeight)
93     {
94         MoveToEx(hdcg, 0, p, NULL);
95         LineTo(hdcg, inst->BitmapWidth, p);
96     }
97     for (p = inst->BitmapWidth - 1;
98          p > 0;
99          p -= inst->GridCellWidth)
100     {
101         MoveToEx(hdcg, p, 0, NULL);
102         LineTo(hdcg, p, inst->BitmapHeight);
103     }
104     SelectObject(hdcg, inst->hPen0);
105 
106     return TRUE;
107 
108 fail:
109     GraphCtrl_Dispose(inst);
110     return FALSE;
111 }
112 
113 void
114 GraphCtrl_Dispose(PTM_GRAPH_CONTROL inst)
115 {
116     if (inst->PointBuffer)
117         HeapFree(GetProcessHeap(), 0, inst->PointBuffer);
118 
119     if (inst->hPenGrid)
120         DeleteObject(inst->hPenGrid);
121 
122     if (inst->hPen0)
123         DeleteObject(inst->hPen0);
124 
125     if (inst->hPen1)
126         DeleteObject(inst->hPen1);
127 
128     if (inst->hBrushBack)
129         DeleteObject(inst->hBrushBack);
130 
131     if (inst->hbmGraph)
132         DeleteObject(inst->hbmGraph);
133 
134     if (inst->hdcGraph)
135         DeleteObject(inst->hdcGraph);
136 }
137 
138 void
139 GraphCtrl_AddPoint(PTM_GRAPH_CONTROL inst, BYTE val0, BYTE val1)
140 {
141     HDC    hdcg;
142     PBYTE  t;
143     RECT   rcDirt;
144 
145     UINT   Prev0, Prev1, RetainingWidth;
146     INT    PrevY, CurrY, p, v;
147 
148     hdcg = inst->hdcGraph;
149     RetainingWidth = inst->BitmapWidth - PLOT_SHIFT;
150     t = inst->PointBuffer;
151     Prev0 = *(t + inst->CurrIndex);
152     Prev1 = *(t + inst->CurrIndex + inst->NumberOfPoints);
153     if (inst->CurrIndex < inst->NumberOfPoints - 1)
154     {
155         inst->CurrIndex++;
156     }
157     else
158     {
159         inst->CurrIndex = 0;
160     }
161     *(t + inst->CurrIndex) = val0;
162     *(t + inst->CurrIndex + inst->NumberOfPoints) = val1;
163 
164     /* Drawing points, first shifting the plot left */
165     BitBlt(hdcg, 0, 0, RetainingWidth, inst->BitmapHeight, hdcg, PLOT_SHIFT, 0, SRCCOPY);
166 
167     rcDirt.left = RetainingWidth;
168     rcDirt.top = 0;
169     rcDirt.right = inst->BitmapWidth;
170     rcDirt.bottom = inst->BitmapHeight;
171     FillRect(hdcg, &rcDirt, inst->hBrushBack);
172 
173     SelectObject(hdcg, inst->hPenGrid);
174     for (p = inst->GridCellHeight - 1;
175          p < inst->BitmapHeight;
176          p += inst->GridCellHeight)
177     {
178         MoveToEx(hdcg, RetainingWidth, p, NULL);
179         LineTo(hdcg, inst->BitmapWidth, p);
180     }
181     v = inst->CurrShift + PLOT_SHIFT;
182     if (v >= inst->GridCellWidth)
183     {
184         v -= inst->GridCellWidth;
185         p = inst->BitmapWidth - v - 1;
186         MoveToEx(hdcg, p, 0, NULL);
187         LineTo(hdcg, p, inst->BitmapHeight);
188     }
189     inst->CurrShift = v;
190 
191     if (inst->DrawSecondaryPlot)
192     {
193         SelectObject(inst->hdcGraph, inst->hPen1);
194 
195         PrevY = inst->BitmapHeight - Prev1 * inst->ftPixelsPerPercent;
196         MoveToEx(inst->hdcGraph, RetainingWidth - 1, PrevY, NULL);
197         CurrY = inst->BitmapHeight - val1 * inst->ftPixelsPerPercent;
198         LineTo(inst->hdcGraph, inst->BitmapWidth - 1, CurrY);
199     }
200 
201     SelectObject(inst->hdcGraph, inst->hPen0);
202     PrevY = inst->BitmapHeight - Prev0 * inst->ftPixelsPerPercent;
203     MoveToEx(inst->hdcGraph, RetainingWidth - 1, PrevY, NULL);
204     CurrY = inst->BitmapHeight - val0 * inst->ftPixelsPerPercent;
205     LineTo(inst->hdcGraph, inst->BitmapWidth - 1, CurrY);
206 }
207 
208 inline void
209 GraphCtrl_RedrawBitmap(PTM_GRAPH_CONTROL inst, INT h)
210 {
211     HDC    hdcg;
212     PBYTE  t;
213     RECT   rc;
214     INT    i, j, y, x, p;
215     FLOAT  coef;
216 
217     hdcg = inst->hdcGraph;
218     rc.left = 0; rc.top = 0;
219     rc.right = inst->BitmapWidth; rc.bottom = h;
220     FillRect(hdcg, &rc, inst->hBrushBack);
221 
222     SelectObject(hdcg, inst->hPenGrid);
223 
224     for (p = inst->GridCellHeight - 1;
225          p < inst->BitmapHeight;
226          p += inst->GridCellHeight)
227     {
228         MoveToEx(hdcg, 0, p, NULL);
229         LineTo(hdcg, inst->BitmapWidth, p);
230     }
231 
232     for (p = inst->BitmapWidth - inst->CurrShift - 1;
233          p > 0;
234          p -= inst->GridCellWidth)
235     {
236         MoveToEx(hdcg, p, 0, NULL);
237         LineTo(hdcg, p, inst->BitmapHeight);
238     }
239 
240     coef = inst->ftPixelsPerPercent;
241 
242     if (inst->DrawSecondaryPlot)
243     {
244         SelectObject(hdcg, inst->hPen1);
245         t = inst->PointBuffer + inst->NumberOfPoints;
246         x = inst->BitmapWidth - 1;
247         j = inst->CurrIndex;
248         y = h - *(t + j) * coef;
249         MoveToEx(hdcg, x, y, NULL);
250         for (i = 0; i < inst->NumberOfPoints; i++)
251         {
252             j = (j ? j : inst->NumberOfPoints) - 1;
253             y = h - *(t + j) * coef;
254             x -= PLOT_SHIFT;
255             LineTo(hdcg, x, y);
256         }
257     }
258 
259     SelectObject(hdcg, inst->hPen0);
260     t = inst->PointBuffer;
261     x = inst->BitmapWidth - 1;
262     j = inst->CurrIndex;
263     y = h - *(t + j) * coef;
264     MoveToEx(hdcg, x, y, NULL);
265 
266     for (i = 0; i < inst->NumberOfPoints; i++)
267     {
268         j = (j ? j : inst->NumberOfPoints) - 1;
269         y = h - *(t + j) * coef;
270         x -= PLOT_SHIFT;
271         LineTo(hdcg, x, y);
272     }
273 }
274 
275 inline void
276 GraphCtrl_RedrawOnHeightChange(PTM_GRAPH_CONTROL inst, INT nh)
277 {
278     HDC     hdc;
279     HBITMAP hbmOld;
280 
281     inst->BitmapHeight = nh;
282     inst->ftPixelsPerPercent = (FLOAT)nh / 100.00f;
283 
284     hdc = GetDC(inst->hParentWnd);
285     hbmOld = inst->hbmGraph;
286     inst->hbmGraph = CreateCompatibleBitmap(hdc, inst->BitmapWidth, nh);
287     SelectObject(inst->hdcGraph, inst->hbmGraph);
288     DeleteObject(hbmOld);
289     ReleaseDC(inst->hParentWnd, hdc);
290 
291     GraphCtrl_RedrawBitmap(inst, nh);
292 }
293 
294 extern TM_GRAPH_CONTROL PerformancePageCpuUsageHistoryGraph;
295 extern TM_GRAPH_CONTROL PerformancePageMemUsageHistoryGraph;
296 extern HWND hPerformancePageCpuUsageHistoryGraph;
297 extern HWND hPerformancePageMemUsageHistoryGraph;
298 
299 INT_PTR CALLBACK
300 GraphCtrl_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
301 {
302     PTM_GRAPH_CONTROL graph;
303 
304     switch (message)
305     {
306         case WM_ERASEBKGND:
307             return TRUE;
308         /*
309          *  Filter out mouse  & keyboard messages
310          */
311         // case WM_APPCOMMAND:
312         case WM_CAPTURECHANGED:
313         case WM_LBUTTONDBLCLK:
314         case WM_LBUTTONDOWN:
315         case WM_LBUTTONUP:
316         case WM_MBUTTONDBLCLK:
317         case WM_MBUTTONDOWN:
318         case WM_MBUTTONUP:
319         case WM_MOUSEACTIVATE:
320         case WM_MOUSEHOVER:
321         case WM_MOUSELEAVE:
322         case WM_MOUSEMOVE:
323         // case WM_MOUSEWHEEL:
324         case WM_NCHITTEST:
325         case WM_NCLBUTTONDBLCLK:
326         case WM_NCLBUTTONDOWN:
327         case WM_NCLBUTTONUP:
328         case WM_NCMBUTTONDBLCLK:
329         case WM_NCMBUTTONDOWN:
330         case WM_NCMBUTTONUP:
331         // case WM_NCMOUSEHOVER:
332         // case WM_NCMOUSELEAVE:
333         case WM_NCMOUSEMOVE:
334         case WM_NCRBUTTONDBLCLK:
335         case WM_NCRBUTTONDOWN:
336         case WM_NCRBUTTONUP:
337         // case WM_NCXBUTTONDBLCLK:
338         // case WM_NCXBUTTONDOWN:
339         // case WM_NCXBUTTONUP:
340         case WM_RBUTTONDBLCLK:
341         case WM_RBUTTONDOWN:
342         case WM_RBUTTONUP:
343         // case WM_XBUTTONDBLCLK:
344         // case WM_XBUTTONDOWN:
345         // case WM_XBUTTONUP:
346         case WM_ACTIVATE:
347         case WM_CHAR:
348         case WM_DEADCHAR:
349         case WM_GETHOTKEY:
350         case WM_HOTKEY:
351         case WM_KEYDOWN:
352         case WM_KEYUP:
353         case WM_KILLFOCUS:
354         case WM_SETFOCUS:
355         case WM_SETHOTKEY:
356         case WM_SYSCHAR:
357         case WM_SYSDEADCHAR:
358         case WM_SYSKEYDOWN:
359         case WM_SYSKEYUP:
360             return 0;
361 
362         case WM_NCCALCSIZE:
363             return 0;
364 
365         case WM_SIZE:
366         {
367             if (hWnd == hPerformancePageCpuUsageHistoryGraph)
368                 graph = &PerformancePageCpuUsageHistoryGraph;
369             else if (hWnd == hPerformancePageMemUsageHistoryGraph)
370                 graph = &PerformancePageMemUsageHistoryGraph;
371             else
372                 return 0;
373 
374             if (HIWORD(lParam) != graph->BitmapHeight)
375             {
376                 GraphCtrl_RedrawOnHeightChange(graph, HIWORD(lParam));
377             }
378             InvalidateRect(hWnd, NULL, FALSE);
379 
380             return 0;
381         }
382 
383         case WM_PAINT:
384         {
385             RECT        rcClient;
386             HDC         hdc;
387             PAINTSTRUCT ps;
388 
389             if (hWnd == hPerformancePageCpuUsageHistoryGraph)
390                 graph = &PerformancePageCpuUsageHistoryGraph;
391             else if (hWnd == hPerformancePageMemUsageHistoryGraph)
392                 graph = &PerformancePageMemUsageHistoryGraph;
393             else
394                 return 0;
395 
396             hdc = BeginPaint(hWnd, &ps);
397             GetClientRect(hWnd, &rcClient);
398             BitBlt(hdc, 0, 0,
399                    rcClient.right,
400                    rcClient.bottom,
401                    graph->hdcGraph,
402                    graph->BitmapWidth - rcClient.right,
403                    0,
404                    SRCCOPY);
405             EndPaint(hWnd, &ps);
406             return 0;
407         }
408     }
409 
410     /*
411      *  We pass on all non-handled messages
412      */
413     return CallWindowProcW(OldGraphCtrlWndProc, hWnd, message, wParam, lParam);
414 }
415