ScrollWindow doesn't repaint some region - winapi

I followed an example in Charles Petzold's book "Programming Windows". That example shows the keyboard message when keyboard event occurs. It scrolls automatically to show the display. Here is part of the code (with some small modification). The problem is that: normally, when a keyboard message is received, it should display 0 at the bottom. When another keyboard message is received, it scrolls down by a line, display 0 at the bottom, and above the bottom, it should display 1.
But what I have is that it displays always 0. Only when I resize the window, I get the correct result like: .... 4, 3, 2, 1, 0. I think the problem is that something is not repainted when it called ScrollWindow, but I don't know what's exactly the problem, since I followed the book.
case WM_SIZE:
if (message == WM_SIZE)
{ cxClient = LOWORD (lParam);
cyClient = HIWORD (lParam);
}
// Calculate scrolling rectangle
rectScroll.left = 0;
rectScroll.right = cxClient;
rectScroll.top = cyChar;
rectScroll.bottom = cyChar * ( cyClient / cyChar);
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_KEYDOWN:
case WM_KEYUP:
case WM_CHAR:
case WM_DEADCHAR:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_SYSCHAR:
case WM_SYSDEADCHAR:
for ( i = cLinesMax - 1; i > 0 ; i-- )
{
pmsg[i] = pmsg[i - 1];
}
// Store new message
pmsg[0].hwnd = hwnd;
pmsg[0].message = message;
pmsg[0].wParam = wParam;
pmsg[0].lParam = lParam;
cLines = min(cLines + 1, cLinesMax);
// Scroll up the display
ScrollWindow(hwnd, 0, -cyChar, &rectScroll, &rectScroll);
break; // call DefWindowProc so System messages work
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
for (i = 0; i < min(cLines, cyClient / cyChar - 1); i++)
{
TextOut(hdc, 0, (cyClient / cyChar - 1 - i) * cyChar, szBuffer,
wsprintf(szBuffer, TEXT("%5d"), i));
}

The whole point of ScrollWindow() is to move the existing contents of a window without needing to repaint everything. The fundamental assumption is that the content that is moved does not change as a result of the scrolling action, and only the “uncovered” region needs to be painted.
In your example you want to change the content (by renumbering the lines), not just move it. That means it needs to be redrawn – which the ScrollWindow() in your key message processing doesn’t cause, but the InvalidateRect() in your WM_SIZE handler does.

Related

Turn MIDI control input into virtual keystrokes (or virtual USB buttons etc) on macOS

I would like to use MIDI control devices (like this https://www.korg.com/us/products/computergear/nanokontrol2/ ) to generate control input for various software, Blender in particular.
One way is obviously to add MIDI input handling into Blender. Adding low-level code to Blender to listen for MIDI buttons and sliders is not hard at all, and I have basically implemented that. (I.e. I added a new "class" of input, MIDI, at Blender's lowest level.) But connecting that to the existing keyboard and mouse plumbing and especially UI functionality to associate functions with input is much more complex, and not something I want to dive into now.
Another way would perhaps be to instead run some separate software that listens for MIDI events and turns those into virtual keystrokes. Assuming it is possible to generate a much larger variety of keystrokes than there are actual keys on any keyboard, this could work nicely (like, generate keystrokes corresponding to various Unicode blocks that no real keyboard ever has). Does this sound feasible? Is a11y APIs what I should be looking at to implement such virtual keystroke generation? This way would have the benefit that it would work with any software.
Or does anybody have some better idea?
OK, so I wrote this small program. Works beautifully (once you give it the right to generate key events in System Preferences > Security & Privacy > Privacy > Accessibility > Allow the apps below to control your computer). MIDI note on and off events and MIDI controller value changes generate macOS key presses of keys with CJK Unified Ideographs as the characters.
But, then I see that Blender is the kind of software that thinks that ASCII should be enough for everybody. In other words, Blender has hardcoded restrictions that the only keys it handles are basically those on an English keyboard. You can't bind even Cyrillic or Greek keys (for which there after all exists actual keyboards) to Blender functions, much less CJK keys. Sigh. Back to the drawing board.
/* -*- Mode: ObjC; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; fill-column: 150 -*- */
#import <array>
#import <cassert>
#import <cstdio>
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <CoreMidi/CoreMidi.h>
static constexpr bool DEBUG_MIDI2KBD(true);
static constexpr int BASE_KEYCODE(1000);
static CGEventSourceRef eventSource;
static std::array<unsigned char, 16*128> control;
static void NotifyProc(const MIDINotification *message, void *refCon)
{
}
static void sendKeyDownOrUpEvent(int character, int velocity, bool down) {
CGEventRef event = CGEventCreateKeyboardEvent(eventSource, character + BASE_KEYCODE, down);
// We send CJK Unified Ideographs characters
constexpr int START = 0x4E00;
assert(character >= 0 && character <= 20989);
const UniChar string[1] = { (UniChar)(START + character) };
CGEventKeyboardSetUnicodeString(event, 1, string);
CGEventPost(kCGAnnotatedSessionEventTap, event);
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
MIDIClientRef midi_client;
OSStatus status = MIDIClientCreate((__bridge CFStringRef)#"MIDI2Kbd", NotifyProc, nullptr, &midi_client);
if (status != noErr) {
fprintf(stderr, "Error %d while setting up handlers\n", status);
return 1;
}
eventSource = CGEventSourceCreate(kCGEventSourceStatePrivate);
control.fill(0xFF);
ItemCount number_sources = MIDIGetNumberOfSources();
for (int i = 0; i < number_sources; i++) {
MIDIEndpointRef source = MIDIGetSource(i);
MIDIPortRef port;
status = MIDIInputPortCreateWithProtocol(midi_client,
(__bridge CFStringRef)[NSString stringWithFormat:#"MIDI2Kbd input %d", i],
kMIDIProtocol_1_0,
&port,
^(const MIDIEventList *evtlist, void *srcConnRefCon) {
const MIDIEventPacket* packet = &evtlist->packet[0];
for (int i = 0; i < evtlist->numPackets; i++) {
// We expect just MIDI 1.0 packets.
// The words are in big-endian format.
assert(packet->wordCount == 1);
const unsigned char *bytes = reinterpret_cast<const unsigned char *>(&packet->words[0]);
assert(bytes[3] == 0x20);
if (DEBUG_MIDI2KBD)
printf("Event: %02X %02X %02X\n", bytes[2], bytes[1], bytes[0]);
switch ((bytes[2] & 0xF0) >> 4) {
case 0x9: // Note-On
assert(bytes[1] <= 0x7F);
sendKeyDownOrUpEvent((bytes[2] & 0x0F) * 128 + bytes[1], bytes[0], true);
break;
case 0x8: // Note-Off
assert(bytes[1] <= 0x7F);
sendKeyDownOrUpEvent((bytes[2] & 0x0F) * 128 + bytes[1], bytes[0], false);
break;
case 0xB: // Control Change
assert(bytes[1] <= 0x7F);
const int number = (bytes[2] & 0x0F) * 128 + bytes[1];
if (control.at(number) != 0xFF) {
int diff = bytes[0] - control.at(number);
// If it switches from 0 to 127 or back, we assume it is not really a continuous controller but
// a button.
if (diff == 127)
diff = 1;
else if (diff == -127)
diff = -1;
if (diff > 0) {
for (int i = 0; i < diff; i++) {
// Send keys indicating single-step control value increase
sendKeyDownOrUpEvent(16*128 + number * 2, diff, true);
sendKeyDownOrUpEvent(16*128 + number * 2, diff, false);
}
} else if (diff < 0) {
for (int i = 0; i < -diff; i++) {
// Send key indicating single-step control value decrease
sendKeyDownOrUpEvent(16*128 + number * 2 + 1, -diff, true);
sendKeyDownOrUpEvent(16*128 + number * 2 + 1, -diff, false);
}
}
}
control.at(number) = bytes[0];
break;
}
packet = MIDIEventPacketNext(packet);
}
});
if (status != noErr) {
fprintf(stderr, "Error %d while setting up port\n", status);
return 1;
}
status = MIDIPortConnectSource(port, source, nullptr);
if (status != noErr) {
fprintf(stderr, "Error %d while connecting port to source\n", status);
return 1;
}
}
CFRunLoopRun();
}
return 0;
}

Determine number of rows in a List Box

Does anybody know how I can find out the number of visible rows in a list box? I tried the following but it doesn't work because there can be a scrollbar at the bottom of the list box:
RECT r;
GetWindowRect(hwnd, &r);
visible_rows = (r.bottom - r.top) / SendMessage(hwnd, LB_GETITEMHEIGHT, 0, 0);
Another idea of mine was to find out the index of the last visible item using LB_ITEMFROMPOINT and then subtract the index of the first visible item obtained via LB_GETTOPINDEX. But this will only work if the list box really has items at the top and bottom but I'm looking for a generic solution which also works with an empty list box.
So does anybody have any other suggestions how I could find out the number of visible rows in a list box?
The problem is that you're using the window rectangle which includes the control's non-client area (border and possible scrollbars). If you use GetClientRect instead then the scroll bar will already be removed from the calculation:
RECT r;
GetClientRect(hwnd, &r);
visible_rows = (r.bottom - r.top) / SendMessage(hwnd, LB_GETITEMHEIGHT, 0, 0);
This calculation also assumes the LBS_NOINTEGRALHEIGHT style is not set; if it is you would need to take a possible partial last line into account as well.
If your ListBox does not have the LBS_OWNERDRAWVARIABLE style, try something like this:
int visible_rows;
int count = SendMessage(hwnd, LB_GETCOUNT, 0, 0);
if (count > 0)
{
int top_index = SendMessage(hwnd, LB_GETTOPINDEX, 0, 0);
RECT r;
GetClientRect(hwnd, &r);
int max_visible_rows = (r.bottom - r.top) / SendMessage(hwnd, LB_GETITEMHEIGHT, 0, 0);
int bottom_index = (top_index + max_visible_rows) - 1;
if (bottom_index >= count)
bottom_index = count - 1;
visible_rows = (last_index - top_index) + 1;
}
else
visible_rows = 0;
However, if your ListBox has the LBS_OWNERDRAWVARIABLE style, you cannot use a single LB_GETITEMHEIGHT message to calculate the visible count, as each item may have a different height. In which case, you will have to use something more like this instead:
int visible_rows = 0;
int count = SendMessage(hwnd, LB_GETCOUNT, 0, 0);
if (count > 0)
{
int top_index = SendMessage(hwnd, LB_GETTOPINDEX, 0, 0);
RECT r;
GetClientRect(hwnd, &r);
int client_height = r.bottom - r.top;
for (int idx = top_index; (idx < count) && (client_height > 0); ++idx)
{
++visible_rows;
client_height -= SendMessage(hwnd, LB_GETITEMHEIGHT, idx, 0);
}
}

Delay code execution in VCL Forms Application

Need to animate a sorting algorithm, with source code line by line visualization.
INTRO:
For the begining, there is a FORM (see it in the picture attached). On top of that form is displayed a dinamicaly created array of Edit components, containing the array to sort.
A little below, on the right is placed a Memo component, containing the algorithm. At the left of each line of that algorithm, dinamicaly is placed a Label, that indicates the line number in algorithm.
The idea is to highlight line by colouring that label, where is the execution at the moment. Sorting starts when "Start" button is clicked. The action for it is following:
int n = 10;
bool swapped = true; hl(1);
int j = 0; hl(2);
int tmp; hl(3);
while (swapped) { hl(4);
swapped = false; hl(5);
j++; hl(6);
for (int i = 0; i < n - j; i++) { hl(7);
if (arr[i] > arr[i + 1]) { hl(8);
tmp = arr[i]; hl(9);
arr[i] = arr[i + 1]; hl(10);
arr[i + 1] = tmp; hl(11);
swapped = true; hl(12);
} hl(13);
} hl(14);
} hl(15);
The hl function must colour labels and pause execution by using Sleep() function
void TForm2::hl(int l)
{
for (int i = 0; i < 24; i++) {
Form2->lines[i]->Font->Color = clGray;
}
Form2->lines[l-1]->Font->Color = clRed;
Sleep(300);
}
PROBLEM:
Code execution is pausing (sleep function works properly), but the labels are still gray, with no visible changes, except the last one, when event finishes. The 15th line is red.
QUESTION:
Can anybody tell me, where I'm wrong, and how to do it right?
http://i.stack.imgur.com/crGyC.jpg
You need to allow the paint message to be processed in order to visually update the display. You can do that with either the Refresh or Update procedures:
Form2->Lines[l-1]->Font->Color = clGray;
Form2->Update(); // or Form2->Refresh();

Scrolling Graph

I am trying to draw a graph that has a scrollbar,
the graph uses time for the x-axis and i'd like to have a limited x-axis (1 minute)
so up until 1 minute, the scroll bar's page is the length of the scrollbar,
after that the page should be '60 seconds long' and the scrollbar max should be 'elapsed time' long
when you drag the scrollbar it tracks along, and pulls up the relevant bit of the graph.
the graph should auto-scroll until its dragged away (and auto-scroll once its dragged back to max)
this is what i have so far
is defined at the top of the wndproc
static SCROLLINFO sci = {sizeof(SCROLLINFO),SIF_RANGE|SIF_POS|SIF_PAGE|SIF_TRACKPOS,0,0,0,0,0};
in the WM_HSCROLL event:
GetScrollInfo((HWND)lParam, SB_CTL, &sci);
int pos;
pos = sci.nPos;
switch(LOWORD(wParam))
{
case SB_PAGELEFT:
pos -= sci.nPage;
break;
case SB_LINELEFT:
pos -= (sci.nMax/10);
break;
case SB_PAGERIGHT:
pos += sci.nPage;
break;
case SB_LINERIGHT:
pos += (sci.nMax/10);
break;
case SB_THUMBTRACK:
tsTracking = true;
pos = sci.nTrackPos;
break;
case SB_THUMBPOSITION:
tsTracking = false;
pos = sci.nTrackPos;
break;
}
if (pos < sci.nMin) pos = sci.nMin;
if (pos > sci.nMax) pos = sci.nMax;
if (pos != sci.nPos)
{
sci.fMask = SIF_POS;
sci.nPos = pos;
if(sci.nPos >= (sci.nMax-(sci.nPage*1.2)-1)) //this should detect when the thumb has reached the end of the scrollbar
sci.nPos = sci.nMax;
SetScrollInfo((HWND)lParam, SB_CTL, &sci, TRUE);
PostMessage(hWnd, WM_COMMAND, IDM_DISPLAYRESULTS, 0);
}
break;
in the drawing code called from WM_PAINT
(yes i realize i shouldn't be setting the scrollbar's info here, i intend to put it in its proper place once its working properly)
at the top of the graph-drawing function:
static SCROLLINFO sci = {sizeof(SCROLLINFO),SIF_RANGE|SIF_POS|SIF_PAGE|SIF_TRACKPOS,0,0,0,0,0};
in the graph-drawing function:
__int64
elapsed_time = g_h_maxX-g_h_minX, //the elapsed time
windowLengthMS, //the current window length in ms (between 0 and MAX_TIME_WIDTH_MS)
start, //the start of the current window
end; //the end of the current window
static UINT32 windowMaxS = 1; //how long the window max is (used for x sub-divisions)
double
xTickStep, //x division step
yTickStep, //y division step
xScale, //x-scale (used to get the time relative to the elapsed time and size of the graph)
yScale; //y-scale (used to get the data being plotted relative to its max, and the size of the graph)
if(!tsTracking) //a global bool, checking if the scrollbar's thumb is being dragged
{
GetScrollInfo(get_chwnd(IDCW_HARMONICSRESULTSTIMESHIFTSB),SB_CTL,&sci); //gets the scroll info for the scrollbar
int pos = sci.nPos*(double(elapsed_time)/double(sci.nMax - sci.nMin)); //gets the position relative to the new max
sci.nMin = g_h_minX; //sets min (should always be the same, done for completeness)
sci.nMax = (((g_h_maxX-MAX_TIME_WIDTH_MS)>0)?(g_h_maxX):0); //sets max to either max, or 0 (if max isnt bigger then page length)
sci.nPage = MAX_TIME_WIDTH_MS; //sets the page length to the window length
sci.nPos = pos; //sets position to its new value
if(sci.nPos >= (sci.nMax-(sci.nPage*1.2)-1)) //if position is one "page" from the end
sci.nPos = sci.nMax; //set it to max
SetScrollInfo(get_chwnd(IDCW_HARMONICSRESULTSTIMESHIFTSB),SB_CTL,&sci,true);
} //set scroll info
if (elapsed_time > MAX_TIME_WIDTH_MS) //if elapsed time is longer then the max window length
{
end = ((double)sci.nPos / double(sci.nMax)) * g_h_maxX; //set the end to current (scroll position / scroll max) * latest time -> this should put end relative to the scrollbar
start = end-MAX_TIME_WIDTH_MS; //sets start to end - window length
if (start < 0) //if start is now less than zero
{
end = MAX_TIME_WIDTH_MS; //set end to window length
start = 0; //set start to 0
}
}
else //if (elapsed_time <= MAX_TIME_WIDTH_MS)
{
start = g_h_minX; //start is min
end = g_h_maxX; //end is max
}
windowLengthMS = (end-start); //find the current window length
if(_MSTOSECS(windowLengthMS) > windowMaxS) //if the current window length beats one fo the markers
windowMaxS = ((windowMaxS < 10)?windowMaxS+1:(windowMaxS==40)?windowMaxS*1.5:windowMaxS*2); //change subdiv scaling
xScale = double(graph_width)/double(windowLengthMS); //set x-scale to width / window length
yScale = (double)(graph_height)/((double)g_h_maxY - (double)g_h_minY); //set y-scale to height / (maxVal-minVal)
int ticks = _MSTOSECS(elapsed_time); //ticks = full seconds elapsed
xTickStep = double(graph_width)/double(ticks+1); //tickstep = seconds+1 (accounts for 0)
yTickStep = double(graph_height)/double(Y_TICKS); //6 y-ticks, constant.
SelectObject(hDC,hThickPen);
{ //scope to clear variables!
double x; //x to store subdiv x-location
for(i = ticks; i >= 0; i--) //for each subdiv
{
if(((windowMaxS>40)?(!(i%3)):(windowMaxS >20)?(!(i%2)):i)) //have if <=20 secs, each second is shown on x, >20 <=40, every second second is shown, >40 seconds, every 3rd second is shown
{
x = round((_SECSTOMS(i)*xScale)); //find x-pos
sprintf(buf,"%d",(i+_MSTOSECS(start))); //print to buffer
SetRect(&rc, fr.left+x-5, fr.bottom+5, fr.left+x+xTickStep, fr.bottom+25); //get text rectangle
DrawText(hDC, buf, -1, &rc, DT_SINGLELINE | DT_VCENTER | DT_LEFT); //draw text
}
if (i!=ticks && (windowMaxS>40)?(!(i%6)):(windowMaxS >20)?(!(i%4)):(windowMaxS>10)?(!(i%2)):i)//every <10, each sec gets a subdiv, <20 each second tick gets a subdiv, <40 each 6th tick gets a subdiv
{
MoveToEx(hDC, GRAPH_XL_OFFSET+x, graph_bottom, NULL); //draw the line
LineTo(hDC, GRAPH_XL_OFFSET+x, graph_bottom-graph_height); //draw the line
}
}
}
as for globals:
g_h_minX is the start time and
g_h_maxX is the last result time
MAX_TIME_WIDTH_MS is the "window length" in ms -> how long i want the scroll bar page (60 seconds)
so the idea is to set end to where the scroll bar is, and get the start by taking the window length from end, and working out which part of the graph we're trying to look at.
i've been fiddling with this for the last 2 days and i'm running out of ideas.
im sure i'm close, but i cant quite figure it out,
edit:
updated the code slightly
it also seems i forgot to say what the problem was.
the scolling code isnt working properly, it autoscrolls when new data comes in,
but when i drag the thumb away from the end of the scrollbar, it just snaps to the start, and wont move.
on closer inspection, the arrows work, and the "page-right" "page-left" on the right-click menu work, just not the tracking
any help would be appreciated.
thanks in advance.
I have managed to find the problem,
I ended up converting everything to seconds and working with that, that ended up working, so i must have been having some scaling issues.
the code ended up like this:
case WM_HSCROLL:
{
SCROLLINFO sci = {sizeof(SCROLLINFO),SIF_RANGE|SIF_POS|SIF_PAGE|SIF_TRACKPOS,0,0,0,0,0};
GetScrollInfo((HWND)lParam, SB_CTL, &sci);
int pos;
pos = sci.nPos;
switch(LOWORD(wParam))
{
case SB_PAGELEFT:
pos -= sci.nPage;
break;
case SB_LINELEFT:
pos -= 2;
break;
case SB_PAGERIGHT:
pos += sci.nPage;
break;
case SB_LINERIGHT:
pos += 2;
break;
case SB_THUMBTRACK:
tsTracking = true;
pos = sci.nTrackPos;
break;
case SB_THUMBPOSITION:
tsTracking = false;
pos = sci.nTrackPos;
break;
case SB_LEFT:
pos = sci.nMin;
break;
case SB_RIGHT:
pos = sci.nMax;
break;
}
if (pos < sci.nMin) pos = sci.nMin;
if (pos > sci.nMax) pos = sci.nMax;
if (pos != sci.nPos)
{
sci.fMask = SIF_POS;
sci.nPos = pos;
//if(sci.nPos >= (sci.nMax-sci.nPage)) //this should detect when the thumb has reached the end of the scrollbar
// sci.nPos = sci.nMax;
SetScrollInfo((HWND)lParam, SB_CTL, &sci, TRUE);
PostMessage(hWnd, WM_COMMAND, IDM_DISPLAYRESULTS, 0);
}
}
break;
for the scrolling code and
__int64
elapsed_time = g_h_maxX-g_h_minX, //the elapsed time
windowLengthMS, //the current window length in ms (between 0 and MAX_TIME_WIDTH_MS)
start, //the start of the current window
end; //the end of the current window
static UINT32 windowMaxS = 1; //how long the window max is (used for x sub-divisions)
double
xTickStep, //x division step
yTickStep, //y division step
xScale, //x-scale (used to get the time relative to the elapsed time and size of the graph)
yScale; //y-scale (used to get the data being plotted relative to its max, and the size of the graph)
GetScrollInfo(get_chwnd(IDCW_HARMONICSRESULTSTIMESHIFTSB),SB_CTL,&sci); //gets the scroll info for the scrollbar
if (elapsed_time >= MAX_TIME_WIDTH_MS) //if elapsed time is longer then the max window length
{
start = _SECSTOMS(sci.nPos); //sets start to end - window length
end = start+MAX_TIME_WIDTH_MS; //set the end to current (scroll position / scroll max) * latest time -> this should put end relative to the scrollbar
}
else //if (elapsed_time <= MAX_TIME_WIDTH_MS)
{
start = g_h_minX; //start is min
end = g_h_maxX; //end is max
}
windowLengthMS = (end-start); //find the current window length
if(_MSTOSECS(windowLengthMS) > windowMaxS) //if the current window length beats one fo the markers
windowMaxS = ((windowMaxS < 10)?windowMaxS+1:(windowMaxS==40)?windowMaxS*1.5:windowMaxS*2); //change subdiv scaling
xScale = double(graph_width)/double(windowLengthMS); //set x-scale to width / window length
yScale = (double)(graph_height)/((double)max - (double)g_h_minY); //set y-scale to height / (maxVal-minVal)
if(!(g_h_maxY-g_h_minY))
yScale = 1;
if(!windowLengthMS)
xScale = 1;
int ticks = _MSTOSECS(elapsed_time); //ticks = full seconds elapsed
xTickStep = double(graph_width)/double(ticks+1); //tickstep = seconds+1 (accounts for 0)
yTickStep = double(graph_height)/double(Y_TICKS); //6 y-ticks, constant.
for the graph x-scaling

Windows Application (Game) using a lot of resources

I'm currently working on a game that creates a window using WindowsAPI. However, at the moment the process is taking up 50% of my CPU. All I am doing is creating the window and looping using the code found below:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
MSG message = {0};
WNDCLASSEX wcl = {0};
wcl.cbSize = sizeof(wcl);
wcl.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wcl.lpfnWndProc = WindowProc;
wcl.cbClsExtra = 0;
wcl.cbWndExtra = 0;
wcl.hInstance = hInstance = hInstance;
wcl.hIcon = LoadIcon(0, IDI_APPLICATION);
wcl.hCursor = LoadCursor(0, IDC_ARROW);
wcl.hbrBackground = 0;
wcl.lpszMenuName = 0;
wcl.lpszClassName = "GL2WindowClass";
wcl.hIconSm = 0;
if (!RegisterClassEx(&wcl))
return 0;
hWnd = CreateAppWindow(wcl, "Application");
if (hWnd)
{
if (Init())
{
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
while (true)
{
while (PeekMessage(&message, 0, 0, 0, PM_REMOVE))
{
if (message.message == WM_QUIT)
break;
TranslateMessage(&message);
DispatchMessage(&message);
}
if (message.message == WM_QUIT)
break;
if (hasFocus)
{
elapsedTime = GetElapsedTimeInSeconds();
lastEarth += elapsedTime;
lastUpdate += elapsedTime;
lastFrame += elapsedTime;
lastParticle += elapsedTime;
if(lastUpdate >= (1.0f / 100.0f))
{
Update(lastUpdate);
lastUpdate = 0;
}
if(lastFrame >= (1.0f / 60.0f))
{
UpdateFrameRate(lastFrame);
lastFrame = 0;
Render();
SwapBuffers(hDC);
}
if(lastEarth >= (1.0f / 10.0f))
{
UpdateEarthAnimation();
lastEarth = 0;
}
if(lastParticle >= (1.0f / 30.0f))
{
particleManager->rightBooster->Update();
particleManager->rightBoosterSmoke->Update();
particleManager->leftBooster->Update();
particleManager->leftBoosterSmoke->Update();
particleManager->breakUp->Update();
lastParticle = 0;
}
}
else
{
WaitMessage();
}
}
}
Cleanup();
UnregisterClass(wcl.lpszClassName, hInstance);
}
return static_cast<int>(message.wParam);
}
Where GetElapsedTimeInSeconds :
float GetElapsedTimeInSeconds()
{
static const int MAX_SAMPLE_COUNT = 50;
static float frameTimes[MAX_SAMPLE_COUNT];
static float timeScale = 0.0f;
static float actualElapsedTimeSec = 0.0f;
static INT64 freq = 0;
static INT64 lastTime = 0;
static int sampleCount = 0;
static bool initialized = false;
INT64 time = 0;
float elapsedTimeSec = 0.0f;
if (!initialized)
{
initialized = true;
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&freq));
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&lastTime));
timeScale = 1.0f / freq;
}
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&time));
elapsedTimeSec = (time - lastTime) * timeScale;
lastTime = time;
if (fabsf(elapsedTimeSec - actualElapsedTimeSec) < 1.0f)
{
memmove(&frameTimes[1], frameTimes, sizeof(frameTimes) - sizeof(frameTimes[0]));
frameTimes[0] = elapsedTimeSec;
if (sampleCount < MAX_SAMPLE_COUNT)
++sampleCount;
}
actualElapsedTimeSec = 0.0f;
for (int i = 0; i < sampleCount; ++i)
actualElapsedTimeSec += frameTimes[i];
if (sampleCount > 0)
actualElapsedTimeSec /= sampleCount;
return actualElapsedTimeSec;
}
So even when I am not drawing anything when the window has focus it still takes up 50%. I don't understand how this is taking up so much system resources.
Am I doing something wrong?
Any help would be much appreciated, thank you!
hasfocus falls out of the sky. You'll burn 100% core when it is true, there's nothing inside the if() statement that will make your program wait for anything. Sleep(1) would fix that but it isn't otherwise obvious what you intend to do.
To add to the other answers...
You need to throttle your game loop in some way. PeekMessage returns immediately if there are no messages so you are just looping as fast as possible, consuming 100% of one of your CPU cores. Presumably you see 50% as you have a dual core PC.
Instead of doing a Sleep() to avoid consuming 100% of the CPU cycles, call MsgWaitForMultipleObjects at the start of each loop, passing in zero handles, passing in a small timeout being your minimal interval between frames. Each time it returns, its because the timeout elapsed, OR because there are messages to process. If there are messages, process them (all) and then, either way, then process a game OnNextFrame / Repaint cycle. Use UpdateWindow instead of waiting for a WM_PAINT to be posted.
Your application is running in a tight loop and burning CPU by not doing anything useful. You have to add something like Sleep(1) into the loop.
This example you provided is a degenerate case and it doesn't really have to be fixed. If you going to make a game out of this, then you are going to put the frame update and render functionality into the loop and it will be doing some useful "stuff". In this case you wouldn't want to "sleep" any cycles.
If I were you I wouldn't start making a game from scratch and would look for some game engine. There's a lot of high quality open and closed source free game engines available on the market. Or at least look for some simple game skeletons that provide basic things like a message loop and double buffered window drawing setup.

Resources