Shall not pass 80 characters in width.

This commit is contained in:
2026-03-22 11:34:06 -07:00
parent 8af1c59bb6
commit b6f7e027b6
5 changed files with 589 additions and 229 deletions
+21 -7
View File
@@ -15,16 +15,28 @@ endif
CC = clang CC = clang
UFLAGS = UFLAGS =
FLAGS = -ansi -D_POSIX_C_SOURCE=200809L -pedantic -pedantic-errors -Wall -Wextra -fno-caret-diagnostics -fno-show-column -Wno-missing-field-initializers -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable -Wno-unused-but-set-variable # NOTE: We comply with C89 (plain C), however the standard has no 64-bit
# -pedantic-errors # support. But, every compiler at that time supported long long,
# hence we ignore the incompliance in our code.
# NOTE: We allow function declrations with specifying void
# (ex, void test() is allowed).
FLAGS = -ansi -pedantic -pedantic-errors -Wno-long-long \
-Wno-strict-prototypes -Wall -Wextra -fno-caret-diagnostics \
-fno-show-column -Wno-missing-field-initializers \
-Wno-unused-function -Wno-unused-parameter -Wno-unused-variable \
-Wno-unused-but-set-variable
ifeq ($(DETECTED_OS),macos) ifeq ($(DETECTED_OS),macos)
UFLAGS = -gldb UFLAGS = -glldb
INCLUDE = -I./sdl/include/ INCLUDE = -I./sdl/include/
LIBS = ./sdl/lib/arm-macos/libSDL2.a -lm LIBS = ./sdl/lib/arm-macos/libSDL2.a -lm
FRAMEWORKS = -framework Cocoa -framework IOKit -framework CoreAudio -framework AudioToolbox -framework ForceFeedback -framework Carbon -framework GameController -framework Metal -framework QuartzCore -framework CoreHaptics FRAMEWORKS = -framework Cocoa -framework IOKit -framework CoreAudio \
-framework AudioToolbox -framework ForceFeedback \
-framework Carbon -framework GameController \
-framework Metal -framework QuartzCore \
-framework CoreHaptics
else ifeq ($(DETECTED_OS),linux) else ifeq ($(DETECTED_OS),linux)
UFLAGS = -ggdb UFLAGS = -ggdb -D_POSIX_C_SOURCE=200809L
INCLUDE = -I./sdl/include/ INCLUDE = -I./sdl/include/
LIBS = ./sdl/lib/x86_64-linux/libSDL2.a -lm -lX11 LIBS = ./sdl/lib/x86_64-linux/libSDL2.a -lm -lX11
FRAMEWORKS = FRAMEWORKS =
@@ -43,10 +55,12 @@ EXE=handmadehero
default: libhandmade $(EXE) default: libhandmade $(EXE)
libhandmade: handmade.c libhandmade: handmade.c
@ $(CC) $(FLAGS) $(UFLAGS) -fPIC -shared $(INCLUDE) $^ $(LIBS) $(FRAMEWORKS) -o libhandmade.so @ $(CC) $(FLAGS) $(UFLAGS) -fPIC -shared $(INCLUDE) $^ $(LIBS) \
$(FRAMEWORKS) -o libhandmade.so
$(EXE): sdl_handmade.c libhandmade $(EXE): sdl_handmade.c libhandmade
@ $(CC) $(FLAGS) $(UFLAGS) $(INCLUDE) sdl_handmade.c $(LIBS) $(FRAMEWORKS) -o $@ @ $(CC) $(FLAGS) $(UFLAGS) $(INCLUDE) sdl_handmade.c $(LIBS) \
$(FRAMEWORKS) -o $@
run: run:
./$(EXE) ./$(EXE)
+68 -32
View File
@@ -4,17 +4,27 @@
#include <math.h> #include <math.h>
static void GameOutputSound(game_state *GameState, game_sound_output_buffer *SoundBuffer, S32 ToneHz) static void GameOutputSound(game_state *GameState,
game_sound_output_buffer *SoundBuffer,
S32 ToneHz)
{ {
S16 ToneVolume = 8000; S16 ToneVolume, *SampleOut;
S32 WavePeriod = SoundBuffer->SamplesPerSecond / ToneHz; S32 WavePeriod, SampleIndex;
S16 *SampleOut = SoundBuffer->Samples; ToneVolume = 8000;
WavePeriod = SoundBuffer->SamplesPerSecond / ToneHz;
S32 SampleIndex; SampleOut = SoundBuffer->Samples;
for(SampleIndex = 0; SampleIndex < SoundBuffer->SampleCount; SampleIndex++) {
F32 SineValue = sinf(GameState->tSine); for(SampleIndex = 0;
S16 SampleValue = (S16)(SineValue * ToneVolume); SampleIndex < SoundBuffer->SampleCount;
SampleIndex++)
{
F32 SineValue;
S16 SampleValue;
SineValue = sinf(GameState->tSine);
SampleValue = (S16)(SineValue * ToneVolume);
*SampleOut++ = SampleValue; *SampleOut++ = SampleValue;
*SampleOut++ = SampleValue; *SampleOut++ = SampleValue;
@@ -25,19 +35,26 @@ static void GameOutputSound(game_state *GameState, game_sound_output_buffer *Sou
} }
} }
static void RenderWeirdGradient(game_offscreen_buffer *Buffer, S32 XOffset, S32 YOffset) static void RenderWeirdGradient(game_offscreen_buffer *Buffer, S32 XOffset,
S32 YOffset)
{ {
S32 Width = Buffer->Width; S32 Width, Height, Y;
S32 Height = Buffer->Height; U8 *Row;
U8 *Row = (U8 *)Buffer->Memory; Width = Buffer->Width;
S32 Y; Height = Buffer->Height;
Row = (U8 *)Buffer->Memory;
for(Y = 0; Y < Height; Y++) { for(Y = 0; Y < Height; Y++) {
U32 *Pixel = (U32 *)Row; U32 *Pixel = (U32 *)Row;
S32 X; S32 X;
Pixel = (U32 *)Row;
for(X = 0; X < Width; X++) { for(X = 0; X < Width; X++) {
U8 Blue = (U8)(X + XOffset); U8 Blue, Green;
U8 Green = (U8)(Y + YOffset);
Blue = (U8)(X + XOffset);
Green = (U8)(Y + YOffset);
*Pixel = (Green << 8) | Blue; *Pixel = (Green << 8) | Blue;
Pixel++; Pixel++;
@@ -46,18 +63,30 @@ static void RenderWeirdGradient(game_offscreen_buffer *Buffer, S32 XOffset, S32
} }
} }
void UpdateAndRender(game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer, game_sound_output_buffer *SoundBuffer) void UpdateAndRender(game_memory *Memory, game_input *Input,
game_offscreen_buffer *Buffer,
game_sound_output_buffer *SoundBuffer)
{ {
Assert((&Input->Controllers[0].Terminator - &Input->Controllers[0].Buttons[0]) == ARRAY_SIZE(Input->Controllers[0].Buttons)); game_state *GameState;
U32 ControllerIndex;
Assert((&Input->Controllers[0].u.buttons.Terminator -
&Input->Controllers[0].u.Buttons[0]) ==
ARRAY_SIZE(Input->Controllers[0].u.Buttons));
Assert(sizeof(game_state) <= Memory->PermanentStorageSize); Assert(sizeof(game_state) <= Memory->PermanentStorageSize);
game_state *GameState = (game_state *)Memory->PermanentStorage; GameState = (game_state *)Memory->PermanentStorage;
if(!Memory->IsInitialized) { if(!Memory->IsInitialized) {
#if BUILD_INTERNAL #if BUILD_INTERNAL
char FileName[] = "/home/igor/Developer/handmade/sdl_handmade.cpp"; char *FileName;
debug_read_file_result BitmapMemory = Memory->DEBUGPlatformReadEntireFile(FileName); debug_read_file_result BitmapMemory;
FileName = "/home/igor/Developer/handmade/sdl_handmade.cpp";
BitmapMemory = Memory->DEBUGPlatformReadEntireFile(FileName);
if(BitmapMemory.Contents) { if(BitmapMemory.Contents) {
Memory->DEBUGPlatformWriteEntireFile("test.out", BitmapMemory.ContentsSize, BitmapMemory.Contents); Memory->DEBUGPlatformWriteEntireFile("test.out",
BitmapMemory.ContentsSize,
BitmapMemory.Contents);
Memory->DEBUGPlatformFreeFileMemory(BitmapMemory.Contents); Memory->DEBUGPlatformFreeFileMemory(BitmapMemory.Contents);
} }
#endif #endif
@@ -72,29 +101,36 @@ void UpdateAndRender(game_memory *Memory, game_input *Input, game_offscreen_buff
/* GameState.GreenOffset; */ /* GameState.GreenOffset; */
/* GameState.BlueOffset; */ /* GameState.BlueOffset; */
U32 ControllerIndex; for(ControllerIndex = 0;
for(ControllerIndex = 0; ControllerIndex < ARRAY_SIZE(Input->Controllers); ControllerIndex++) { ControllerIndex < ARRAY_SIZE(Input->Controllers);
game_controller_input *Controller = GetController(Input, ControllerIndex); ControllerIndex++)
{
game_controller_input *Controller =
GetController(Input, ControllerIndex);
if(Controller->IsAnalog) { if(Controller->IsAnalog) {
GameState->GreenOffset += (int)(4.0f*(Controller->StickAverageY)); GameState->GreenOffset +=
GameState->BlueOffset += (int)(4.0f*(Controller->StickAverageX)); (int)(4.0f*(Controller->StickAverageY));
GameState->ToneHz = 256 + (S32)(128.0f*(Controller->StickAverageY)); GameState->BlueOffset +=
(int)(4.0f*(Controller->StickAverageX));
GameState->ToneHz =
256 + (S32)(128.0f*(Controller->StickAverageY));
} else { } else {
if(Controller->MoveUp.EndedDown) { if(Controller->u.buttons.MoveUp.EndedDown) {
GameState->GreenOffset -= 5; GameState->GreenOffset -= 5;
} }
if(Controller->MoveDown.EndedDown) { if(Controller->u.buttons.MoveDown.EndedDown) {
GameState->GreenOffset += 5; GameState->GreenOffset += 5;
} }
if(Controller->MoveLeft.EndedDown) { if(Controller->u.buttons.MoveLeft.EndedDown) {
GameState->BlueOffset -= 5; GameState->BlueOffset -= 5;
} }
if(Controller->MoveRight.EndedDown) { if(Controller->u.buttons.MoveRight.EndedDown) {
GameState->BlueOffset += 5; GameState->BlueOffset += 5;
} }
} }
} }
GameOutputSound(GameState, SoundBuffer, GameState->ToneHz); GameOutputSound(GameState, SoundBuffer, GameState->ToneHz);
RenderWeirdGradient(Buffer, GameState->BlueOffset, GameState->GreenOffset); RenderWeirdGradient(Buffer, GameState->BlueOffset,
GameState->GreenOffset);
} }
+21 -11
View File
@@ -14,7 +14,8 @@ typedef struct debug_read_file_result {
} debug_read_file_result; } debug_read_file_result;
debug_read_file_result DEBUGPlatformReadEntireFile(const char *Filename); debug_read_file_result DEBUGPlatformReadEntireFile(const char *Filename);
B32 DEBUGPlatformWriteEntireFile(const char *Filename, U32 MemorySize, void *Memory); B32 DEBUGPlatformWriteEntireFile(const char *Filename, U32 MemorySize,
void *Memory);
void DEBUGPlatformFreeFileMemory(void *Memory); void DEBUGPlatformFreeFileMemory(void *Memory);
#endif #endif
@@ -23,7 +24,8 @@ void DEBUGPlatformFreeFileMemory(void *Memory);
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
typedef struct game_offscreen_buffer { typedef struct game_offscreen_buffer {
/* Pixels are always 32-bit and have the bytes in BGRX (little-endian). */ /* Pixels are always 32-bit and have the bytes in BGRX
* (little-endian). */
void *Memory; void *Memory;
S32 Width; S32 Width;
S32 Height; S32 Height;
@@ -70,8 +72,8 @@ typedef struct game_controller_input {
/* NOTE: All buttons must be added above this line. */ /* NOTE: All buttons must be added above this line. */
game_button_state Terminator; game_button_state Terminator;
}; } buttons;
}; } u;
} game_controller_input; } game_controller_input;
typedef struct game_input { typedef struct game_input {
@@ -81,8 +83,10 @@ typedef struct game_input {
game_controller_input *GetController(game_input *Input, S32 ControllerIndex) game_controller_input *GetController(game_input *Input, S32 ControllerIndex)
{ {
game_controller_input *Result;
Assert((U32)ControllerIndex < ARRAY_SIZE(Input->Controllers)); Assert((U32)ControllerIndex < ARRAY_SIZE(Input->Controllers));
game_controller_input *Result = &(Input->Controllers[ControllerIndex]); Result = &(Input->Controllers[ControllerIndex]);
return Result; return Result;
} }
@@ -90,10 +94,14 @@ typedef struct game_memory {
B32 IsInitialized; B32 IsInitialized;
U64 PermanentStorageSize; U64 PermanentStorageSize;
void *PermanentStorage; /* WARNING: This memory is REQUIRED to be zero initialized!!! (Done by the OS layer.) */ void *PermanentStorage; /* WARNING: This memory is REQUIRED to be zero
initialized!!! (Done by the OS
layer.) */
U64 TransientStorageSize; U64 TransientStorageSize;
void *TransientStorage; /* WARNING: This memory is REQUIRED to be zero initialized!!! (Done by the OS layer.) */ void *TransientStorage; /* WARNING: This memory is REQUIRED to be zero
initialized!!! (Done by the OS
layer.) */
#if BUILD_INTERNAL #if BUILD_INTERNAL
debug_read_file_result (*DEBUGPlatformReadEntireFile)(const char *); debug_read_file_result (*DEBUGPlatformReadEntireFile)(const char *);
@@ -107,11 +115,13 @@ typedef struct game_memory {
F32 SecondsElapsed; F32 SecondsElapsed;
} game_clocks;*/ } game_clocks;*/
void UpdateAndRender(game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer, game_sound_output_buffer *SoundBuffer); void UpdateAndRender(game_memory *Memory, game_input *Input,
game_offscreen_buffer *Buffer,
game_sound_output_buffer *SoundBuffer);
/**/ /*
/**/ *
/**/ */
typedef struct game_state { typedef struct game_state {
S32 ToneHz; S32 ToneHz;
+477 -178
View File
@@ -37,20 +37,27 @@ static offscreen_buffer GlobalBackbuffer;
static U32 SafeTruncateUInt64(U64 Value) static U32 SafeTruncateUInt64(U64 Value)
{ {
U32 Result;
Assert(Value <= 0xFFFFFFFF); Assert(Value <= 0xFFFFFFFF);
U32 Result = (U32)Value; Result = (U32)Value;
return Result; return Result;
} }
debug_read_file_result DEBUGPlatformReadEntireFile(const char *Filename) debug_read_file_result DEBUGPlatformReadEntireFile(const char *Filename)
{ {
debug_read_file_result Result = {0}; debug_read_file_result Result;
S32 FileHandle = open(Filename, O_RDONLY); S32 FileHandle;
struct stat FileStatus;
U32 BytesToRead;
U8 *NextByteLocation;
memset(&Result, 0, sizeof(Result));
FileHandle = open(Filename, O_RDONLY);
if(FileHandle == -1) { if(FileHandle == -1) {
return Result; return Result;
} }
struct stat FileStatus;
if(fstat(FileHandle, &FileStatus) == -1) { if(fstat(FileHandle, &FileStatus) == -1) {
close(FileHandle); close(FileHandle);
return Result; return Result;
@@ -64,10 +71,12 @@ debug_read_file_result DEBUGPlatformReadEntireFile(const char *Filename)
return Result; return Result;
} }
U32 BytesToRead = Result.ContentsSize; BytesToRead = Result.ContentsSize;
U8 *NextByteLocation = (U8 *)Result.Contents; NextByteLocation = (U8 *)Result.Contents;
while(BytesToRead) { while(BytesToRead) {
U32 BytesRead = read(FileHandle, NextByteLocation, BytesToRead); U32 BytesRead;
BytesRead = read(FileHandle, NextByteLocation, BytesToRead);
if(BytesRead == (U32)-1) { if(BytesRead == (U32)-1) {
free(Result.Contents); free(Result.Contents);
Result.Contents = 0; Result.Contents = 0;
@@ -84,16 +93,24 @@ debug_read_file_result DEBUGPlatformReadEntireFile(const char *Filename)
return Result; return Result;
} }
B32 DEBUGPlatformWriteEntireFile(const char *Filename, U32 MemorySize, void *Memory) B32 DEBUGPlatformWriteEntireFile(const char *Filename, U32 MemorySize,
void *Memory)
{ {
S32 FileHandle = open(Filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); S32 FileHandle;
U32 BytesToWrite;
U8 *NextByteLocation;
FileHandle = open(Filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR |
S_IRGRP | S_IROTH);
if(FileHandle == -1) if(FileHandle == -1)
return FALSE; return FALSE;
U32 BytesToWrite = MemorySize; BytesToWrite = MemorySize;
U8 *NextByteLocation = (U8*)Memory; NextByteLocation = (U8*)Memory;
while(BytesToWrite) { while(BytesToWrite) {
U32 BytesWritten = write(FileHandle, NextByteLocation, BytesToWrite); U32 BytesWritten;
BytesWritten = write(FileHandle, NextByteLocation, BytesToWrite);
if(BytesWritten == (U32)-1) { if(BytesWritten == (U32)-1) {
close(FileHandle); close(FileHandle);
return FALSE; return FALSE;
@@ -121,17 +138,22 @@ static sdl_window_dimension SDLGetWindowDimension(SDL_Window *Window)
return Dimension; return Dimension;
} }
static void SDLResizeTexture(offscreen_buffer *Buffer, SDL_Renderer *Renderer, int Width, int Height) static void SDLResizeTexture(offscreen_buffer *Buffer,
SDL_Renderer *Renderer, int Width, int Height)
{ {
S32 BytesPerPixel;
if(Buffer->Texture) if(Buffer->Texture)
SDL_DestroyTexture(Buffer->Texture); SDL_DestroyTexture(Buffer->Texture);
if(Buffer->Memory) if(Buffer->Memory)
free(Buffer->Memory); free(Buffer->Memory);
S32 BytesPerPixel = 4; BytesPerPixel = 4;
Buffer->Texture = SDL_CreateTexture(Renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Width, Height); Buffer->Texture = SDL_CreateTexture(Renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
Width, Height);
Buffer->Memory = malloc(Width * Height * BytesPerPixel); Buffer->Memory = malloc(Width * Height * BytesPerPixel);
Buffer->Height = Height; Buffer->Height = Height;
@@ -139,7 +161,9 @@ static void SDLResizeTexture(offscreen_buffer *Buffer, SDL_Renderer *Renderer, i
Buffer->Pitch = Width * BytesPerPixel; Buffer->Pitch = Width * BytesPerPixel;
} }
static void SDLDisplayBufferInWindow(offscreen_buffer Buffer, SDL_Window *Window, SDL_Renderer *Renderer) static void SDLDisplayBufferInWindow(offscreen_buffer Buffer,
SDL_Window *Window,
SDL_Renderer *Renderer)
{ {
if(SDL_UpdateTexture(Buffer.Texture, 0, Buffer.Memory, Buffer.Pitch)) { if(SDL_UpdateTexture(Buffer.Texture, 0, Buffer.Memory, Buffer.Pitch)) {
/* TODO: Do something about this error! */ /* TODO: Do something about this error! */
@@ -151,14 +175,17 @@ static void SDLDisplayBufferInWindow(offscreen_buffer Buffer, SDL_Window *Window
static void SDLInitControllers() static void SDLInitControllers()
{ {
int MaxJoysticks = SDL_NumJoysticks(); S32 MaxJoysticks, ControllerIndex, JoystickIndex;
int ControllerIndex = 0;
for(int JoystickIndex = 0; JoystickIndex < MaxJoysticks; JoystickIndex++) { MaxJoysticks = SDL_NumJoysticks();
ControllerIndex = 0;
for(JoystickIndex = 0; JoystickIndex < MaxJoysticks; JoystickIndex++) {
if(!SDL_IsGameController(JoystickIndex)) if(!SDL_IsGameController(JoystickIndex))
continue; continue;
if(ControllerIndex >= MAX_CONTROLLERS) if(ControllerIndex >= MAX_CONTROLLERS)
break; break;
ControllerHandles[ControllerIndex] = SDL_GameControllerOpen(JoystickIndex); ControllerHandles[ControllerIndex] =
SDL_GameControllerOpen(JoystickIndex);
ControllerIndex++; ControllerIndex++;
} }
} }
@@ -166,17 +193,23 @@ static void SDLInitControllers()
/* /*
static void SDLDeinitControllers() static void SDLDeinitControllers()
{ {
for(int ControllerIndex = 0; ControllerIndex < MAX_CONTROLLERS; ControllerIndex++) { S32 ControllerIndex;
if (ControllerHandles[ControllerIndex])
for(ControllerIndex = 0;
ControllerIndex < MAX_CONTROLLERS;
ControllerIndex++)
{
if(ControllerHandles[ControllerIndex])
SDL_GameControllerClose(ControllerHandles[ControllerIndex]); SDL_GameControllerClose(ControllerHandles[ControllerIndex]);
} }
} }
*/ */
static void SDLInitSound(S32 SamplesPerSecond, S32 BufferSize) static void SDLInitSound(S32 SamplesPerSecond, S32 BufferSize)
{ {
SDL_AudioSpec AudioSettings = {0}; SDL_AudioSpec AudioSettings;
memset(&AudioSettings, 0, sizeof(AudioSettings));
AudioSettings.freq = SamplesPerSecond; AudioSettings.freq = SamplesPerSecond;
AudioSettings.format = AUDIO_S16LSB; AudioSettings.format = AUDIO_S16LSB;
@@ -190,7 +223,8 @@ static void SDLInitSound(S32 SamplesPerSecond, S32 BufferSize)
} }
} }
static void SDLFillSoundBuffer(sdl_sound_output *SoundOutput, S32 BytesToWrite) static void SDLFillSoundBuffer(sdl_sound_output *SoundOutput,
S32 BytesToWrite)
{ {
SDL_QueueAudio(1, SoundOutput->Samples, BytesToWrite); SDL_QueueAudio(1, SoundOutput->Samples, BytesToWrite);
} }
@@ -200,18 +234,22 @@ static void SDLClearSoundBuffer(sdl_sound_output *SoundOutput)
memset(SoundOutput->Samples, 0, SoundOutput->SecondaryBufferSize); memset(SoundOutput->Samples, 0, SoundOutput->SecondaryBufferSize);
} }
static void SDLProcessKeyboardMessage(game_button_state *NewState, B32 IsDown) static void SDLProcessKeyboardMessage(game_button_state *NewState,
B32 IsDown)
{ {
Assert(NewState->EndedDown != IsDown); Assert(NewState->EndedDown != IsDown);
NewState->EndedDown = IsDown; NewState->EndedDown = IsDown;
++NewState->HalfTransitionCount; ++NewState->HalfTransitionCount;
} }
static void SDLProcessInputDigitalButton(SDL_GameController *Controller, game_button_state *OldState, static void SDLProcessInputDigitalButton(SDL_GameController *Controller,
SDL_GameControllerButton Button, game_button_state *NewState) game_button_state *OldState,
SDL_GameControllerButton Button,
game_button_state *NewState)
{ {
NewState->EndedDown = SDL_GameControllerGetButton(Controller, Button); NewState->EndedDown = SDL_GameControllerGetButton(Controller, Button);
NewState->HalfTransitionCount = (OldState->EndedDown != NewState->EndedDown) ? 1 : 0; NewState->HalfTransitionCount =
(OldState->EndedDown != NewState->EndedDown) ? 1 : 0;
} }
static void HandleEvent(SDL_Event *Event) static void HandleEvent(SDL_Event *Event)
@@ -224,11 +262,16 @@ static void HandleEvent(SDL_Event *Event)
case SDL_WINDOWEVENT: { case SDL_WINDOWEVENT: {
switch(Event->window.event) { switch(Event->window.event) {
case SDL_WINDOWEVENT_SIZE_CHANGED: { case SDL_WINDOWEVENT_SIZE_CHANGED: {
SDL_Window *Window = SDL_GetWindowFromID(Event->window.windowID); SDL_Window *Window;
SDL_Renderer *Renderer = SDL_GetRenderer(Window); SDL_Renderer *Renderer;
sdl_window_dimension Dimension;
sdl_window_dimension Dimension = SDLGetWindowDimension(Window); Window = SDL_GetWindowFromID(Event->window.windowID);
SDLResizeTexture(&GlobalBackbuffer, Renderer, Dimension.Width, Dimension.Height); Renderer = SDL_GetRenderer(Window);
Dimension = SDLGetWindowDimension(Window);
SDLResizeTexture(&GlobalBackbuffer, Renderer,
Dimension.Width, Dimension.Height);
} break; } break;
case SDL_WINDOWEVENT_EXPOSED: { case SDL_WINDOWEVENT_EXPOSED: {
@@ -238,12 +281,18 @@ static void HandleEvent(SDL_Event *Event)
} }
} }
static void SDLBeginRecordingInput(sdl_state *SDLState, S32 InputRecordingIndex) static void SDLBeginRecordingInput(sdl_state *SDLState,
S32 InputRecordingIndex)
{ {
char *FileName;
SDLState->InputRecordingIndex = InputRecordingIndex; SDLState->InputRecordingIndex = InputRecordingIndex;
const char *FileName = "foo.hmi"; FileName = "foo.hmi";
SDLState->RecordingHandle = open(FileName, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); SDLState->RecordingHandle =
write(SDLState->RecordingHandle, SDLState->GameMmemoryBlock, (size_t)(SDLState->TotalSize)); open((const char *)FileName, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR |
S_IRGRP | S_IROTH);
write(SDLState->RecordingHandle, SDLState->GameMmemoryBlock,
(size_t)(SDLState->TotalSize));
} }
static void SDLEndRecordingInput(sdl_state *SDLState) static void SDLEndRecordingInput(sdl_state *SDLState)
@@ -252,11 +301,19 @@ static void SDLEndRecordingInput(sdl_state *SDLState)
SDLState->InputRecordingIndex = 0; SDLState->InputRecordingIndex = 0;
} }
static void SDLBeginInputPlayback(sdl_state *SDLState, S32 InputPlayingIndex) static void SDLBeginInputPlayback(sdl_state *SDLState,
S32 InputPlayingIndex)
{ {
ssize_t size;
char *FileName;
SDLState->InputPlayingIndex = InputPlayingIndex; SDLState->InputPlayingIndex = InputPlayingIndex;
const char *FileName = "foo.hmi"; FileName = "foo.hmi";
SDLState->PlaybackHandle = open(FileName, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); SDLState->PlaybackHandle =
open((const char *)FileName, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR |
S_IRGRP | S_IROTH);
size = read(SDLState->PlaybackHandle, SDLState->GameMmemoryBlock,
(size_t)(SDLState->TotalSize));
} }
static void SDLEndInputPlayback(sdl_state *SDLState) static void SDLEndInputPlayback(sdl_state *SDLState)
@@ -272,24 +329,32 @@ static void SDLRecordInput(sdl_state *SDLState, game_input *NewInput)
static void SDLPlaybackInput(sdl_state *SDLState, game_input *NewInput) static void SDLPlaybackInput(sdl_state *SDLState, game_input *NewInput)
{ {
ssize_t size = read(SDLState->PlaybackHandle, NewInput, sizeof(*NewInput)); ssize_t size;
if(size == EOF || size == -1) {
S32 PlayingIndex = SDLState->InputPlayingIndex; size = read(SDLState->PlaybackHandle, NewInput, sizeof(*NewInput));
if(size == 0 || size == -1) {
S32 PlayingIndex;
PlayingIndex = SDLState->InputPlayingIndex;
SDLEndInputPlayback(SDLState); SDLEndInputPlayback(SDLState);
SDLBeginInputPlayback(SDLState, PlayingIndex); SDLBeginInputPlayback(SDLState, PlayingIndex);
} }
} }
static void SDLProcessMessages(sdl_state *SDLState, game_controller_input *KeyboardController) static void SDLProcessMessages(sdl_state *SDLState,
game_controller_input *KeyboardController)
{ {
SDL_Event Event; SDL_Event Event;
while(SDL_PollEvent(&Event)) { while(SDL_PollEvent(&Event)) {
switch(Event.type) { switch(Event.type) {
case SDL_KEYDOWN: case SDL_KEYDOWN:
case SDL_KEYUP: { case SDL_KEYUP: {
SDL_Keycode KeyCode = Event.key.keysym.sym; SDL_Keycode KeyCode;
B32 IsDown = (Event.key.state == SDL_PRESSED); B32 IsDown, WasDown;
B32 WasDown = FALSE;
KeyCode = Event.key.keysym.sym;
IsDown = (Event.key.state == SDL_PRESSED);
WasDown = FALSE;
if(Event.key.state == SDL_RELEASED) { if(Event.key.state == SDL_RELEASED) {
WasDown = TRUE; WasDown = TRUE;
} else if(Event.key.repeat != 0) { } else if(Event.key.repeat != 0) {
@@ -298,34 +363,61 @@ static void SDLProcessMessages(sdl_state *SDLState, game_controller_input *Keybo
if(Event.key.repeat == 0) { if(Event.key.repeat == 0) {
if(KeyCode == SDLK_w) { if(KeyCode == SDLK_w) {
SDLProcessKeyboardMessage(&KeyboardController->MoveUp, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.MoveUp,
IsDown);
} else if(KeyCode == SDLK_s) { } else if(KeyCode == SDLK_s) {
SDLProcessKeyboardMessage(&KeyboardController->MoveDown, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.MoveDown,
IsDown);
} else if(KeyCode == SDLK_a) { } else if(KeyCode == SDLK_a) {
SDLProcessKeyboardMessage(&KeyboardController->MoveLeft, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.MoveLeft,
IsDown);
} else if(KeyCode == SDLK_d) { } else if(KeyCode == SDLK_d) {
SDLProcessKeyboardMessage(&KeyboardController->MoveRight, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.MoveRight,
IsDown);
} else if(KeyCode == SDLK_q) { } else if(KeyCode == SDLK_q) {
SDLProcessKeyboardMessage(&KeyboardController->LeftShoulder, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.LeftShoulder,
IsDown);
} else if(KeyCode == SDLK_e) { } else if(KeyCode == SDLK_e) {
SDLProcessKeyboardMessage(&KeyboardController->RightShoulder, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.RightShoulder,
IsDown);
} else if(KeyCode == SDLK_UP) { } else if(KeyCode == SDLK_UP) {
SDLProcessKeyboardMessage(&KeyboardController->ActionUp, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.ActionUp,
IsDown);
} else if(KeyCode == SDLK_DOWN) { } else if(KeyCode == SDLK_DOWN) {
SDLProcessKeyboardMessage(&KeyboardController->ActionDown, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.ActionDown,
IsDown);
} else if(KeyCode == SDLK_LEFT) { } else if(KeyCode == SDLK_LEFT) {
SDLProcessKeyboardMessage(&KeyboardController->ActionLeft, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.ActionLeft,
IsDown);
} else if(KeyCode == SDLK_RIGHT) { } else if(KeyCode == SDLK_RIGHT) {
SDLProcessKeyboardMessage(&KeyboardController->ActionRight, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.ActionRight,
IsDown);
} else if(KeyCode == SDLK_ESCAPE) { } else if(KeyCode == SDLK_ESCAPE) {
SDLProcessKeyboardMessage(&KeyboardController->Start, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.Start,
IsDown);
} else if(KeyCode == SDLK_SPACE) { } else if(KeyCode == SDLK_SPACE) {
SDLProcessKeyboardMessage(&KeyboardController->Back, IsDown); SDLProcessKeyboardMessage(
&KeyboardController->u.buttons.Back,
IsDown);
} }
} }
if(WasDown) { if(WasDown) {
B32 AltKeyWasDown;
#if BUILD_INTERNAL #if BUILD_INTERNAL
if(KeyCode == SDLK_l) { if(KeyCode == SDLK_l) {
if(SDLState->InputRecordingIndex == 0) { if(SDLState->InputRecordingIndex == 0) {
SDLBeginRecordingInput(SDLState, 1); SDLBeginRecordingInput(SDLState, 1);
@@ -336,8 +428,11 @@ static void SDLProcessMessages(sdl_state *SDLState, game_controller_input *Keybo
} }
#endif #endif
/* 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
B32 AltKeyWasDown = (Event.key.keysym.mod & KMOD_ALT); * keybind to close programs, then we are likely
* to see SDL_QUIT event occur before our
* keybind. */
AltKeyWasDown = (Event.key.keysym.mod & KMOD_ALT);
if(KeyCode == SDLK_F4 && AltKeyWasDown) if(KeyCode == SDLK_F4 && AltKeyWasDown)
GlobalRunning = FALSE; GlobalRunning = FALSE;
#if BUILD_INTERNAL #if BUILD_INTERNAL
@@ -356,24 +451,32 @@ static void SDLProcessMessages(sdl_state *SDLState, game_controller_input *Keybo
static F32 SDLProcessInputStickValue(F32 Value, S32 DeadZoneThreshold) static F32 SDLProcessInputStickValue(F32 Value, S32 DeadZoneThreshold)
{ {
F32 Result = 0.0f; F32 Result;
Result = 0.0f;
if(Value < -DeadZoneThreshold) if(Value < -DeadZoneThreshold)
Result = (F32)((Value + DeadZoneThreshold) / (32768.0f - DeadZoneThreshold)); Result = (F32)((Value + DeadZoneThreshold) /
(32768.0f - DeadZoneThreshold));
else if(Value > DeadZoneThreshold) else if(Value > DeadZoneThreshold)
Result = (F32)((Value + DeadZoneThreshold) / (32767.0f - DeadZoneThreshold)); Result = (F32)((Value + DeadZoneThreshold) /
(32767.0f - DeadZoneThreshold));
return Result; return Result;
} }
#define DEFAULT_REFRESH_RATE 60 #define DEFAULT_REFRESH_RATE 60
static S32 SDLGetWindowRefreshRate(SDL_Window *Window) static S32 SDLGetWindowRefreshRate(SDL_Window *Window)
{ {
int DisplayIndex = SDL_GetWindowDisplayIndex(Window); S32 DisplayIndex;
SDL_DisplayMode Mode; SDL_DisplayMode Mode;
DisplayIndex = SDL_GetWindowDisplayIndex(Window);
if(SDL_GetDesktopDisplayMode(DisplayIndex, &Mode) != 0) if(SDL_GetDesktopDisplayMode(DisplayIndex, &Mode) != 0)
return DEFAULT_REFRESH_RATE; return DEFAULT_REFRESH_RATE;
if(Mode.refresh_rate == 0) if(Mode.refresh_rate == 0)
return DEFAULT_REFRESH_RATE; return DEFAULT_REFRESH_RATE;
return Mode.refresh_rate; return Mode.refresh_rate;
} }
@@ -391,15 +494,18 @@ typedef struct sdl_game_code {
void *GameCodeDLL; void *GameCodeDLL;
U32 DLLLastWriteTime; U32 DLLLastWriteTime;
void (*UpdateAndRender)(game_memory *, game_input *, game_offscreen_buffer *, game_sound_output_buffer *); void (*UpdateAndRender)(game_memory *, game_input *,
game_offscreen_buffer *,
game_sound_output_buffer *);
B32 IsValid; B32 IsValid;
} sdl_game_code; } sdl_game_code;
static U32 GetLastWriteTime(const char *FileName) static U32 GetLastWriteTime(const char *FileName)
{ {
struct stat file_info = {0}; struct stat file_info;
memset(&file_info, 0, sizeof(file_info));
if(stat(FileName, &file_info) != 0) { if(stat(FileName, &file_info) != 0) {
/* TODO: Diagnostic. perror("stat failed"); */ /* TODO: Diagnostic. perror("stat failed"); */
} }
@@ -411,13 +517,17 @@ static U32 GetLastWriteTime(const char *FileName)
static sdl_game_code SDLLoadGameCode(char *DLLPath) static sdl_game_code SDLLoadGameCode(char *DLLPath)
{ {
sdl_game_code Result = {0}; sdl_game_code Result;
memset(&Result, 0, sizeof(Result));
Result.GameCodeDLL = dlopen(DLLPath, RTLD_NOW); Result.GameCodeDLL = dlopen(DLLPath, RTLD_NOW);
if(Result.GameCodeDLL) { if(Result.GameCodeDLL) {
Result.DLLLastWriteTime = GetLastWriteTime(DLLPath); Result.DLLLastWriteTime = GetLastWriteTime(DLLPath);
Result.UpdateAndRender = (void(*)(game_memory *, game_input *, game_offscreen_buffer *, game_sound_output_buffer *))dlsym(Result.GameCodeDLL, "UpdateAndRender"); Result.UpdateAndRender =
(void(*)(game_memory *, game_input *, game_offscreen_buffer *,
game_sound_output_buffer *))dlsym(Result.GameCodeDLL,
"UpdateAndRender");
if(!Result.UpdateAndRender) { if(!Result.UpdateAndRender) {
/* TODO: Diagnostic. dlerror() */ /* TODO: Diagnostic. dlerror() */
} }
@@ -444,16 +554,27 @@ static void SDLUnloadGameCode(sdl_game_code *GameCode)
char *GetExecutablePath() char *GetExecutablePath()
{ {
static char path[1024] = ""; /* NOTE: We choose 1024 only because MacOS's MAXPATHLEN says such a value. For example, linux/limits.h PATH_MAX says 4096. */ static char path[1024];
size_t path_size = sizeof(path); size_t path_size;
#if __APPLE__ && __MACH__
U32 size;
#else
ssize_t size;
#endif
/* NOTE: We choose 1024 only because MacOS's MAXPATHLEN says such a
* value. For example, linux/limits.h PATH_MAX says 4096. */
path[0] = '\0';
path_size = sizeof(path);
#if __APPLE__ && __MACH__ #if __APPLE__ && __MACH__
U32 size = path_size; 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. */ /* TODO: Diagnostics. */
} }
#else #else
ssize_t size = readlink("/proc/self/exe", path, path_size - 1); size = readlink("/proc/self/exe", path, path_size - 1);
if(size != -1) { if(size != -1) {
path[size] = '\0'; path[size] = '\0';
} else { } else {
@@ -466,51 +587,48 @@ char *GetExecutablePath()
S32 main(S32 argc, char *argv[]) S32 main(S32 argc, char *argv[])
{ {
char *path = GetExecutablePath(); char *path;
U32 len = strlen(path); U32 len, i;
U32 i = len; char game_dll_path[1024];
SDL_Window *Window;
path = GetExecutablePath();
len = strlen(path);
i = len;
while(path[i] != '/' || i == 0) { while(path[i] != '/' || i == 0) {
i--; i--;
} }
path[i+1] = '\0'; path[i+1] = '\0';
char game_dll_path[1024]; snprintf(game_dll_path, sizeof(game_dll_path), "%s%s", path,
snprintf(game_dll_path, sizeof(game_dll_path), "%s%s", path, GAME_DLL_NAME); GAME_DLL_NAME);
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO)) { if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER |
SDL_INIT_AUDIO))
{
/* TODO: This didn't work . . . */ /* TODO: This didn't work . . . */
} }
SDLInitControllers(); SDLInitControllers();
SDL_Window *Window = SDL_CreateWindow("Handmade Hero", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 700, 500, SDL_WINDOW_RESIZABLE); Window = SDL_CreateWindow("Handmade Hero", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, 700, 500,
SDL_WINDOW_RESIZABLE);
if(Window) { if(Window) {
/*SDL_Renderer *Renderer = SDL_CreateRenderer(Window, -1, 0); */ SDL_Renderer *Renderer;
SDL_Renderer *Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_PRESENTVSYNC);
/* "Wayland needs an event loop and rendering or it won't function." https://github.com/libsdl-org/SDL/issues/7699#issuecomment-1545684792 */ Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_PRESENTVSYNC);
/* NOTE: This means that we need to readraw to even resize the window, otherwise the window frame will appear outdated to its actual size. */ /* "Wayland needs an event loop and rendering or it won't function."
* https://github.com/libsdl-org/SDL/issues/7699#issuecomment-1545684792 */
/* NOTE: This means that we need to readraw to even resize the
* window, otherwise the window frame will appear outdated to
* its actual size. */
/* SDL_RenderPresent(Renderer) is enough to display the window. */ /* SDL_RenderPresent(Renderer) is enough to display the window. */
if(Renderer) { if(Renderer) {
sdl_state SDLState = {0}; sdl_state SDLState;
GlobalRunning = TRUE; sdl_window_dimension Dimension;
sdl_sound_output SoundOutput;
sdl_window_dimension Dimension = SDLGetWindowDimension(Window); game_memory GameMemory;
SDLResizeTexture(&GlobalBackbuffer, Renderer, Dimension.Width, Dimension.Height);
sdl_sound_output SoundOutput = {0};
SoundOutput.SamplesPerSecond = 48000;
SoundOutput.BytesPerSample = sizeof(S16) * 2;
SoundOutput.SecondaryBufferSize = SoundOutput.SamplesPerSecond * SoundOutput.BytesPerSample;
SoundOutput.tSine = 0;
SoundOutput.LatencySampleCount = SoundOutput.SamplesPerSecond / 15;
SDLInitSound(SoundOutput.SamplesPerSecond, SoundOutput.SamplesPerSecond * SoundOutput.BytesPerSample / 60);
SDL_PauseAudio(0);
SoundOutput.Samples = calloc(SoundOutput.SamplesPerSecond, SoundOutput.BytesPerSample);
/* SoundOutput.Samples = malloc(SoundOutput.SecondaryBufferSize); */
/* SDLClearSoundBuffer(&SoundOutput); */ /* NOTE: calloc auto clears to zero */
#if BUILD_DEBUG #if BUILD_DEBUG
void *BaseAddress = (void *)TB(2); void *BaseAddress = (void *)TB(2);
@@ -518,129 +636,299 @@ S32 main(S32 argc, char *argv[])
void *BaseAddress = (void *)(0); void *BaseAddress = (void *)(0);
#endif #endif
game_memory GameMemory = {0}; memset(&SDLState, 0, sizeof(SDLState));
GlobalRunning = TRUE;
Dimension = SDLGetWindowDimension(Window);
SDLResizeTexture(&GlobalBackbuffer, Renderer, Dimension.Width,
Dimension.Height);
memset(&SoundOutput, 0, sizeof(SoundOutput));
SoundOutput.SamplesPerSecond = 48000;
SoundOutput.BytesPerSample = sizeof(S16) * 2;
SoundOutput.SecondaryBufferSize =
SoundOutput.SamplesPerSecond * SoundOutput.BytesPerSample;
SoundOutput.tSine = 0;
SoundOutput.LatencySampleCount =
SoundOutput.SamplesPerSecond / 15;
SDLInitSound(SoundOutput.SamplesPerSecond,
SoundOutput.SamplesPerSecond * SoundOutput.BytesPerSample /
60);
SDL_PauseAudio(0);
SoundOutput.Samples =
calloc(SoundOutput.SamplesPerSecond,
SoundOutput.BytesPerSample);
/* SoundOutput.Samples =
* malloc(SoundOutput.SecondaryBufferSize); */
/* NOTE: calloc auto clears to zero */
/* SDLClearSoundBuffer(&SoundOutput); */
memset(&GameMemory, 0, sizeof(GameMemory));
GameMemory.PermanentStorageSize = MB(64); GameMemory.PermanentStorageSize = MB(64);
GameMemory.TransientStorageSize = GB(4); GameMemory.TransientStorageSize = GB(4);
GameMemory.DEBUGPlatformReadEntireFile = &DEBUGPlatformReadEntireFile; GameMemory.DEBUGPlatformReadEntireFile =
GameMemory.DEBUGPlatformWriteEntireFile = &DEBUGPlatformWriteEntireFile; &DEBUGPlatformReadEntireFile;
GameMemory.DEBUGPlatformFreeFileMemory = &DEBUGPlatformFreeFileMemory; GameMemory.DEBUGPlatformWriteEntireFile =
&DEBUGPlatformWriteEntireFile;
GameMemory.DEBUGPlatformFreeFileMemory =
&DEBUGPlatformFreeFileMemory;
SDLState.TotalSize = GameMemory.PermanentStorageSize + GameMemory.TransientStorageSize; SDLState.TotalSize =
SDLState.GameMmemoryBlock = mmap(BaseAddress, SDLState.TotalSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); /* NOTE: On MacOS and Linux, mmap seems to zero-fill anonymous memory as a side-effect of security. */ GameMemory.PermanentStorageSize +
GameMemory.TransientStorageSize;
/* NOTE: On MacOS and Linux, mmap seems to zero-fill anonymous
* memory as a side-effect of security. */
SDLState.GameMmemoryBlock =
mmap(BaseAddress, SDLState.TotalSize, PROT_READ |
PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
GameMemory.PermanentStorage = SDLState.GameMmemoryBlock; GameMemory.PermanentStorage = SDLState.GameMmemoryBlock;
GameMemory.TransientStorage = (U8 *)(GameMemory.PermanentStorage) + GameMemory.PermanentStorageSize; GameMemory.TransientStorage =
(U8 *)(GameMemory.PermanentStorage) +
GameMemory.PermanentStorageSize;
if(SoundOutput.Samples && GameMemory.PermanentStorage && GameMemory.TransientStorage) { if(SoundOutput.Samples &&
GameMemory.PermanentStorage &&
GameMemory.TransientStorage)
{
game_input Input[2]; game_input Input[2];
game_input *NewInput = &Input[0]; game_input *NewInput, *OldInput;
game_input *OldInput = &Input[1]; sdl_game_code Game;
memset(&Input, 0, sizeof(Input));
GlobalPerfCountFrequency = SDL_GetPerformanceFrequency();
U64 LastCounter; U64 LastCounter;
#if __x86_64__ || __i386__ #if __x86_64__ || __i386__
U64 LastCycleCount; U64 LastCycleCount;
#endif #endif
NewInput = &Input[0];
OldInput = &Input[1];
memset(&Input, 0, sizeof(Input));
sdl_game_code Game = SDLLoadGameCode(game_dll_path); GlobalPerfCountFrequency = SDL_GetPerformanceFrequency();
Game = SDLLoadGameCode(game_dll_path);
while(GlobalRunning) { while(GlobalRunning) {
S32 MonitorRefreshHz, GameUpdateHz;
F32 TargetSecondsPerFrame;
U32 NewDLLWriteTime, ButtonIndex, ControllerIndex;
game_controller_input *OldKeyboardController,
*NewKeyboardController;
game_controller_input ZeroController;
S32 TargetQueueBytes, BytesToWrite;
game_sound_output_buffer SoundBuffer;
game_offscreen_buffer Buffer;
U64 WorkCounter, EndCounter;
F32 WorkSecondsElapsed, SecondsElapsedForFrame;
F64 MSPerFrame, FPS;
#if __x86_64__ || __i386__
U64 EndCycleCount, CyclesElapsed;
F64 MCPF;
#endif
LastCounter = SDLGetWallClock(); LastCounter = SDLGetWallClock();
#if __x86_64__ || __i386__ #if __x86_64__ || __i386__
LastCycleCount = __rdtsc(); /* NOTE: rdtsc reports clock cylces, however it is not meant for really precise profiler work as the value returned is varied. */ /* NOTE: rdtsc reports clock cylces, however it is not
#endif /* Also, __rdtsc is not available on MacOS ARM; I have not checked MacOS x64. */ * 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();
#endif
S32 MonitorRefreshHz = SDLGetWindowRefreshRate(Window); MonitorRefreshHz = SDLGetWindowRefreshRate(Window);
S32 GameUpdateHz = MonitorRefreshHz; GameUpdateHz = MonitorRefreshHz;
F32 TargetSecondsPerFrame = 1.0f / (F32)GameUpdateHz; TargetSecondsPerFrame = 1.0f / (F32)GameUpdateHz;
U32 NewDLLWriteTime = GetLastWriteTime(game_dll_path); NewDLLWriteTime = GetLastWriteTime(game_dll_path);
if(NewDLLWriteTime > Game.DLLLastWriteTime) { if(NewDLLWriteTime > Game.DLLLastWriteTime) {
SDLUnloadGameCode(&Game); SDLUnloadGameCode(&Game);
Game = SDLLoadGameCode(game_dll_path); Game = SDLLoadGameCode(game_dll_path);
} }
game_controller_input *OldKeyboardController = GetController(OldInput, 0); OldKeyboardController = GetController(OldInput, 0);
game_controller_input *NewKeyboardController = GetController(NewInput, 0); NewKeyboardController = GetController(NewInput, 0);
game_controller_input ZeroController = {0}; memset(&ZeroController, 0, sizeof(ZeroController));
*NewKeyboardController = ZeroController; *NewKeyboardController = ZeroController;
NewKeyboardController->IsConnected = TRUE; NewKeyboardController->IsConnected = TRUE;
for(unsigned int ButtonIndex = 0; ButtonIndex < ARRAY_SIZE(NewKeyboardController->Buttons); ButtonIndex++) { for(ButtonIndex = 0;
NewKeyboardController->Buttons[ButtonIndex].EndedDown = OldKeyboardController->Buttons[ButtonIndex].EndedDown; ButtonIndex < ARRAY_SIZE(
NewKeyboardController->u.Buttons);
ButtonIndex++)
{
NewKeyboardController->
u.Buttons[ButtonIndex].EndedDown =
OldKeyboardController->
u.Buttons[ButtonIndex].
EndedDown;
} }
SDLProcessMessages(&SDLState, NewKeyboardController); SDLProcessMessages(&SDLState, NewKeyboardController);
for(unsigned int ControllerIndex = 0; ControllerIndex < MAX_CONTROLLERS; ControllerIndex++) { for(ControllerIndex = 0;
game_controller_input *OldController = GetController(OldInput, ControllerIndex+1); ControllerIndex < MAX_CONTROLLERS;
game_controller_input *NewController = GetController(NewInput, ControllerIndex+1); ControllerIndex++)
{
game_controller_input *OldController, *NewController;
OldController =
GetController(OldInput, ControllerIndex+1);
NewController =
GetController(NewInput, ControllerIndex+1);
if(ControllerHandles[ControllerIndex] != 0 &&
SDL_GameControllerGetAttached(
ControllerHandles[ControllerIndex]))
{
S16 StickX, StickY;
float Threshold;
if(ControllerHandles[ControllerIndex] != 0 && SDL_GameControllerGetAttached(ControllerHandles[ControllerIndex])) {
NewController->IsConnected = TRUE; NewController->IsConnected = TRUE;
S16 StickX = SDL_GameControllerGetAxis(ControllerHandles[ControllerIndex], SDL_CONTROLLER_AXIS_LEFTX); StickX =
S16 StickY = SDL_GameControllerGetAxis(ControllerHandles[ControllerIndex], SDL_CONTROLLER_AXIS_LEFTY); SDL_GameControllerGetAxis(
ControllerHandles[ControllerIndex],
SDL_CONTROLLER_AXIS_LEFTX);
StickY =
SDL_GameControllerGetAxis(
ControllerHandles[ControllerIndex],
SDL_CONTROLLER_AXIS_LEFTY);
NewController->IsAnalog = TRUE; NewController->IsAnalog = TRUE;
NewController->StickAverageX = SDLProcessInputStickValue((F32)StickX, CONTROLLER_LEFT_THUMB_DEADZONE); NewController->StickAverageX =
NewController->StickAverageY = SDLProcessInputStickValue((F32)StickY, CONTROLLER_LEFT_THUMB_DEADZONE); SDLProcessInputStickValue((F32)StickX,
CONTROLLER_LEFT_THUMB_DEADZONE);
NewController->StickAverageY =
SDLProcessInputStickValue((F32)StickY,
CONTROLLER_LEFT_THUMB_DEADZONE);
if((NewController->StickAverageX != 0.0f) || (NewController->StickAverageY != 0.0f)) { if((NewController->StickAverageX != 0.0f) ||
(NewController->StickAverageY != 0.0f))
{
NewController->IsAnalog = TRUE; NewController->IsAnalog = TRUE;
} }
if(SDL_GameControllerGetButton(ControllerHandles[ControllerIndex], SDL_CONTROLLER_BUTTON_DPAD_UP)) { if(SDL_GameControllerGetButton(
ControllerHandles[ControllerIndex],
SDL_CONTROLLER_BUTTON_DPAD_UP))
{
NewController->StickAverageY = -1.0f; NewController->StickAverageY = -1.0f;
NewController->IsAnalog = FALSE; NewController->IsAnalog = FALSE;
} }
if(SDL_GameControllerGetButton(ControllerHandles[ControllerIndex], SDL_CONTROLLER_BUTTON_DPAD_DOWN)) { if(SDL_GameControllerGetButton(
ControllerHandles[ControllerIndex],
SDL_CONTROLLER_BUTTON_DPAD_DOWN))
{
NewController->StickAverageY = 1.0f; NewController->StickAverageY = 1.0f;
NewController->IsAnalog = FALSE; NewController->IsAnalog = FALSE;
} }
if(SDL_GameControllerGetButton(ControllerHandles[ControllerIndex], SDL_CONTROLLER_BUTTON_DPAD_LEFT)) { if(SDL_GameControllerGetButton(
ControllerHandles[ControllerIndex],
SDL_CONTROLLER_BUTTON_DPAD_LEFT))
{
NewController->StickAverageX = -1.0f; NewController->StickAverageX = -1.0f;
NewController->IsAnalog = FALSE; NewController->IsAnalog = FALSE;
} }
if(SDL_GameControllerGetButton(ControllerHandles[ControllerIndex], SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) { if(SDL_GameControllerGetButton(
ControllerHandles[ControllerIndex],
SDL_CONTROLLER_BUTTON_DPAD_RIGHT))
{
NewController->StickAverageX = 1.0f; NewController->StickAverageX = 1.0f;
NewController->IsAnalog = FALSE; NewController->IsAnalog = FALSE;
} }
float Threshold = 0.5f; Threshold = 0.5f;
SDLProcessInputDigitalButton(ControllerHandles[(NewController->StickAverageY < -Threshold) ? 1 : 0], &OldController->MoveUp, SDL_CONTROLLER_BUTTON_A, &NewController->MoveUp); SDLProcessInputDigitalButton(
SDLProcessInputDigitalButton(ControllerHandles[(NewController->StickAverageY > Threshold) ? 1 : 0], &OldController->MoveDown, SDL_CONTROLLER_BUTTON_A, &NewController->MoveDown); ControllerHandles[
SDLProcessInputDigitalButton(ControllerHandles[(NewController->StickAverageX < -Threshold) ? 1 : 0], &OldController->MoveLeft, SDL_CONTROLLER_BUTTON_A, &NewController->MoveLeft); (NewController->StickAverageY <
SDLProcessInputDigitalButton(ControllerHandles[(NewController->StickAverageX > Threshold) ? 1 : 0], &OldController->MoveRight, SDL_CONTROLLER_BUTTON_A, &NewController->MoveRight); -Threshold) ? 1 : 0],
&OldController->u.buttons.MoveUp,
SDL_CONTROLLER_BUTTON_A,
&NewController->u.buttons.MoveUp);
SDLProcessInputDigitalButton(
ControllerHandles[
(NewController->StickAverageY >
Threshold) ? 1 : 0],
&OldController->u.buttons.MoveDown,
SDL_CONTROLLER_BUTTON_A,
&NewController->u.buttons.MoveDown);
SDLProcessInputDigitalButton(
ControllerHandles[
(NewController->StickAverageX <
-Threshold) ? 1 : 0],
&OldController->u.buttons.MoveLeft,
SDL_CONTROLLER_BUTTON_A,
&NewController->u.buttons.MoveLeft);
SDLProcessInputDigitalButton(
ControllerHandles[
(NewController->StickAverageX >
Threshold) ? 1 : 0],
&OldController->u.buttons.MoveRight,
SDL_CONTROLLER_BUTTON_A,
&NewController->u.buttons.MoveRight);
SDLProcessInputDigitalButton(ControllerHandles[ControllerIndex], &OldController->ActionDown, SDL_CONTROLLER_BUTTON_A, &NewController->ActionDown); SDLProcessInputDigitalButton(
SDLProcessInputDigitalButton(ControllerHandles[ControllerIndex], &OldController->ActionRight, SDL_CONTROLLER_BUTTON_B, &NewController->ActionRight); ControllerHandles[ControllerIndex],
SDLProcessInputDigitalButton(ControllerHandles[ControllerIndex], &OldController->ActionLeft, SDL_CONTROLLER_BUTTON_X, &NewController->ActionLeft); &OldController->u.buttons.ActionDown,
SDLProcessInputDigitalButton(ControllerHandles[ControllerIndex], &OldController->ActionUp, SDL_CONTROLLER_BUTTON_Y, &NewController->ActionUp); SDL_CONTROLLER_BUTTON_A,
SDLProcessInputDigitalButton(ControllerHandles[ControllerIndex], &OldController->LeftShoulder, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, &NewController->LeftShoulder); &NewController->u.buttons.ActionDown);
SDLProcessInputDigitalButton(ControllerHandles[ControllerIndex], &OldController->RightShoulder, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, &NewController->RightShoulder); SDLProcessInputDigitalButton(
ControllerHandles[ControllerIndex],
&OldController->u.buttons.ActionRight,
SDL_CONTROLLER_BUTTON_B,
&NewController->u.buttons.ActionRight);
SDLProcessInputDigitalButton(
ControllerHandles[ControllerIndex],
&OldController->u.buttons.ActionLeft,
SDL_CONTROLLER_BUTTON_X,
&NewController->u.buttons.ActionLeft);
SDLProcessInputDigitalButton(
ControllerHandles[ControllerIndex],
&OldController->u.buttons.ActionUp,
SDL_CONTROLLER_BUTTON_Y,
&NewController->u.buttons.ActionUp);
SDLProcessInputDigitalButton(
ControllerHandles[ControllerIndex],
&OldController->u.buttons.LeftShoulder,
SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
&NewController->u.buttons.LeftShoulder);
SDLProcessInputDigitalButton(
ControllerHandles[ControllerIndex],
&OldController->u.buttons.RightShoulder,
SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
&NewController->u.buttons.RightShoulder);
SDLProcessInputDigitalButton(ControllerHandles[ControllerIndex], &OldController->Start, SDL_CONTROLLER_BUTTON_START, &NewController->Start); SDLProcessInputDigitalButton(
SDLProcessInputDigitalButton(ControllerHandles[ControllerIndex], &OldController->Back, SDL_CONTROLLER_BUTTON_BACK, &NewController->Back); ControllerHandles[ControllerIndex],
&OldController->u.buttons.Start,
SDL_CONTROLLER_BUTTON_START,
&NewController->u.buttons.Start);
SDLProcessInputDigitalButton(
ControllerHandles[ControllerIndex],
&OldController->u.buttons.Back,
SDL_CONTROLLER_BUTTON_BACK,
&NewController->u.buttons.Back);
} else { } else {
/* NOTE: This controller is not plugged in. */ /* NOTE: This controller is not plugged in. */
NewController->IsConnected = FALSE; NewController->IsConnected = FALSE;
} }
} }
S32 TargetQueueBytes = SoundOutput.LatencySampleCount * SoundOutput.BytesPerSample; TargetQueueBytes = SoundOutput.LatencySampleCount *
S32 BytesToWrite = TargetQueueBytes - SDL_GetQueuedAudioSize(1); SoundOutput.BytesPerSample;
game_sound_output_buffer SoundBuffer; BytesToWrite =
SoundBuffer.SamplesPerSecond = SoundOutput.SamplesPerSecond; TargetQueueBytes - SDL_GetQueuedAudioSize(1);
SoundBuffer.SampleCount = BytesToWrite / SoundOutput.BytesPerSample; SoundBuffer.SamplesPerSecond =
SoundOutput.SamplesPerSecond;
SoundBuffer.SampleCount =
BytesToWrite / SoundOutput.BytesPerSample;
SoundBuffer.Samples = (short *)SoundOutput.Samples; SoundBuffer.Samples = (short *)SoundOutput.Samples;
game_offscreen_buffer Buffer;
Buffer.Memory = GlobalBackbuffer.Memory; Buffer.Memory = GlobalBackbuffer.Memory;
Buffer.Width = GlobalBackbuffer.Width; Buffer.Width = GlobalBackbuffer.Width;
Buffer.Height = GlobalBackbuffer.Height; Buffer.Height = GlobalBackbuffer.Height;
@@ -654,41 +942,50 @@ S32 main(S32 argc, char *argv[])
} }
if(Game.UpdateAndRender) if(Game.UpdateAndRender)
Game.UpdateAndRender(&GameMemory, NewInput, &Buffer, &SoundBuffer); Game.UpdateAndRender(&GameMemory, NewInput, &Buffer,
&SoundBuffer);
SDLFillSoundBuffer(&SoundOutput, BytesToWrite); SDLFillSoundBuffer(&SoundOutput, BytesToWrite);
U64 WorkCounter = SDLGetWallClock(); WorkCounter = SDLGetWallClock();
F32 WorkSecondsElapsed = SDLGetSecondsElapsed(LastCounter, WorkCounter); WorkSecondsElapsed = SDLGetSecondsElapsed(LastCounter,
WorkCounter);
F32 SecondsElapsedForFrame = WorkSecondsElapsed; SecondsElapsedForFrame = WorkSecondsElapsed;
if(SecondsElapsedForFrame < TargetSecondsPerFrame) { if(SecondsElapsedForFrame < TargetSecondsPerFrame) {
while(SecondsElapsedForFrame < TargetSecondsPerFrame) { while(SecondsElapsedForFrame < TargetSecondsPerFrame) {
SecondsElapsedForFrame = SDLGetSecondsElapsed(LastCounter, SDLGetWallClock()); SecondsElapsedForFrame =
SDL_Delay((unsigned int)((TargetSecondsPerFrame - SecondsElapsedForFrame) * 1000.0)); SDLGetSecondsElapsed(LastCounter,
SDLGetWallClock());
SDL_Delay((U32)((TargetSecondsPerFrame -
SecondsElapsedForFrame) * 1000.0));
} }
} else { } else {
/* TODO: Missed frame rate! */ /* TODO: Missed frame rate! */
/* TODO: Logging. */ /* TODO: Logging. */
} }
U64 EndCounter = SDLGetWallClock(); EndCounter = SDLGetWallClock();
SDLDisplayBufferInWindow(GlobalBackbuffer, Window, Renderer); SDLDisplayBufferInWindow(GlobalBackbuffer, Window,
Renderer);
F64 MSPerFrame = 1000.0 * SDLGetSecondsElapsed(LastCounter, EndCounter); MSPerFrame = 1000.0 * SDLGetSecondsElapsed(LastCounter,
F64 FPS = 0.0; EndCounter);
FPS = 0.0;
LastCounter = EndCounter; LastCounter = EndCounter;
#if __x86_64__ || __i386__ #if __x86_64__ || __i386__
U64 EndCycleCount = __rdtsc(); EndCycleCount = __rdtsc();
U64 CyclesElapsed = EndCycleCount - LastCycleCount; CyclesElapsed = EndCycleCount - LastCycleCount;
F64 MCPF = ((double)CyclesElapsed / (1000.0 * 1000.0)); MCPF = ((double)CyclesElapsed / (1000.0 * 1000.0));
fprintf(stderr, "%.02f ms/f, %.02f/s, %.02f mc/f\n", MSPerFrame, FPS, MCPF); fprintf(stderr, "%.02f ms/f, %.02f/s, %.02f mc/f\n",
MSPerFrame, FPS, MCPF);
LastCycleCount = EndCycleCount; LastCycleCount = EndCycleCount;
#else #else
fprintf(stderr, "%.02f ms/f, %.02f/s\n", MSPerFrame, FPS); fprintf(stderr, "%.02f ms/f, %.02f/s\n",
MSPerFrame, FPS);
#endif #endif
SWAP(game_input *, NewInput, OldInput); SWAP(game_input *, NewInput, OldInput);
@@ -704,7 +1001,9 @@ S32 main(S32 argc, char *argv[])
} }
/* NOTE: Let the OS clean things up. */ /* NOTE: Let the OS clean things up. */
/* SDLDeinitControllers(); // NOTE: This, however, might need to be closed? Is there maybe some clean up state that close communicates to the controller? */ /* NOTE: This, however, might need to be closed? Is there maybe some
* clean up state that close communicates to the controller? */
/* SDLDeinitControllers(); */
/* SDL_CloseAudio(); */ /* SDL_CloseAudio(); */
/* SDL_DestroyRenderer(Renderer); */ /* SDL_DestroyRenderer(Renderer); */
/* SDL_DestroyWindow(Window); */ /* SDL_DestroyWindow(Window); */
+2 -1
View File
@@ -12,7 +12,8 @@ typedef struct sdl_window_dimension {
} sdl_window_dimension; } sdl_window_dimension;
typedef struct offscreen_buffer { typedef struct offscreen_buffer {
/* Pixels are always 32-bit and have the bytes in BGRX (little-endian). */ /* Pixels are always 32-bit and have the bytes in BGRX
* (little-endian). */
SDL_Texture *Texture; SDL_Texture *Texture;
void *Memory; void *Memory;
S32 Width; S32 Width;