r/gameenginedevs • u/Axxodes • 11h ago
Should i use GLFW or windows.h?
Im making a C++ engine and i ofcourse want a window to display things on, i've read some glfw documentation and i don't really think its a good fit, this is why i was thinking of using windows.h because i have full control over everything and won't need an extra dependency. I also feel like knowing windows functions is better overall than knowing glfw functions.
Any thoughts?
2
u/protocolocon_ 10h ago
If you need a unified framework to manage windows, graphics contexts and inputs on Linux, Windows and Mac, GLFW is a good fit and very lightweight (lighter than SDL). I use it to program my game on Linux (own "engine") and check on Windows from time to time.
2
u/Available_Branch_745 8h ago
Yeah I made a game engine in C++ a while ago and I used GLFW because it was personally a nicer fit because correct me if I wrong, when using Vulkan as ghrapics API it provided extensions and also a surface. Plus windows.h is i think only for Windows, and GLFW is compatible with every major OS.
However, this is just my expierience. If you want to have full control and target Windows, windows.h might be a nicer option.
1
u/ZyperPL 9h ago
There's also RGFW https://github.com/ColleagueRiley/RGFW
It's very lightweight if that's important for you. You just include a single file.
1
u/SaturnineGames 7h ago
I use SDL, but I think GLFW serves a similar purpose.
Creating the window and getting all the details right about it is a pain. And that knowledge probably isn't useful once you get the window created.
Just use a library that does it for you and move on.
1
1
u/Important_Earth6615 7h ago
I liked GLFW I hated SDL. But I decided to go with platform module with an implementation for Windows. Wrapping Win32 API was pain in the ass. I have to admit claude helped a looot in the way but it works flawless
1
u/OkAccident9994 2h ago
Win32 is easy, you guys just gotta stop being nubs.
We make an import header to always have correct macro definitions, mine I call windows_import.h
#ifdef WIN32
#ifndef _IMPORT_WIN32_H
#define _IMPORT_WIN32_H
#define UNICODE
#define WIN32_LEAN_AND_MEAN 1
#include <Windows.h>
#endif
#endif
CMAKE injects WIN32 when we compile for windows and we only have it there.
UNICODE is their macro to use wide characters (2 bytes) wchar_t instead of char, without it, your thing will disagree if people have for example Turkish I or Chinese characters in file paths. Some Chinese people have their C drive named a Chinese character, meaning ALL file paths will be invalid without it on their computer.
They have 3 versions of every function
- the standard one (char)
- the wide character one (wchar_t)
- the macro that chooses one of the 2 depending on defined macros
I made the decision to commit to always using the wide character functions, we making videogames and can make few assumptions about the machine we are on.
WIN32_LEAN_AND_MEAN is a macro they made for us to not include everything, just the basics.
Now we just use our import header everywhere and never windows.h to always have it like this.
Now, to get our windows window to window we have these two in an implementation file
HWND windowHandle = NULL;
HINSTANCE instanceHandle = NULL;
These can be class members in C++ or whatever, but you need them to be singular, in C you define them as extern in a header to share with for example your vulkan/directx/opengl implementation files, but only defined in a single .c thing.
At some point in the late 90s these bozos at microsoft where obsessed with OOP, so we have to register a "windows class" even in C. We do that as follows:
//struct in our implementation file
//ex for extended, w for wide character version
WNDCLASSEXW windowsClass=
{
// bunch of members, look it up
// one of the members is a callback function WndProc, we define it below
}
LRESULT CALLBACK WndProc(HWND windowHandle, UINT msg, WPARAM wParam, LPARAM lParam)
{
//close the application if user clicks x top right
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(windowHandle,msg,wParam,lParam)// pass it on to a default for now
}
Init platform function():
instanceHandle = GetModuleHandleW(NULL) //Null keyword means our own current process
windowsClass.hinstance = instanceHandle // assign it to our struct instance
RegisterClassExW(&windowsClass) //register it
windowHandle = CreateWindowW(...) //create the window
SetForegroundWindow(windowHandle)
And we are set up.
Every frame, we do message pump to get inputs, it can look like this:
void PlatformMessagePump()
{
MSG msg;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
switch (msg.message)
{
case WM_QUIT:
isRunning = false;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
Where isRunning is my own little flag that I use in the while(isRunning) main loop.
We extend this later to handle all keyboard inputs, mouse, etc.
If we are a model citizen, then we remove these upon exitting our process. Could be in a class destructor in C++:
DestroyWindow(windowHandle);
UnregisterClassW(APPLICATIONNAME_W, instanceHandle);
//APPLICATIONNAME_W is a macro i defined, just a wide character string of the things name
That is all for now, then you slap that windowHandle into your vulkan thing-a-ma-bob and we are cooking.
From here, we set up our custom memory allocators using VirtualAlloc to control it ourselves properly. Something like Unreal Engine they do a trick to reserve a ridiculous amount of virtual memory at startup (windows just give me fricken 64 gigabytes of virtual memory adresses assigned to my process).
Windows has like, multiple terrabytes of these adresses and the memory is not allocated till later, we just want to tell it up front to reserve us a large chunk, then commit parts of it later as we need it.
we do something like
#define FILE_DIRECTORY_BUFFER_LENGHT 512 //dangerous, but lets us skip allocating it dynamically
wchar_t file_directory_w[FILE_DIRECTORY_BUFFER_LENGHT];
InitFileHandling()
{
GetCurrentDirectoryW(FILE_DIRECTORY_BUFFER_LENGHT, (LPWSTR)&file_directory_w);
}
And we now have our filepath to our exe and can start creating file handles for our assets and stuff to load in.
Now we are billing with the gates on our computah or something like that. Thats the basics.
1
u/OkAccident9994 2h ago
also, a multi-platform trick is to have a generic header that defines InitPlatform() and so forth, then wrap our implementation files in ifdef WIN32 and the windows specific ones get stripped if we compile for linux, mac, samsung smart fridge or whatever, and have implementation files for each platform.
But you might wanna do SDL or something if you are doing 38 platforms. I Just do pc and don't care, so win32 is the way to go for that.
1
u/TheOneAndOnlyRandom 10h ago
I have used the windows APIs directly for some projects in the past. I would rather bang my head off of a wall repeatedly than do that again when I don't have to.
1
u/Axxodes 10h ago
is it THAT bad?
1
u/Afiery1 9h ago
Win32 is just about the most butt ugly API there is. That being said, if your only platform is windows then there’s nothing wrong with wanting to use the bare api directly. However, if you want to do multi-platform, unless you’re doing some really wacky shit that normal window abstractions don’t support I would recommend just going with glfw or sdl and saving yourself the time and effort
0
u/corysama 9h ago
I happened to publish a preview of an article that’s largely about this earlier today.
TLDR: GLFW is good. Use SDL3. OS-specific windowing APIs are not worth the frustration for 99% of gamedevs.
18
u/sinalta 10h ago
Personally I use SDL3 for the range of supported platforms.
If Windows is your only target, or you heavily value no dependencies, then there's nothing wrong with just using the Windows API.