graphctl.c 19 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 *  ReactOS Task Manager
 *
 *  graphctl.c
 *
 *  Copyright (C) 2002  Robert Dickenson <robd@reactos.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 22 23
 */

#include <stdio.h>
24
#include <stdlib.h>
25
#include <math.h>
26 27 28

#include <windows.h>
#include <commctrl.h>
29 30 31
#include "graphctl.h"
#include "taskmgr.h"

32
WNDPROC OldGraphCtrlWndProc;
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

static void GraphCtrl_Init(TGraphCtrl* this)
{
    int i;

    this->m_hWnd = 0;
    this->m_hParentWnd = 0;
    this->m_dcGrid = 0;
    this->m_dcPlot = 0; 
    this->m_bitmapOldGrid = 0;
    this->m_bitmapOldPlot = 0;
    this->m_bitmapGrid = 0;
    this->m_bitmapPlot = 0;
    this->m_brushBack = 0;
    
    this->m_penPlot[0] = 0;
    this->m_penPlot[1] = 0;
    this->m_penPlot[2] = 0;
    this->m_penPlot[3] = 0;
    
    /* since plotting is based on a LineTo for each new point
     * we need a starting point (i.e. a "previous" point)
     * use 0.0 as the default first point.
     * these are public member variables, and can be changed outside
57
     * (after construction).  Therefore m_dPreviousPosition could be set to
58 59 60 61 62 63 64 65 66 67 68
     * a more appropriate value prior to the first call to SetPosition.
     */
    this->m_dPreviousPosition[0] = 0.0;
    this->m_dPreviousPosition[1] = 0.0;
    this->m_dPreviousPosition[2] = 0.0;
    this->m_dPreviousPosition[3] = 0.0;

    /*  public variable for the number of decimal places on the y axis */
    this->m_nYDecimals = 3;

    /*  set some initial values for the scaling until "SetRange" is called.
69 70
     *  these are protected variables and must be set with SetRange
     *  in order to ensure that m_dRange is updated accordingly
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
     */
    /*   m_dLowerLimit = -10.0; */
    /*   m_dUpperLimit =  10.0; */
    this->m_dLowerLimit = 0.0;
    this->m_dUpperLimit = 100.0;
    this->m_dRange      =  this->m_dUpperLimit - this->m_dLowerLimit;   /*  protected member variable */

    /*  m_nShiftPixels determines how much the plot shifts (in terms of pixels)  */
    /*  with the addition of a new data point */
    this->m_nShiftPixels     = 4;
    this->m_nHalfShiftPixels = this->m_nShiftPixels/2;                     /*  protected */
    this->m_nPlotShiftPixels = this->m_nShiftPixels + this->m_nHalfShiftPixels;  /*  protected */

    /*  background, grid and data colors */
    /*  these are public variables and can be set directly */
    this->m_crBackColor = RGB(  0,   0,   0);  /*  see also SetBackgroundColor */
    this->m_crGridColor = RGB(  0, 255, 255);  /*  see also SetGridColor */
    this->m_crPlotColor[0] = RGB(255, 255, 255);  /*  see also SetPlotColor */
    this->m_crPlotColor[1] = RGB(100, 255, 255);  /*  see also SetPlotColor */
    this->m_crPlotColor[2] = RGB(255, 100, 255);  /*  see also SetPlotColor */
    this->m_crPlotColor[3] = RGB(255, 255, 100);  /*  see also SetPlotColor */

    /*  protected variables */
    for (i = 0; i < MAX_PLOTS; i++) 
    {
        this->m_penPlot[i] = CreatePen(PS_SOLID, 0, this->m_crPlotColor[i]);
    }
    this->m_brushBack = CreateSolidBrush(this->m_crBackColor);

    /*  public member variables, can be set directly  */
    strcpy(this->m_strXUnitsString, "Samples");  /*  can also be set with SetXUnits */
    strcpy(this->m_strYUnitsString, "Y units");  /*  can also be set with SetYUnits */

    /*  protected bitmaps to restore the memory DC's */
    this->m_bitmapOldGrid = NULL;
    this->m_bitmapOldPlot = NULL;
}

109
static void GraphCtrl_Resize(TGraphCtrl* this)
110
{
111 112
    /*  NOTE: Resize automatically gets called during the setup of the control */
    GetClientRect(this->m_hWnd, &this->m_rectClient);
113

114 115 116
    /*  set some member variables to avoid multiple function calls */
    this->m_nClientHeight = this->m_rectClient.bottom - this->m_rectClient.top;/* m_rectClient.Height(); */
    this->m_nClientWidth  = this->m_rectClient.right - this->m_rectClient.left;/* m_rectClient.Width(); */
117

118 119
    /*  the "left" coordinate and "width" will be modified in  */
    /*  InvalidateCtrl to be based on the width of the y axis scaling */
120
    SetRect(&this->m_rectPlot, 0, -1, this->m_rectClient.right, this->m_rectClient.bottom);
121

122 123 124
    /*  set some member variables to avoid multiple function calls */
    this->m_nPlotHeight = this->m_rectPlot.bottom - this->m_rectPlot.top;/* m_rectPlot.Height(); */
    this->m_nPlotWidth  = this->m_rectPlot.right - this->m_rectPlot.left;/* m_rectPlot.Width(); */
125

126 127 128
    /*  set the scaling factor for now, this can be adjusted  */
    /*  in the SetRange functions */
    this->m_dVerticalFactor = (double)this->m_nPlotHeight / this->m_dRange;
129 130
}

131
void GraphCtrl_Create(TGraphCtrl* this, HWND hWnd, HWND hParentWnd, UINT nID)
132
{
133 134 135 136
    GraphCtrl_Init(this);
    this->m_hParentWnd = hParentWnd;
    this->m_hWnd = hWnd;
    GraphCtrl_Resize(this);
137 138
}

139
static void GraphCtrl_InvalidateCtrl(TGraphCtrl* this)
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
{
    /*  There is a lot of drawing going on here - particularly in terms of  */
    /*  drawing the grid.  Don't panic, this is all being drawn (only once) */
    /*  to a bitmap.  The result is then BitBlt'd to the control whenever needed. */
    int i, j;
    int nCharacters;
    int nTopGridPix, nMidGridPix, nBottomGridPix;

    HPEN oldPen;
    HPEN solidPen = CreatePen(PS_SOLID, 0, this->m_crGridColor);
    /* HFONT axisFont, yUnitFont, oldFont; */
    /* char strTemp[50]; */

    /*  in case we haven't established the memory dc's */
    /* CClientDC dc(this); */
    HDC dc = GetDC(this->m_hParentWnd);

    /*  if we don't have one yet, set up a memory dc for the grid */
    if (this->m_dcGrid == NULL)
    {
        this->m_dcGrid = CreateCompatibleDC(dc);
        this->m_bitmapGrid = CreateCompatibleBitmap(dc, this->m_nClientWidth, this->m_nClientHeight);
162
        this->m_bitmapOldGrid = SelectObject(this->m_dcGrid, this->m_bitmapGrid);
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
    }
  
    SetBkColor(this->m_dcGrid, this->m_crBackColor);

    /*  fill the grid background */
    FillRect(this->m_dcGrid, &this->m_rectClient, this->m_brushBack);

    /*  draw the plot rectangle: */
    /*  determine how wide the y axis scaling values are */
    nCharacters = abs((int)log10(fabs(this->m_dUpperLimit)));
    nCharacters = max(nCharacters, abs((int)log10(fabs(this->m_dLowerLimit))));

    /*  add the units digit, decimal point and a minus sign, and an extra space */
    /*  as well as the number of decimal places to display */
    nCharacters = nCharacters + 4 + this->m_nYDecimals;  

    /*  adjust the plot rectangle dimensions */
    /*  assume 6 pixels per character (this may need to be adjusted) */
    /*   m_rectPlot.left = m_rectClient.left + 6*(nCharacters); */
    this->m_rectPlot.left = this->m_rectClient.left;
    this->m_nPlotWidth    = this->m_rectPlot.right - this->m_rectPlot.left;/* m_rectPlot.Width(); */

    /*  draw the plot rectangle */
186
    oldPen = SelectObject(this->m_dcGrid, solidPen);
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    MoveToEx(this->m_dcGrid, this->m_rectPlot.left, this->m_rectPlot.top, NULL);
    LineTo(this->m_dcGrid, this->m_rectPlot.right+1, this->m_rectPlot.top);
    LineTo(this->m_dcGrid, this->m_rectPlot.right+1, this->m_rectPlot.bottom+1);
    LineTo(this->m_dcGrid, this->m_rectPlot.left, this->m_rectPlot.bottom+1);
    /*   LineTo(m_dcGrid, m_rectPlot.left, m_rectPlot.top); */
    SelectObject(this->m_dcGrid, oldPen); 
    DeleteObject(solidPen);

    /*  draw the dotted lines, 
     *  use SetPixel instead of a dotted pen - this allows for a 
     *  finer dotted line and a more "technical" look
     */
    nMidGridPix    = (this->m_rectPlot.top + this->m_rectPlot.bottom)/2;
    nTopGridPix    = nMidGridPix - this->m_nPlotHeight/4;
    nBottomGridPix = nMidGridPix + this->m_nPlotHeight/4;

    for (i=this->m_rectPlot.left; i<this->m_rectPlot.right; i+=2) 
    {
        SetPixel(this->m_dcGrid, i, nTopGridPix,    this->m_crGridColor);
        SetPixel(this->m_dcGrid, i, nMidGridPix,    this->m_crGridColor);
        SetPixel(this->m_dcGrid, i, nBottomGridPix, this->m_crGridColor);
    }

    for (i=this->m_rectPlot.left; i<this->m_rectPlot.right; i+=10)
    {
        for (j=this->m_rectPlot.top; j<this->m_rectPlot.bottom; j+=2)
        {
            SetPixel(this->m_dcGrid, i, j, this->m_crGridColor);
            /*       SetPixel(m_dcGrid, i, j, m_crGridColor); */
            /*       SetPixel(m_dcGrid, i, j, m_crGridColor); */
        }
    }

220 221
    /*  at this point we are done filling the grid bitmap,  */
    /*  no more drawing to this bitmap is needed until the settings are changed */
222 223 224 225 226 227
  
    /*  if we don't have one yet, set up a memory dc for the plot */
    if (this->m_dcPlot == NULL) 
    {
        this->m_dcPlot = CreateCompatibleDC(dc);
        this->m_bitmapPlot = CreateCompatibleBitmap(dc, this->m_nClientWidth, this->m_nClientHeight);
228
        this->m_bitmapOldPlot = SelectObject(this->m_dcPlot, this->m_bitmapPlot);
229 230 231 232 233 234 235 236 237 238 239
    }

    /*  make sure the plot bitmap is cleared */
    SetBkColor(this->m_dcPlot, this->m_crBackColor);
    FillRect(this->m_dcPlot, &this->m_rectClient, this->m_brushBack);

    /*  finally, force the plot area to redraw */
    InvalidateRect(this->m_hParentWnd, &this->m_rectClient, TRUE);
    ReleaseDC(this->m_hParentWnd, dc);
}

240
void GraphCtrl_SetRange(TGraphCtrl* this, double dLower, double dUpper, int nDecimalPlaces)
241
{
242 243 244 245 246 247 248 249
    /* ASSERT(dUpper > dLower); */
    this->m_dLowerLimit     = dLower;
    this->m_dUpperLimit     = dUpper;
    this->m_nYDecimals      = nDecimalPlaces;
    this->m_dRange          = this->m_dUpperLimit - this->m_dLowerLimit;
    this->m_dVerticalFactor = (double)this->m_nPlotHeight / this->m_dRange;
    /*  clear out the existing garbage, re-start with a clean plot */
    GraphCtrl_InvalidateCtrl(this);
250 251
}

252 253 254 255 256 257
void GraphCtrl_SetGridColor(TGraphCtrl* this, COLORREF color)
{
    this->m_crGridColor = color;
    /*  clear out the existing garbage, re-start with a clean plot */
    GraphCtrl_InvalidateCtrl(this);
}
258

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
void GraphCtrl_SetPlotColor(TGraphCtrl* this, int plot, COLORREF color)
{
    this->m_crPlotColor[plot] = color;
    DeleteObject(this->m_penPlot[plot]);
    this->m_penPlot[plot] = CreatePen(PS_SOLID, 0, this->m_crPlotColor[plot]);
    /*  clear out the existing garbage, re-start with a clean plot */
    GraphCtrl_InvalidateCtrl(this);
}

void GraphCtrl_SetBackgroundColor(TGraphCtrl* this, COLORREF color)
{
    this->m_crBackColor = color;
    DeleteObject(this->m_brushBack);
    this->m_brushBack = CreateSolidBrush(this->m_crBackColor);
    /*  clear out the existing garbage, re-start with a clean plot */
    GraphCtrl_InvalidateCtrl(this);
275 276
}

277
static void GraphCtrl_DrawPoint(TGraphCtrl* this)
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
{
    /*  this does the work of "scrolling" the plot to the left
     *  and appending a new data point all of the plotting is
     *  directed to the memory based bitmap associated with m_dcPlot
     *  the will subsequently be BitBlt'd to the client in Paint
     */
    int currX, prevX, currY, prevY;
    HPEN oldPen;
    RECT rectCleanUp;
    int i;
    
    if (this->m_dcPlot != NULL) 
    {
        /*  shift the plot by BitBlt'ing it to itself 
         *  note: the m_dcPlot covers the entire client
         *        but we only shift bitmap that is the size 
         *        of the plot rectangle
295
         *  grab the right side of the plot (excluding m_nShiftPixels on the left)
296 297 298 299 300 301 302 303
         *  move this grabbed bitmap to the left by m_nShiftPixels
         */
        BitBlt(this->m_dcPlot, this->m_rectPlot.left, this->m_rectPlot.top+1, 
               this->m_nPlotWidth, this->m_nPlotHeight, this->m_dcPlot, 
               this->m_rectPlot.left+this->m_nShiftPixels, this->m_rectPlot.top+1, 
               SRCCOPY);

        /*  establish a rectangle over the right side of plot */
304
        /*  which now needs to be cleaned up prior to adding the new point */
305 306 307 308 309 310
        rectCleanUp = this->m_rectPlot;
        rectCleanUp.left  = rectCleanUp.right - this->m_nShiftPixels;

        /*  fill the cleanup area with the background */
        FillRect(this->m_dcPlot, &rectCleanUp, this->m_brushBack);

311
        /*  draw the next line segment */
312 313 314
        for (i = 0; i < MAX_PLOTS; i++) 
        {
            /*  grab the plotting pen */
315
            oldPen = SelectObject(this->m_dcPlot, this->m_penPlot[i]);
316 317 318

            /*  move to the previous point */
            prevX = this->m_rectPlot.right-this->m_nPlotShiftPixels;
319 320
            prevY = this->m_rectPlot.bottom -
                (int)((this->m_dPreviousPosition[i] - this->m_dLowerLimit) * this->m_dVerticalFactor);
321 322 323 324 325
            MoveToEx(this->m_dcPlot, prevX, prevY, NULL);

            /*  draw to the current point */
            currX = this->m_rectPlot.right-this->m_nHalfShiftPixels;
            currY = this->m_rectPlot.bottom -
326
                (int)((this->m_dCurrentPosition[i] - this->m_dLowerLimit) * this->m_dVerticalFactor);
327 328 329 330 331 332 333 334 335 336 337 338 339
            LineTo(this->m_dcPlot, currX, currY);

            /*  Restore the pen  */
            SelectObject(this->m_dcPlot, oldPen);

            /*  if the data leaks over the upper or lower plot boundaries
             *  fill the upper and lower leakage with the background
             *  this will facilitate clipping on an as needed basis
             *  as opposed to always calling IntersectClipRect
             */
            if ((prevY <= this->m_rectPlot.top) || (currY <= this->m_rectPlot.top)) 
            {
                RECT rc;
340
                SetRect(&rc, prevX, this->m_rectClient.top, currX + 1, this->m_rectPlot.top + 1);
341 342 343 344 345
                FillRect(this->m_dcPlot, &rc, this->m_brushBack);
            }
            if ((prevY >= this->m_rectPlot.bottom) || (currY >= this->m_rectPlot.bottom)) 
            {
                RECT rc;
346 347
                SetRect(&rc, prevX, this->m_rectPlot.bottom + 1, currX + 1,
                        this->m_rectClient.bottom + 1);
348 349 350 351 352 353 354 355 356 357
                /* RECT rc(prevX, m_rectPlot.bottom+1, currX+1, m_rectClient.bottom+1); */
                FillRect(this->m_dcPlot, &rc, this->m_brushBack);
            }

            /*  store the current point for connection to the next point */
            this->m_dPreviousPosition[i] = this->m_dCurrentPosition[i];
        }
    }
}

358 359 360
double GraphCtrl_AppendPoint(TGraphCtrl* this,
                             double dNewPoint0, double dNewPoint1,
                             double dNewPoint2, double dNewPoint3)
361
{
362 363
    /*  append a data point to the plot & return the previous point */
    double dPrevious;
364

365 366 367 368 369 370 371 372 373
    dPrevious = this->m_dCurrentPosition[0];
    this->m_dCurrentPosition[0] = dNewPoint0;
    this->m_dCurrentPosition[1] = dNewPoint1;
    this->m_dCurrentPosition[2] = dNewPoint2;
    this->m_dCurrentPosition[3] = dNewPoint3;
    GraphCtrl_DrawPoint(this);
    /* Invalidate(); */
    return dPrevious;
}
374

375
static void GraphCtrl_Paint(TGraphCtrl* this, HWND hWnd, HDC dc)
376 377 378 379
{
    HDC memDC;
    HBITMAP memBitmap;
    HBITMAP oldBitmap; /*  bitmap originally found in CMemDC */
380

381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
    /*  no real plotting work is performed here,  */
    /*  just putting the existing bitmaps on the client */

    /*  to avoid flicker, establish a memory dc, draw to it */
    /*  and then BitBlt it to the client */
    memDC = CreateCompatibleDC(dc);
    memBitmap = CreateCompatibleBitmap(dc, this->m_nClientWidth, this->m_nClientHeight);
    oldBitmap = SelectObject(memDC, memBitmap);

    if (memDC != NULL)
    {
        /*  first drop the grid on the memory dc */
        BitBlt(memDC, 0, 0, this->m_nClientWidth, this->m_nClientHeight, this->m_dcGrid, 0, 0, SRCCOPY);
        /*  now add the plot on top as a "pattern" via SRCPAINT. */
        /*  works well with dark background and a light plot */
        BitBlt(memDC, 0, 0, this->m_nClientWidth, this->m_nClientHeight, this->m_dcPlot, 0, 0, SRCPAINT);  /* SRCPAINT */
        /*  finally send the result to the display */
        BitBlt(dc, 0, 0, this->m_nClientWidth, this->m_nClientHeight, memDC, 0, 0, SRCCOPY);
    }
    SelectObject(memDC, oldBitmap);
    DeleteObject(memBitmap);
    DeleteDC(memDC);
403 404 405 406 407 408 409
}

extern TGraphCtrl PerformancePageCpuUsageHistoryGraph;
extern TGraphCtrl PerformancePageMemUsageHistoryGraph;
extern HWND hPerformancePageCpuUsageHistoryGraph;
extern HWND hPerformancePageMemUsageHistoryGraph;

410 411
INT_PTR CALLBACK
GraphCtrl_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
{
    RECT        rcClient;
    HDC            hdc;
    PAINTSTRUCT     ps;
    
    switch (message) 
    {
    case WM_ERASEBKGND:
        return TRUE;
    /* 
     *  Filter out mouse  & keyboard messages
     */ 
    /* case WM_APPCOMMAND: */
    case WM_CAPTURECHANGED:
    case WM_LBUTTONDBLCLK:
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_MBUTTONDBLCLK:
    case WM_MBUTTONDOWN:
    case WM_MBUTTONUP:
    case WM_MOUSEACTIVATE:
    case WM_MOUSEHOVER:
    case WM_MOUSELEAVE:
    case WM_MOUSEMOVE:
    /* case WM_MOUSEWHEEL: */
    case WM_NCHITTEST:
    case WM_NCLBUTTONDBLCLK:
    case WM_NCLBUTTONDOWN:
    case WM_NCLBUTTONUP:
    case WM_NCMBUTTONDBLCLK:
    case WM_NCMBUTTONDOWN:
    case WM_NCMBUTTONUP:
    /* case WM_NCMOUSEHOVER: */
    /* case WM_NCMOUSELEAVE: */
    case WM_NCMOUSEMOVE:
    case WM_NCRBUTTONDBLCLK:
    case WM_NCRBUTTONDOWN:
    case WM_NCRBUTTONUP:
    /* case WM_NCXBUTTONDBLCLK: */
    /* case WM_NCXBUTTONDOWN: */
    /* case WM_NCXBUTTONUP: */
    case WM_RBUTTONDBLCLK:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
    /* case WM_XBUTTONDBLCLK: */
    /* case WM_XBUTTONDOWN: */
    /* case WM_XBUTTONUP: */
    case WM_ACTIVATE:
    case WM_CHAR:
    case WM_DEADCHAR:
    case WM_GETHOTKEY:
    case WM_HOTKEY:
    case WM_KEYDOWN:
    case WM_KEYUP:
    case WM_KILLFOCUS:
    case WM_SETFOCUS:
    case WM_SETHOTKEY:
    case WM_SYSCHAR:
    case WM_SYSDEADCHAR:
    case WM_SYSKEYDOWN:
    case WM_SYSKEYUP:
        return 0;

    case WM_NCCALCSIZE:
        return 0;

    case WM_SIZE:
        if (hWnd == hPerformancePageMemUsageHistoryGraph) 
        {
            GraphCtrl_Resize(&PerformancePageMemUsageHistoryGraph);
            GraphCtrl_InvalidateCtrl(&PerformancePageMemUsageHistoryGraph);
        }
        if (hWnd == hPerformancePageCpuUsageHistoryGraph) 
        {
            GraphCtrl_Resize(&PerformancePageCpuUsageHistoryGraph);
            GraphCtrl_InvalidateCtrl(&PerformancePageCpuUsageHistoryGraph);
        }
        return 0;

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        GetClientRect(hWnd, &rcClient);
        if (hWnd == hPerformancePageMemUsageHistoryGraph)
            GraphCtrl_Paint(&PerformancePageMemUsageHistoryGraph, hWnd, hdc);
        if (hWnd == hPerformancePageCpuUsageHistoryGraph)
            GraphCtrl_Paint(&PerformancePageCpuUsageHistoryGraph, hWnd, hdc);
        EndPaint(hWnd, &ps);
        return 0;
    }
    
    /* 
     *  We pass on all non-handled messages
     */ 
505
    return CallWindowProcW(OldGraphCtrlWndProc, hWnd, message, wParam, lParam);
506
}