Windows build ready. Program icon, however, exceeds string length on MSVC.

This commit is contained in:
2026-05-27 00:07:06 -07:00
parent 5f663308d2
commit e72db80fb4
13 changed files with 270 additions and 141 deletions
+140 -96
View File
@@ -1,31 +1,47 @@
/* NOTE: This layer only supports MacOS and Linux, for now. */
#include "core.h"
// Remove SDL's entry point.
#if OS_WINDOWS
#define SDL_MAIN_HANDLED
#endif
#include "handmade.h"
#include "sdl_handmade.h"
#include <SDL.h>
#include <sys/mman.h> /* mmap */
#include <stdlib.h> /* malloc, calloc */
#include <string.h> /* memset, memcpy */
#include <stdio.h>
#include <sys/stat.h> /* fstat */
#include <fcntl.h> /* open */
#include <unistd.h> /* close, write, read, readlink, unlink */
#include <dlfcn.h> /* dlopen */
#include <sys/stat.h> /* stat */
#if OS_MACOS || OS_LINUX
#include <dlfcn.h> /* dlopen */
#include <sys/mman.h> /* mmap */
#include <unistd.h> /* close, write, read, readlink, unlink */
#endif
#if OS_WINDOWS
#include <windows.h> /* VirtualAlloc */
#endif
#if OS_MACOS
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
#endif
/* NOTE: This is so it compiles on ARM. */
#if ARCHITECTURE_X64 || ARCHITECTURE_X86
#include <x86intrin.h>
#if OS_MACOS || OS_LINUX
#include <x86intrin.h> // TODO: What is the equivalent on Windows?
#endif
#endif
#if 0
#include "program_icon.c"
#endif
#define RESOLUTION_WIDTH 960
#define RESOLUTION_HEIGHT 540
@@ -61,77 +77,65 @@ static U32 SafeTruncateUInt64(U64 Value)
debug_read_file_result DEBUGPlatformReadEntireFile(thread_context *Thread, const char *Filename)
{
(void)Thread;
debug_read_file_result Result;
memset(&Result, 0, sizeof(Result));
S32 FileHandle = open(Filename, O_RDONLY);
if(FileHandle == -1) {
return Result;
}
SDL_RWops *FileHandle = SDL_RWFromFile(Filename, "r");
if(FileHandle) {
S64 FileSize = SDL_RWsize(FileHandle);
if(FileSize >= 0) {
Result.ContentsSize = SafeTruncateUInt64(FileSize);
} else {
Result.ContentsSize = 0;
SDL_RWclose(FileHandle);
return Result;
}
struct stat FileStatus;
if(fstat(FileHandle, &FileStatus) == -1) {
close(FileHandle);
return Result;
}
Result.ContentsSize = SafeTruncateUInt64(FileStatus.st_size);
Result.Contents = malloc(Result.ContentsSize);
if(!Result.Contents) {
Result.ContentsSize = 0;
SDL_RWclose(FileHandle);
return Result;
}
Result.Contents = malloc(Result.ContentsSize);
if(!Result.Contents) {
Result.ContentsSize = 0;
close(FileHandle);
return Result;
}
U32 BytesToRead = Result.ContentsSize;
U8 *NextByteLocation = (U8 *)Result.Contents;
while(BytesToRead) {
U32 BytesRead = read(FileHandle, NextByteLocation, BytesToRead);
if(BytesRead == (U32)-1) {
size_t ObjectsRead = SDL_RWread(FileHandle, (void *)Result.Contents, (size_t)Result.ContentsSize, 1);
if(ObjectsRead == 0) {
free(Result.Contents);
Result.Contents = 0;
Result.ContentsSize = 0;
close(FileHandle);
SDL_RWclose(FileHandle);
return Result;
}
BytesToRead -= BytesRead;
NextByteLocation += BytesRead;
}
close(FileHandle);
SDL_RWclose(FileHandle);
}
return Result;
}
B32 DEBUGPlatformWriteEntireFile(thread_context *Thread, const char *Filename, U32 MemorySize, void *Memory)
{
S32 FileHandle = open(Filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(FileHandle == -1)
return FALSE;
U32 BytesToWrite = MemorySize;
U8 *NextByteLocation = (U8*)Memory;
while(BytesToWrite) {
U32 BytesWritten;
BytesWritten = write(FileHandle, NextByteLocation, BytesToWrite);
if(BytesWritten == (U32)-1) {
close(FileHandle);
SDL_RWops *FileHandle = SDL_RWFromFile(Filename, "w");
if(FileHandle) {
size_t ObjectsWritten = SDL_RWwrite(FileHandle, (void *)Memory, (size_t)MemorySize, 1);
if(ObjectsWritten == 0) {
SDL_RWclose(FileHandle);
return FALSE;
}
BytesToWrite -= BytesWritten;
NextByteLocation += BytesWritten;
SDL_RWclose(FileHandle);
return TRUE;
}
close(FileHandle);
return TRUE;
return FALSE;
}
void DEBUGPlatformFreeFileMemory(thread_context *Thread, void *Memory)
{
if(Memory)
free(Memory);
free(Memory); // TODO: Should we accept debug_read_file_result instead of just memory.
}
static sdl_window_size SDLGetWindowSize(SDL_Window *Window)
@@ -187,8 +191,8 @@ static void SDLDisplayBufferInWindow(offscreen_buffer *Buffer, SDL_Window *Windo
/* TODO: We temporarily introduce the target rectangle to avoid stretching the canvas. */
/* SDL_RenderCopy(Renderer, Buffer.Texture, 0, 0); */
sdl_window_size WindowSize = SDLGetWindowSize(Window);
if(WindowSize.Width >= Buffer->Width*2 && WindowSize.Height >= Buffer->Height*2) {
sdl_window_size WinSize = SDLGetWindowSize(Window);
if(WinSize.Width >= Buffer->Width*2 && WinSize.Height >= Buffer->Height*2) {
SDL_Rect dest_rect = { OffsetX, OffsetY, Buffer->Width*2, Buffer->Height*2 };
SDL_RenderCopy(Renderer, Buffer->Texture, 0, (const SDL_Rect *)(&dest_rect));
} else {
@@ -230,7 +234,7 @@ static void SDLInitSound(S32 SamplesPerSecond, S32 BufferSize)
AudioSettings.freq = SamplesPerSecond;
AudioSettings.format = AUDIO_S16LSB;
AudioSettings.channels = 2;
AudioSettings.samples = BufferSize;
AudioSettings.samples = (U16)BufferSize; // TODO: Unsafe truncate from S32 to U16
SDL_OpenAudio(&AudioSettings, 0);
@@ -381,10 +385,8 @@ static void SDLProcessMessages(sdl_state *SDLState, game_controller_input *Keybo
}
if(WasDown) {
/* NOTE: If your window manager already uses an Alt+F4
* keybind to close programs, then we are likely
* to see SDL_QUIT event occur before our
* keybind. */
/* NOTE: If your window manager already uses an Alt+F4 keybind to close programs, then we are likely
* to see SDL_QUIT event occur before our keybind. */
B32 AltKeyWasDown = (Event.key.keysym.mod & KMOD_ALT);
if(KeyCode == SDLK_F4 && AltKeyWasDown)
@@ -463,18 +465,38 @@ static U32 GetLastWriteTime(const char *FileName)
return (U32)file_info.st_mtime;
}
#if OS_MACOS || OS_LINUX
#define GAME_DLL_NAME "libhandmade.so"
#elif OS_WINDOWS
#define GAME_DLL_NAME "handmade.dll"
#else
#error Unknown OS: please specify GAME_DLL_NAME
#endif
static sdl_game_code SDLLoadGameCode(char *DLLPath)
{
sdl_game_code Result;
memset(&Result, 0, sizeof(Result));
#if OS_MACOS || OS_LINUX
Result.GameCodeDLL = dlopen(DLLPath, RTLD_NOW);
#elif OS_WINDOWS
Result.GameCodeDLL = LoadLibraryA(DLLPath);
#else
#error Unknown OS: implement DLL loading...
#endif
if(Result.GameCodeDLL) {
Result.DLLLastWriteTime = GetLastWriteTime(DLLPath);
Result.UpdateAndRender = (void(*)(thread_context *, game_memory *, game_input *, game_offscreen_buffer *, game_sound_output_buffer *)) dlsym(Result.GameCodeDLL, "UpdateAndRender");
#if OS_MACOS || OS_LINUX
Result.UpdateAndRender = (void(*)(thread_context *, game_memory *, game_input *, game_offscreen_buffer *, game_sound_output_buffer *))dlsym(Result.GameCodeDLL, "UpdateAndRender");
#elif OS_WINDOWS
Result.UpdateAndRender = (void(*)(thread_context *, game_memory *, game_input *, game_offscreen_buffer *, game_sound_output_buffer *))GetProcAddress(Result.GameCodeDLL, "UpdateAndRender");
#else
#error Unknown OS: implement DLL loading...
#endif
if(!Result.UpdateAndRender) {
/* TODO: Diagnostic. dlerror() */
}
@@ -493,8 +515,15 @@ static sdl_game_code SDLLoadGameCode(char *DLLPath)
static void SDLUnloadGameCode(sdl_game_code *GameCode)
{
if(GameCode->GameCodeDLL)
if(GameCode->GameCodeDLL) {
#if OS_MACOS || OS_LINUX
dlclose(GameCode->GameCodeDLL); /* NOTE: Might fail. */
#elif OS_WINDOWS
FreeLibrary(GameCode->GameCodeDLL);
#else
#error Unknown OS: implement closing of DLL
#endif
}
GameCode->IsValid = FALSE;
GameCode->UpdateAndRender = NULL;
}
@@ -503,7 +532,7 @@ void GetExecutablePath(char *path, size_t path_size)
{
#if OS_MACOS
U32 size;
#else
#elif OS_LINUX
ssize_t size;
#endif
@@ -512,18 +541,20 @@ void GetExecutablePath(char *path, size_t path_size)
#if OS_MACOS
size = path_size;
if(_NSGetExecutablePath(path, &size) != 0) { /* NOTE: Not an
absolute
path. */
if(_NSGetExecutablePath(path, &size) != 0) { /* NOTE: Not an absolute path. */
/* TODO: Diagnostics. */
}
#else
#elif OS_LINUX
size = readlink("/proc/self/exe", path, path_size - 1);
if(size != -1) {
path[size] = '\0';
} else {
/* TODO: Diagnostics. */
}
#elif OS_WINDOWS
GetModuleFileName(NULL, path, (DWORD)path_size); // TODO: hm...
#else
#error Unknown OS: Implement getting executable location.
#endif
}
}
@@ -533,7 +564,16 @@ static void SDLGetEXEDirPath(char *path, size_t path_size)
GetExecutablePath(path, path_size);
U32 len = str_len(path);
U32 i = len;
while(path[i] != '/' || i == 0) {
#if OS_MACOS || OS_LINUX
char delimeter = '/';
#elif OS_WINDOWS
char delimeter = '\\';
#else
#error Unknown OS: specify whether your OS uses forward or backward slashes for paths
#endif
while(path[i] != delimeter || i == 0) {
i--;
}
path[i+1] = '\0';
@@ -557,6 +597,7 @@ U64 __rdtsc()
* drawing into a bitmap. */
void SDLSetProgramIcon(SDL_Window *Window)
{
#if 0
U32 Rmask, Gmask, Bmask, Amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
S32 shift_by = (program_icon.bytes_per_pixel == 3) ? 8 : 0;
@@ -571,19 +612,15 @@ void SDLSetProgramIcon(SDL_Window *Window)
Amask = (program_icon.bytes_per_pixel == 3) ? 0 : 0xff000000;
#endif
SDL_Surface * icon = SDL_CreateRGBSurfaceFrom(
(void *)program_icon.pixel_data,
program_icon.width, program_icon.height,
program_icon.bytes_per_pixel * 8,
program_icon.bytes_per_pixel * program_icon.width,
Rmask, Gmask, Bmask, Amask);
SDL_Surface * icon = SDL_CreateRGBSurfaceFrom((void *)program_icon.pixel_data, program_icon.width, program_icon.height,
program_icon.bytes_per_pixel * 8, program_icon.bytes_per_pixel * program_icon.width, Rmask, Gmask, Bmask, Amask);
if(icon) {
SDL_SetWindowIcon(Window, icon);
SDL_FreeSurface(icon);
} else {
/* TODO: This didn't work . . . SDL_GetError() */
}
#endif
}
#include "handmade_intrinsics.h"
@@ -595,7 +632,7 @@ S32 main(S32 argc, char *argv[])
SDLGetEXEDirPath(SDLState.EXEDirPath, sizeof(SDLState.EXEDirPath));
SDLGetDLLPath(SDLState.DLLPath, sizeof(SDLState.DLLPath), SDLState.EXEDirPath);
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO)) {
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER)) {
/* TODO: This didn't work . . . */
}
@@ -616,9 +653,7 @@ S32 main(S32 argc, char *argv[])
SDL_Renderer *Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_PRESENTVSYNC);
/* NOTE: For future reference, the custom window bar should use
* SDL_SetWindowHitTest to define areas that will be used to
* drag the window. */
/* NOTE: For future reference, the custom window bar should use SDL_SetWindowHitTest to define areas that will be used to drag the window. */
/* "Wayland needs an event loop and rendering or it won't function."
* https://github.com/libsdl-org/SDL/issues/7699#issuecomment-1545684792 */
@@ -653,7 +688,7 @@ S32 main(S32 argc, char *argv[])
SoundOutput.LatencySampleCount = SoundOutput.SamplesPerSecond / 15;
SDLInitSound(SoundOutput.SamplesPerSecond, SoundOutput.SamplesPerSecond * SoundOutput.BytesPerSample / 60);
SDL_PauseAudio(0);
SDL_PauseAudio(0); // TODO: This should be paused until we have some actual sound to play.
SoundOutput.Samples = calloc(SoundOutput.SamplesPerSecond, SoundOutput.BytesPerSample);
/* SoundOutput.Samples =
@@ -672,13 +707,18 @@ S32 main(S32 argc, char *argv[])
GameMemory.PermanentStorageSize = MB(64);
GameMemory.TransientStorageSize = GB(1);
GameMemory.DEBUGPlatformReadEntireFile = &DEBUGPlatformReadEntireFile;
GameMemory.DEBUGPlatformWriteEntireFile = &DEBUGPlatformWriteEntireFile;
//GameMemory.DEBUGPlatformWriteEntireFile = &DEBUGPlatformWriteEntireFile;
GameMemory.DEBUGPlatformFreeFileMemory = &DEBUGPlatformFreeFileMemory;
SDLState.TotalSize = GameMemory.PermanentStorageSize + GameMemory.TransientStorageSize;
/* NOTE: On MacOS and Linux, mmap seems to zero-fill anonymous
* memory as a side-effect of security. */
#if OS_MACOS || OS_LINUX
/* NOTE: On MacOS and Linux, mmap seems to zero-fill anonymous memory as a side-effect of security. */
SDLState.GameMmemoryBlock = mmap(BaseAddress, (size_t)SDLState.TotalSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
#elif OS_WINDOWS
SDLState.GameMmemoryBlock = VirtualAlloc(BaseAddress, SDLState.TotalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#else
#error Unknown OS: please, allocate memory for arena
#endif
GameMemory.PermanentStorage = SDLState.GameMmemoryBlock;
GameMemory.TransientStorage = (U8 *)(GameMemory.PermanentStorage) + GameMemory.PermanentStorageSize;
@@ -701,18 +741,17 @@ S32 main(S32 argc, char *argv[])
sdl_game_code Game = SDLLoadGameCode(SDLState.DLLPath);
SDL_ShowWindow(Window); // NOTE: Everything is ready; display the window.
SDL_ShowWindow(Window); // Everything is ready; display the window.
U64 FPSLastCounter = SDLGetWallClock();
while(GlobalRunning) {
NewInput->dtForFrame = TargetSecondsPerFrame;
LastCounter = SDLGetWallClock();
/* NOTE: rdtsc reports clock cylces, however it is not
* meant for really precise profiler work as the
* value returned is varied. */
/* Also, __rdtsc is not available on MacOS ARM; I
* have not checked MacOS x64. */
/* NOTE: rdtsc reports clock cylces, however it is not meant for really precise profiler work as the
* value returned is varied. Also, __rdtsc is not available on MacOS ARM; I have not checked MacOS x64. */
LastCycleCount = __rdtsc();
U32 NewDLLWriteTime = GetLastWriteTime(SDLState.DLLPath);
@@ -842,7 +881,8 @@ S32 main(S32 argc, char *argv[])
if(SecondsElapsedForFrame < TargetSecondsPerFrame) {
while(SecondsElapsedForFrame < TargetSecondsPerFrame) {
SecondsElapsedForFrame = SDLGetSecondsElapsed(LastCounter, SDLGetWallClock());
SDL_Delay((U32)((TargetSecondsPerFrame - SecondsElapsedForFrame) * 1000.0));
if((TargetSecondsPerFrame - SecondsElapsedForFrame) >= 0.0f)
SDL_Delay((U32)((TargetSecondsPerFrame - SecondsElapsedForFrame) * 1000.0));
}
} else {
/* TODO: Missed frame rate! */
@@ -859,17 +899,21 @@ S32 main(S32 argc, char *argv[])
LastCounter = EndCounter;
#if 1
U64 EndCycleCount = __rdtsc();
U64 CyclesElapsed = EndCycleCount - LastCycleCount;
F64 MCPF = ((F64)CyclesElapsed / (1000.0 * 1000.0));
if(SDLGetSecondsElapsed(FPSLastCounter, SDLGetWallClock()) > 0.1f) {
U64 EndCycleCount = __rdtsc();
U64 CyclesElapsed = EndCycleCount - LastCycleCount;
F64 MCPF = ((F64)CyclesElapsed / (1000.0 * 1000.0));
//fprintf(stderr, "%.02f ms/f, %.02f f/s, %.02f mc/f\n", MSPerFrame, FPS, MCPF);
//fprintf(stderr, "%.02f ms/f, %.02f f/s, %.02f mc/f\n", MSPerFrame, FPS, MCPF);
static char fps_buffer[128];
snprintf(fps_buffer, sizeof(fps_buffer), "%.02f ms/f, %.02f f/s, %.02f mc/f", MSPerFrame, FPS, MCPF);
SDL_SetWindowTitle(Window, (const char *)fps_buffer);
static char fps_buffer[128];
snprintf(fps_buffer, sizeof(fps_buffer), "%.02f ms/f, %.02f f/s, %.02f mc/f", MSPerFrame, FPS, MCPF);
SDL_SetWindowTitle(Window, (const char *)fps_buffer);
LastCycleCount = EndCycleCount;
LastCycleCount = EndCycleCount;
FPSLastCounter = SDLGetWallClock();
}
#endif
SWAP(game_input *, OldInput, NewInput);