The code is given below with build command. Here are some question that you might want me to answer:
-
What is the intent of this program? This code is in the early stage of writing cross-platform 2D graphics library which, for context of this question, needs to manage multiple windows. Currently I am trying to render an animated window by having platform independent functions and structs in
WinMain()
-
Why do I have an if else statement in WindowProcedure? Its because within the following switch case statements, the function requires
WINDOW*
to be passed. But for application to handle multiple windows, you need to decide which window sent you toWindowProcedure
. But Win32 API only sends you theHWND WindowHandle
. So I basically iterate through array of WINDOW structs (WindowArray
)and find the matching struct. But whenCreateWindow()
is called it sends some messages as well, which then callsWindowProcedure
beforeCreateWindow()
returns theWindowHandle
that I am trying to match with. So I created a global variableLastInitializedIndex
to keep track of the window that was just assigned an index in the array and simply use that whenCreateWindow()
sends its messages. -
What is problem with this code? The program compiles properly. When you run it, it displays the window with intended animation with correct size but there is no title bar and border and window stays on top of z-order but you can still click through and interect with other windows.
When I get rid of
Message == WM_NCDESTROY
in the WindowProcedure(), window is created unresponsive and then closes automatically, with title bar and border and part of client area is white and rest is black. WM_NCDESTROY is also sent even though documentation doesn’t say so. -
Why C like code in C++ program? C is simple enough to think in. Eventually I will add C++ features like constructors, namespaces and all as a part of refactoring. This is my style.
I am compiling using Visual Studio cl.exe and g++ and both give the same results.
console
----
cl -Zi program.cpp user32.lib gdi32.lib && program.exe
console
---
g++ -o program.exe program.cpp -lgdi32 -mwindows && program.exe
program.cpp
-----------
#include <windows.h>
#include <stdint.h>
#include <cstring>
#define WINDOW_MAX_COUNT 256
WNDCLASS WindowClass = {};
typedef struct {
bool Running;
uint8_t x;
} STATE;
typedef struct {
HWND Handle;
BITMAPINFO Info;
int Width;
int Height;
int XPos;
int YPos;
void* Bitmap;
char* Name;
} WINDOW;
STATE GameState = {};
bool WindowIndecesOccupied[WINDOW_MAX_COUNT] = {};
WINDOW WindowArray[WINDOW_MAX_COUNT] = {};
uint8_t LastInitializedIndex = 0;
void UpdateState(STATE* GameState){
GameState->x += 1;
}
void UpdateBitmap(STATE* GameState, WINDOW* Window){
uint32_t* Pixel = (uint32_t*) Window->Bitmap;
for(int Y = 0; Y < Window->Height; Y++){
for(int X = 0; X < Window->Width; X++){
uint8_t Red = 0;
uint8_t Green = 0;
uint8_t Blue = GameState->x;
*Pixel = (Red << 8*2) | (Green << 8*1) | (Blue << 8*0);
Pixel++;
}
}
}
void SetBitmapToWindow(WINDOW* Window){
HDC DeviceContext = GetDC(Window->Handle);
SetDIBitsToDevice(DeviceContext, 0, 0, Window->Width, Window->Height, 0, 0, 0, Window->Height, Window->Bitmap, &Window->Info, DIB_RGB_COLORS);
ReleaseDC(Window->Handle, DeviceContext);
}
WINDOW* InitWindow(int Width, int Height, int XPos, int YPos, char* WindowName){
WINDOW* Window;
for(uint8_t Index = 0; Index < WINDOW_MAX_COUNT; Index++){
if(!WindowIndecesOccupied[Index]){
WindowIndecesOccupied[Index] = true;
Window = &WindowArray[Index];
LastInitializedIndex = Index;
break;
}
}
Window->Width = Width;
Window->Height = Height;
Window->XPos = XPos;
Window->YPos = YPos;
Window->Name = WindowName;
Window->Info.bmiHeader.biSize = sizeof(Window->Info.bmiHeader);
Window->Info.bmiHeader.biWidth = Window->Width;
Window->Info.bmiHeader.biHeight = -Window->Height;
Window->Info.bmiHeader.biPlanes = 1;
Window->Info.bmiHeader.biBitCount = 32;
Window->Info.bmiHeader.biCompression = BI_RGB;
Window->Bitmap = VirtualAlloc(0, Window->Width * Window->Height * 4, MEM_COMMIT, PAGE_READWRITE);
Window->Handle = CreateWindow(WindowClass.lpszClassName, Window->Name, WS_OVERLAPPEDWINDOW, Window->XPos, Window->YPos, Window->Width, Window->Height, 0, 0, WindowClass.hInstance, 0);
return Window;
}
void UpdateWindowInfo(WINDOW* Window){
int PrevBitmapSize = Window->Width * Window->Height * 4;
RECT ClientRect;
GetClientRect(Window->Handle, &ClientRect);
Window->Width = ClientRect.right - ClientRect.left;
Window->Height = ClientRect.bottom - ClientRect.top;
Window->XPos = ClientRect.left;
Window->YPos = ClientRect.top;
Window->Info.bmiHeader.biWidth = Window->Width;
Window->Info.bmiHeader.biHeight = -Window->Height;
if (PrevBitmapSize != Window->Width * Window->Height * 4){
if(Window->Bitmap) VirtualFree(Window->Bitmap, 0, MEM_RELEASE);
Window->Bitmap = VirtualAlloc(0, Window->Width * Window->Height * 4, MEM_COMMIT, PAGE_READWRITE);
}
}
LRESULT CALLBACK WindowProcedure(HWND WindowHandle, uint32_t Message, WPARAM WParam, LPARAM LParam){
LRESULT Result = 0;
WINDOW* Window;
if(Message == WM_CREATE || Message == WM_GETMINMAXINFO || Message == WM_NCCREATE || Message == WM_NCDESTROY){
Window = &WindowArray[LastInitializedIndex];
} else {
for (uint8_t Index = 0; Index < WINDOW_MAX_COUNT; Index++){
if(WindowIndecesOccupied[Index] && WindowArray[Index].Handle == WindowHandle){
Window = &WindowArray[Index];
break;
}
}
}
switch(Message){
case WM_MOVE:
case WM_SIZE:{
UpdateWindowInfo(Window);
} break;
case WM_PAINT:{
SetBitmapToWindow(Window);
ValidateRect(Window->Handle, 0);
} break;
case WM_DESTROY:
case WM_CLOSE:
case WM_QUIT:{
GameState.Running = false;
PostQuitMessage(0);
} break;
default:{
Result = DefWindowProc(Window->Handle, Message, WParam, LParam);
} break;
}
return Result;
}
int WinMain(HINSTANCE Instance, HINSTANCE PrevInstance, LPSTR ComandLine, int ShowComand){
WindowClass.lpfnWndProc = WindowProcedure;
WindowClass.hInstance = Instance;
WindowClass.lpszClassName = "Espur Window Class";
if(RegisterClass(&WindowClass)){
WINDOW* Window = InitWindow(800, 800, 0, 0, "Espur Window");
if (Window->Handle) ShowWindow(Window->Handle, ShowComand);
GameState.Running = true;
while(GameState.Running){
MSG Message = {};
while(PeekMessage(&Message, 0, 0, 0, PM_REMOVE)){
TranslateMessage(&Message);
DispatchMessage(&Message);
}
UpdateBitmap(&GameState, Window);
UpdateState(&GameState);
SetBitmapToWindow(Window);
}
}
}
I want to know why the window is behaving the way it is and any suggestions on how to handle multiple windows at the same time. Or any resources that can help me design the entire library as well.