aboutsummaryrefslogtreecommitdiff
path: root/code/linux_handmade.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'code/linux_handmade.cpp')
-rw-r--r--code/linux_handmade.cpp1489
1 files changed, 0 insertions, 1489 deletions
diff --git a/code/linux_handmade.cpp b/code/linux_handmade.cpp
deleted file mode 100644
index 0ed3b1d..0000000
--- a/code/linux_handmade.cpp
+++ /dev/null
@@ -1,1489 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <x86intrin.h>
-#include <dlfcn.h>
-#include <fcntl.h>
-
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <linux/limits.h>
-#include <linux/input.h>
-#include <alsa/asoundlib.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/keysymdef.h>
-#include <X11/extensions/Xrandr.h>
-
-#include "handmade_platform.h"
-#include "linux_handmade.h"
-
-#include "x11_keysym_convert.c"
-
-#define true 1
-#define false 0
-
-#define ALSA_RECOVER_SILENT true
-#define MAX_PLAYER_COUNT 4
-
-#ifdef Assert
-#undef Assert
-#define Assert(Expression) \
-if(!(Expression)) \
-{ \
-raise(SIGTRAP); \
-}
-#endif
-
-// NOTE(luca): Bits are layed out over multiple bytes. This macro checks which byte the bit will be set in.
-#define IsEvdevBitSet(Bit, Array) (Array[(Bit) / 8] & (1 << ((Bit) % 8)))
-#define BytesNeededForBits(Bits) ((Bits + 7) / 8)
-
-global_variable b32 GlobalRunning;
-global_variable b32 GlobalPaused;
-
-void MemCpy(char *Dest, char *Source, size_t Count)
-{
- while(Count--) *Dest++ = *Source++;
-}
-
-void MemSet(char *Dest, char Value, size_t Count)
-{
- while(Count--) *Dest++ = Value;
-}
-
-int StrLen(char *String)
-{
- size_t Result = 0;
-
- while(*String++) Result++;
-
- return Result;
-}
-
-void CatStrings(size_t SourceACount, char *SourceA,
- size_t SourceBCount, char *SourceB,
- size_t DestCount, char *Dest)
-{
- for(size_t Index = 0;
- Index < SourceACount;
- Index++)
- {
- *Dest++ = *SourceA++;
- }
-
- for(size_t Index = 0;
- Index < SourceBCount;
- Index++)
- {
- *Dest++ = *SourceB++;
- }
-}
-
-internal rune
-ConvertUTF8StringToRune(u8 UTF8String[4])
-{
- rune Codepoint = 0;
-
- if((UTF8String[0] & 0x80) == 0x00)
- {
- Codepoint = UTF8String[0];
- }
- else if((UTF8String[0] & 0xE0) == 0xC0)
- {
- Codepoint = (
- ((UTF8String[0] & 0x1F) << 6*1) |
- ((UTF8String[1] & 0x3F) << 6*0)
- );
- }
- else if((UTF8String[0] & 0xF0) == 0xE0)
- {
- Codepoint = (
- ((UTF8String[0] & 0x0F) << 6*2) |
- ((UTF8String[1] & 0x3F) << 6*1) |
- ((UTF8String[2] & 0x3F) << 6*0)
- );
- }
- else if((UTF8String[0] & 0xF8) == 0xF8)
- {
- Codepoint = (
- ((UTF8String[0] & 0x0E) << 6*3) |
- ((UTF8String[1] & 0x3F) << 6*2) |
- ((UTF8String[2] & 0x3F) << 6*1) |
- ((UTF8String[3] & 0x3F) << 6*0)
- );
- }
- else
- {
- Assert(0);
- }
-
- return Codepoint;
-}
-
-struct linux_init_alsa_result
-{
- snd_pcm_t *PCMHandle;
- snd_pcm_hw_params_t *PCMParams;
-};
-
-internal linux_init_alsa_result LinuxInitALSA()
-{
- linux_init_alsa_result Result = {};
-
- int PCMResult = 0;
- if((PCMResult = snd_pcm_open(&Result.PCMHandle, "default",
- SND_PCM_STREAM_PLAYBACK, 0)) == 0)
- {
- snd_pcm_hw_params_alloca(&Result.PCMParams);
- snd_pcm_hw_params_any(Result.PCMHandle, Result.PCMParams);
- u32 ChannelCount = 2;
- u32 SampleRate = 48000;
-
- if((PCMResult = snd_pcm_hw_params_set_access(Result.PCMHandle, Result.PCMParams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
- {
- // TODO(luca): Logging
- // snd_strerror(pcm)
- }
- if((PCMResult = snd_pcm_hw_params_set_format(Result.PCMHandle, Result.PCMParams, SND_PCM_FORMAT_S16_LE)) < 0)
- {
- // TODO(luca): Logging
- }
- if((PCMResult = snd_pcm_hw_params_set_channels(Result.PCMHandle, Result.PCMParams, ChannelCount)) < 0)
- {
- // TODO(luca): Logging
- }
- if((PCMResult = snd_pcm_hw_params_set_rate_near(Result.PCMHandle, Result.PCMParams, &SampleRate, 0)) < 0)
- {
- // TODO(luca): Logging
- }
- if((PCMResult = snd_pcm_nonblock(Result.PCMHandle, 1)) < 0)
- {
- // TODO(luca): Logging
- }
- if((PCMResult = snd_pcm_reset(Result.PCMHandle)) < 0)
- {
- // TODO(luca): Logging
- }
- if((PCMResult = snd_pcm_hw_params(Result.PCMHandle, Result.PCMParams)) < 0)
- {
- // TODO(luca): Logging
- }
-
- }
- else
- {
- // TODO(luca): Logging
- }
-
- return Result;
-}
-
-internal void LinuxGetAxisInfo(linux_gamepad *GamePad, linux_gamepad_axes_enum Axis, int AbsAxis)
-{
- input_absinfo AxesInfo = {};
- if(ioctl(GamePad->File, EVIOCGABS(AbsAxis), &AxesInfo) != -1)
- {
- GamePad->Axes[Axis].Minimum = AxesInfo.minimum;
- GamePad->Axes[Axis].Maximum = AxesInfo.maximum;
- GamePad->Axes[Axis].Fuzz = AxesInfo.fuzz;
- GamePad->Axes[Axis].Flat = AxesInfo.flat;
- }
-}
-
-internal void LinuxOpenGamePad(char *FilePath, linux_gamepad *GamePad)
-{
- GamePad->File = open(FilePath, O_RDWR|O_NONBLOCK);
-
- if(GamePad->File != -1)
- {
- int Version = 0;
- int IsCompatible = true;
-
- // TODO(luca): Check versions
- ioctl(GamePad->File, EVIOCGVERSION, &Version);
- ioctl(GamePad->File, EVIOCGNAME(sizeof(GamePad->Name)), GamePad->Name);
-
- char SupportedEventBits[BytesNeededForBits(EV_MAX)] = {};
- if(ioctl(GamePad->File, EVIOCGBIT(0, sizeof(SupportedEventBits)), SupportedEventBits) != -1)
- {
- if(!IsEvdevBitSet(EV_ABS, SupportedEventBits))
- {
- // TODO(luca): Logging
- IsCompatible = false;
- }
- if(!IsEvdevBitSet(EV_KEY, SupportedEventBits))
- {
- // TODO(luca): Logging
- IsCompatible = false;
- }
- }
-
- char SupportedKeyBits[BytesNeededForBits(KEY_MAX)] = {};
- if(ioctl(GamePad->File, EVIOCGBIT(EV_KEY , sizeof(SupportedKeyBits)), SupportedKeyBits) != -1)
- {
- if(!IsEvdevBitSet(BTN_GAMEPAD, SupportedKeyBits))
- {
- // TODO(luca): Logging
- IsCompatible = false;
- }
- }
-
- GamePad->SupportsRumble = IsEvdevBitSet(EV_FF, SupportedEventBits);
-
- if(IsCompatible)
- {
- // NOTE(luca): Map evdev axes to my enum.
- LinuxGetAxisInfo(GamePad, LSTICKX, ABS_X);
- LinuxGetAxisInfo(GamePad, LSTICKY, ABS_Y);
- LinuxGetAxisInfo(GamePad, RSTICKX, ABS_RX);
- LinuxGetAxisInfo(GamePad, RSTICKY, ABS_RY);
- LinuxGetAxisInfo(GamePad, LSHOULDER, ABS_Z);
- LinuxGetAxisInfo(GamePad, RSHOULDER, ABS_RZ);
- LinuxGetAxisInfo(GamePad, DPADX, ABS_HAT0X);
- LinuxGetAxisInfo(GamePad, DPADY, ABS_HAT0Y);
-
- MemCpy(GamePad->FilePath, FilePath, StrLen(FilePath));
- }
- else
- {
- close(GamePad->File);
- *GamePad = {};
- GamePad->File = -1;
- }
- }
-}
-
-// TODO(luca): Make this work in the case of multiple displays.
-internal r32 LinuxGetMonitorRefreshRate(Display *DisplayHandle, Window RootWindow)
-{
- r32 Result = 0;
-
- void *LibraryHandle = dlopen("libXrandr.so.2", RTLD_NOW);
-
- if(LibraryHandle)
- {
- typedef XRRScreenResources *xrr_get_screen_resources(Display *Display, Window Window);
- typedef XRRCrtcInfo *xrr_get_crtc_info(Display* Display, XRRScreenResources *Resources, RRCrtc Crtc);
-
- xrr_get_screen_resources *XRRGetScreenResources = (xrr_get_screen_resources *)dlsym(LibraryHandle, "XRRGetScreenResources");
- xrr_get_crtc_info *XRRGetCrtcInfo = (xrr_get_crtc_info *)dlsym(LibraryHandle, "XRRGetCrtcInfo");
-
- XRRScreenResources *ScreenResources = XRRGetScreenResources(DisplayHandle, RootWindow);
-
- RRMode ActiveModeID = 0;
- for(int CRTCIndex = 0;
- CRTCIndex< ScreenResources->ncrtc;
- CRTCIndex++)
- {
- XRRCrtcInfo *CRTCInfo = XRRGetCrtcInfo(DisplayHandle, ScreenResources, ScreenResources->crtcs[CRTCIndex]);
- if(CRTCInfo->mode)
- {
- ActiveModeID = CRTCInfo->mode;
- }
- }
-
- r32 ActiveRate = 0;
- for(int ModeIndex = 0;
- ModeIndex < ScreenResources->nmode;
- ModeIndex++)
- {
- XRRModeInfo ModeInfo = ScreenResources->modes[ModeIndex];
- if(ModeInfo.id == ActiveModeID)
- {
- Assert(ActiveRate == 0);
- Result = (r32)ModeInfo.dotClock / ((r32)ModeInfo.hTotal * (r32)ModeInfo.vTotal);
- }
- }
-
- dlclose(LibraryHandle);
- }
-
- return Result;
-}
-
-internal void LinuxBuildFileNameFromExecutable(char *Dest, linux_state *State, char *FileName)
-{
- char *Path = State->ExecutablePath;
-
- size_t LastSlash = 0;
- for(char *Scan = Path;
- *Scan;
- Scan++)
- {
- if(*Scan == '/')
- {
- LastSlash = Scan - Path;
- }
- }
-
- for(size_t Index = 0;
- Index < LastSlash + 1;
- Index++)
- {
- *Dest++ = *Path++;
- }
-
- while(*FileName)
- *Dest++ = *FileName++;
-}
-
-GAME_UPDATE_AND_RENDER(LinuxGameUpdateAndRenderStub) {}
-GAME_GET_SOUND_SAMPLES(LinuxGameGetSoundSamplesStub) {}
-
-internal linux_game_code LinuxLoadGameCode(char *LibraryPath)
-{
- linux_game_code Result = {};
-
- Result.LibraryHandle = dlopen(LibraryPath, RTLD_NOW);
- if(Result.LibraryHandle)
- {
- Result.UpdateAndRender = (game_update_and_render *)dlsym(Result.LibraryHandle, "GameUpdateAndRender");
- Result.GetSoundSamples = (game_get_sound_samples *)dlsym(Result.LibraryHandle, "GameGetSoundSamples");
- }
- else
- {
- Result.UpdateAndRender = (game_update_and_render *)LinuxGameUpdateAndRenderStub;
- Result.GetSoundSamples = (game_get_sound_samples *)LinuxGameGetSoundSamplesStub;
- }
-
- return Result;
-}
-
-internal void LinuxUnloadGameCode(linux_game_code *Game)
-{
- if(Game->LibraryHandle)
- {
- dlclose(Game->LibraryHandle);
- }
-}
-
-internal void LinuxProcessKeyPress(game_button_state *ButtonState, b32 IsDown)
-{
- if(ButtonState->EndedDown != IsDown)
- {
- ButtonState->EndedDown = IsDown;
- ButtonState->HalfTransitionCount++;
- }
-}
-
-internal void LinuxBeginRecordingInput(linux_state *State, int SlotIndex)
-{
- char FileName[64];
- sprintf(FileName, "loop_edit_%d.hmi", SlotIndex);
-
- int File = open(FileName, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600);
- if(State->InputRecordingFile != -1)
- {
- int BytesWritten = write(File, State->GameMemoryBlock, State->TotalSize);
- if(BytesWritten != -1 &&
- BytesWritten == (int)State->TotalSize)
- {
- State->InputRecordingFile = File;
- State->InputRecordingIndex = SlotIndex;
- }
- else
- {
- // TODO(luca): Logging
- }
- }
- else
- {
- // TODO(luca): Logging
- }
-
-}
-
-internal void LinuxEndRecordingInput(linux_state *State, int SlotIndex)
-{
- Assert(State->InputRecordingIndex);
- Assert(State->InputRecordingFile != -1);
-
- State->InputRecordingIndex = 0;
-
- if(close(State->InputRecordingFile) == -1)
- {
- State->InputRecordingFile = -1;
- }
- else
- {
- // TODO(luca): Logging
- }
-
-}
-
-internal void LinuxBeginInputPlayBack(linux_state *State, int SlotIndex)
-{
- char FileName[64];
- sprintf(FileName, "loop_edit_%d.hmi", SlotIndex);
-
- int File = open(FileName, O_RDONLY, 0400);
- if(File != -1)
- {
- State->InputPlayingFile = File;
-
- int BytesRead = read(State->InputPlayingFile, State->GameMemoryBlock, State->TotalSize);
- if(BytesRead != -1)
- {
- State->InputPlayingIndex = SlotIndex;
- }
- else
- {
- // TODO(luca): Logging
- }
- }
- else
- {
- // TODO(luca): Logging
- }
-
-}
-
-internal void LinuxEndInputPlayBack(linux_state *State)
-{
- Assert(State->InputPlayingIndex);
- Assert(State->InputPlayingFile != -1);
-
- if(close(State->InputPlayingFile) != -1)
- {
- State->InputPlayingFile = -1;
- }
- else
- {
- // TODO(luca): Logging
- }
-
- State->InputPlayingIndex = 0;
-}
-
-internal void LinuxRecordInput(linux_state *State, game_input *Input)
-{
- int BytesWritten = write(State->InputRecordingFile, Input, sizeof(*Input));
- if(BytesWritten != -1 &&
- BytesWritten == (int)sizeof(*Input))
- {
- }
- else
- {
- // TODO: Logging
- }
-
-}
-
-internal void LinuxPlayBackInput(linux_state *State, game_input *Input)
-{
- int BytesRead = read(State->InputPlayingFile, Input, sizeof(*Input));
- if(BytesRead != -1)
- {
- if(BytesRead == 0)
- {
- int PlayingIndex = State->InputPlayingIndex;
- LinuxEndInputPlayBack(State);
- LinuxBeginInputPlayBack(State, PlayingIndex);
- read(State->InputPlayingFile, Input, sizeof(*Input));
- }
- }
-
-}
-
-internal void LinuxHideCursor(Display *DisplayHandle, Window WindowHandle)
-{
- XColor black = {};
- char NoData[8] = {};
-
- Pixmap BitmapNoData = XCreateBitmapFromData(DisplayHandle, WindowHandle, NoData, 8, 8);
- Cursor InvisibleCursor = XCreatePixmapCursor(DisplayHandle, BitmapNoData, BitmapNoData,
- &black, &black, 0, 0);
- XDefineCursor(DisplayHandle, WindowHandle, InvisibleCursor);
- XFreeCursor(DisplayHandle, InvisibleCursor);
- XFreePixmap(DisplayHandle, BitmapNoData);
-}
-
-internal void LinuxShowCursor(Display *DisplayHandle, Window WindowHandle)
-{
- XUndefineCursor(DisplayHandle, WindowHandle);
-}
-
-internal void LinuxProcessPendingMessages(Display *DisplayHandle, Window WindowHandle,
- XIC InputContext, Atom WM_DELETE_WINDOW, linux_state *State, game_controller_input *KeyboardController)
-{
- XEvent WindowEvent = {};
- while(XPending(DisplayHandle) > 0)
- {
- XNextEvent(DisplayHandle, &WindowEvent);
- b32 FilteredEvent = XFilterEvent(&WindowEvent, WindowHandle);
- if(FilteredEvent)
- {
- Assert(WindowEvent.type == KeyPress || WindowEvent.type == KeyRelease);
- }
-
- switch(WindowEvent.type)
- {
- case KeyPress:
- case KeyRelease:
- {
- //- How text input works
- // The needs:
- // 1. Preserve game buttons, so that we can switch between a "game mode" or
- // "text input mode".
- // 2. Text input using the input method of the user which should allow for utf8 characters.
- // 3. Hotkey support. Eg. quickly navigating text.
- // 3 will be supported by 2 for code reuse.
- //
- // We are going to send a buffer text button presses to the game layer, this solves these
- // issues:
- // - Pressing the same key multiple times in one frame.
- // - Having modifiers be specific to each key press.
- // - Not having to create a button record for each possible character in the structure.
- // - Key events come in one at a time in the event loop, thus we need to have a buffer for
- // multiple keys pressed on a single frame.
- //
- // We store a count along the buffer and in the buffer we store the utf8 codepoint and its
- // modifiers.
- // The app code is responsible for traversing this buffer and applying the logic.
-
- // The problem of input methods and hotkeys:
- // Basically the problem is that if we allow the input method and combo's that could be
- // filtered by the input method it won't seem consistent to the user.
- // So we don't allow key bound to the input method to have an effect and we only pass key
- // inputs that have not been filtered.
- //
- // In the platform layer we handle the special case were the input methods creates non-
- // printable characters and we decompose those key inputs since non-printable characters
- // have no use anymore.
-
- // Extra:
- // - I refuse to check which keys bind to what modifiers. It's not important.
-
- // - Handy resources:
- // - https://www.coderstool.com/unicode-text-converter
- // - man Compose(5).
- // - https://en.wikipedia.org/wiki/Control_key#History
-
- KeySym Symbol = XLookupKeysym(&WindowEvent.xkey, 0);
- b32 IsDown = (WindowEvent.type == KeyPress);
-
- // TODO(luca): Refresh mappings.
- // NOTE(luca): Only KeyPress events see man page of Xutf8LookupString(). And skip filtered events for text input, but keep them for controller.
- if(IsDown && !FilteredEvent)
- {
- rune Codepoint = 'e';
- u8 LookupBuffer[4] = {};
- Status LookupStatus = {};
-
- s32 BytesLookepdUp = Xutf8LookupString(InputContext, &WindowEvent.xkey,
- (char *)&LookupBuffer, ArrayCount(LookupBuffer),
- 0, &LookupStatus);
- Assert(LookupStatus != XBufferOverflow);
- Assert(BytesLookepdUp <= 4);
-
- if(LookupStatus!= XLookupNone &&
- LookupStatus!= XLookupKeySym)
- {
- if(BytesLookepdUp)
- {
- Assert(KeyboardController->Keyboard.TextInputCount < ArrayCount(KeyboardController->Keyboard.TextInputBuffer));
-
- Codepoint = ConvertUTF8StringToRune(LookupBuffer);
-
- // NOTE(luca): Input methods might produce non printable characters (< ' '). If this
- // happens we try to "decompose" the key input.
- if(Codepoint < ' ' && Codepoint >= 0)
- {
- if(Symbol >= XK_space)
- {
- Codepoint = (char)(' ' + (Symbol - XK_space));
- }
- }
-
-
- if(Codepoint >= ' ' || Codepoint < 0)
- {
- game_text_button *TextButton = &KeyboardController->Keyboard.TextInputBuffer[KeyboardController->Keyboard.TextInputCount++];
- TextButton->Codepoint = Codepoint;
- TextButton->Shift = (WindowEvent.xkey.state & ShiftMask);
- TextButton->Control = (WindowEvent.xkey.state & ControlMask);
- TextButton->Alt = (WindowEvent.xkey.state & Mod1Mask);
-#if 0
- printf("%d bytes '%c' %d (%c|%c|%c)\n",
- BytesLookepdUp,
- ((Codepoint >= ' ') ? (char)Codepoint : '\0'),
- Codepoint,
- ((WindowEvent.xkey.state & ShiftMask) ? 'S' : ' '),
- ((WindowEvent.xkey.state & ControlMask) ? 'C' : ' '),
- ((WindowEvent.xkey.state & Mod1Mask) ? 'A' : ' '));
-#endif
- }
- else
- {
- // TODO(luca): Logging
- }
-
- }
- }
- }
-
- if(0) {}
- else if(Symbol == XK_w)
- {
- LinuxProcessKeyPress(&KeyboardController->MoveUp, IsDown);
- }
- else if(Symbol == XK_a)
- {
- LinuxProcessKeyPress(&KeyboardController->MoveLeft, IsDown);
- }
- else if(Symbol == XK_r)
- {
- LinuxProcessKeyPress(&KeyboardController->MoveDown, IsDown);
- }
- else if(Symbol == XK_s)
- {
- LinuxProcessKeyPress(&KeyboardController->MoveRight, IsDown);
- }
- else if(Symbol == XK_Up)
- {
- LinuxProcessKeyPress(&KeyboardController->ActionUp, IsDown);
- }
- else if(Symbol == XK_Left)
- {
- LinuxProcessKeyPress(&KeyboardController->ActionLeft, IsDown);
- }
- else if(Symbol == XK_Down)
- {
- LinuxProcessKeyPress(&KeyboardController->ActionDown, IsDown);
- }
- else if(Symbol == XK_Right)
- {
- LinuxProcessKeyPress(&KeyboardController->ActionRight, IsDown);
- }
- else if(Symbol == XK_space)
- {
- LinuxProcessKeyPress(&KeyboardController->Start, IsDown);
- }
- else if((WindowEvent.xkey.state & Mod1Mask) &&
- (Symbol == XK_p))
- {
- if(IsDown)
- {
- GlobalPaused = !GlobalPaused;
- }
- }
- else if((WindowEvent.xkey.state & Mod1Mask) &&
- (Symbol == XK_l))
- {
- if(IsDown)
- {
- if(State->InputPlayingIndex == 0)
- {
- if(State->InputRecordingIndex == 0)
- {
- LinuxBeginRecordingInput(State, 1);
- }
- else
- {
- LinuxEndRecordingInput(State, 1);
- LinuxBeginInputPlayBack(State, 1);
- }
- }
- else
- {
- // NOTE(luca) Reset buttons so they aren't held.
- // TODO(luca): Check if still needed since we clear halftransition counts.
- for(u32 ButtonIndex = 0;
- ButtonIndex < ArrayCount(KeyboardController->Buttons);
- ButtonIndex++)
- {
- KeyboardController->Buttons[ButtonIndex] = {};
- }
- LinuxEndInputPlayBack(State);
- }
- }
- }
- else if((WindowEvent.xkey.state & Mod1Mask) &&
- (Symbol == XK_F4))
- {
- GlobalRunning = false;
- }
-
- } break;
- case DestroyNotify:
- {
- XDestroyWindowEvent *Event = (XDestroyWindowEvent *)&WindowEvent;
- if(Event->window == WindowHandle)
- {
- GlobalRunning = false;
- }
- } break;
-
- case ClientMessage:
- {
- XClientMessageEvent *Event = (XClientMessageEvent *)&WindowEvent;
- if((Atom)Event->data.l[0] == WM_DELETE_WINDOW)
- {
- XDestroyWindow(DisplayHandle, WindowHandle);
- GlobalRunning = false;
- }
- } break;
-
- case EnterNotify:
- {
- //LinuxHideCursor(DisplayHandle, WindowHandle);
- } break;
-
- case LeaveNotify:
- {
- //LinuxShowCursor(DisplayHandle, WindowHandle);
- } break;
- }
- }
-}
-
-internal void LinuxSetSizeHint(Display *DisplayHandle, Window WindowHandle,
- int MinWidth, int MinHeight,
- int MaxWidth, int MaxHeight)
-{
- XSizeHints Hints = {};
- if(MinWidth > 0 && MinHeight > 0) Hints.flags |= PMinSize;
- if(MaxWidth > 0 && MaxHeight > 0) Hints.flags |= PMaxSize;
-
- Hints.min_width = MinWidth;
- Hints.min_height = MinHeight;
- Hints.max_width = MaxWidth;
- Hints.max_height = MaxHeight;
-
- XSetWMNormalHints(DisplayHandle, WindowHandle, &Hints);
-}
-
-DEBUG_PLATFORM_READ_ENTIRE_FILE(DEBUGPlatformReadEntireFile)
-{
- debug_read_file_result Result = {};
-
- int File = open(FileName, O_RDONLY);
- if(File != -1)
- {
- struct stat FileStats = {};
- fstat(File, &FileStats);
- Result.ContentsSize = FileStats.st_size;
- Result.Contents = mmap(0, FileStats.st_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, File, 0);
-
- close(File);
- }
-
- return Result;
-}
-
-PLATFORM_RUN_COMMAND_AND_GET_OUTPUT(PlatformRunCommandAndGetOutput)
-{
- int Result = 0;
-
- int HandlesLink[2] = {0};
- int WaitStatus = 0;
- pid_t ChildPID = 0;
- int Ret = 0;
-
- char *FilePath = Command[0];
- int AccessMode = F_OK | X_OK;
- Ret = access(FilePath, AccessMode);
-
- if(Ret == 0)
- {
- Ret = pipe(HandlesLink);
- if(Ret != -1)
- {
- ChildPID = fork();
- if(ChildPID != -1)
- {
- if(ChildPID == 0)
- {
- dup2(HandlesLink[1], STDOUT_FILENO);
- execve(Command[0], Command, 0);
- }
- else
- {
- wait(&WaitStatus);
-
- Result = read(HandlesLink[0], OutputBuffer, 4096);
- if(Result == -1)
- {
- Result = 0;
- }
- }
-
- }
- else
- {
- // TODO: Logging
- }
- }
- else
- {
- // TODO: Logging
- }
- }
- else
- {
- // TODO: Logging
- }
-
- return Result;
-}
-
-DEBUG_PLATFORM_FREE_FILE_MEMORY(DEBUGPlatformFreeFileMemory)
-{
- munmap(Memory, MemorySize);
-}
-
-DEBUG_PLATFORM_WRITE_ENTIRE_FILE(DEBUGPlatformWriteEntireFile)
-{
- b32 Result = false;
-
- int FD = open(FileName, O_CREAT|O_WRONLY|O_TRUNC, 00600);
- if(FD != -1)
- {
- if(write(FD, Memory, MemorySize) != MemorySize)
- {
- Result = true;
- }
-
- close(FD);
- }
-
- return Result;
-}
-
-internal struct timespec LinuxGetLastWriteTime(char *FilePath)
-{
- struct timespec Result = {};
-
- struct stat LibraryFileStats = {};
- if(!stat(FilePath, &LibraryFileStats))
- {
- Result = LibraryFileStats.st_mtim;
- }
-
- return Result;
-}
-
-internal struct timespec LinuxGetWallClock()
-{
- struct timespec Counter = {};
- clock_gettime(CLOCK_MONOTONIC, &Counter);
- return Counter;
-}
-
-internal s64 LinuxGetNSecondsElapsed(struct timespec Start, struct timespec End)
-{
- s64 Result = 0;
- Result = ((s64)End.tv_sec*1000000000 + (s64)End.tv_nsec) - ((s64)Start.tv_sec*1000000000 + (s64)Start.tv_nsec);
- return Result;
-}
-
-internal r32 LinuxGetSecondsElapsed(struct timespec Start, struct timespec End)
-{
- r32 Result = 0;
- Result = LinuxGetNSecondsElapsed(Start, End)/1000.0f/1000.0f/1000.0f;
-
- return Result;
-}
-
-internal r32 LinuxNormalizeAxisValue(s32 Value, linux_gamepad_axis Axis)
-{
- r32 Result = 0;
- if(Value)
- {
- // ((value - min / max) - 0.5) * 2
- r32 Normalized = ((r32)((r32)(Value - Axis.Minimum) / (r32)(Axis.Maximum - Axis.Minimum)) - 0.5f)*2;
- Result = Normalized;
- }
- Assert(Result <= 1.0f && Result >= -1.0f);
-
- return Result;
-}
-
-void LinuxDebugVerticalLine(game_offscreen_buffer *Buffer, int X, int Y, u32 Color)
-{
- int Height = 32;
-
- if(X <= Buffer->Width && X >= 0 &&
- Y <= Buffer->Height - Height && Y <= 0)
- {
- u8 *Row = (u8 *)Buffer->Memory + Y*Buffer->Pitch + X*Buffer->BytesPerPixel;
- while(Height--)
- {
- *(u32 *)Row = Color;
- Row += Buffer->Pitch;
- }
- }
-}
-
-int main(int ArgC, char *Args[])
-{
- // NOTE(luca): Change to executable's current directory such that file paths are relative to that in the game code.
- char *ExePath = Args[0];
- int Length = StrLen(ExePath);
- char ExecutableDirPath[Length];
- int LastSlash = 0;
- for(int At = 0;
- At < Length;
- At++)
- {
- if(ExePath[At] == '/')
- {
- LastSlash = At;
- }
- }
- MemCpy(ExecutableDirPath, ExePath, LastSlash);
- ExecutableDirPath[LastSlash] = 0;
- if(chdir(ExecutableDirPath) == -1)
- {
- // TODO(luca): Logging
- }
-
- Display *DisplayHandle = XOpenDisplay(0);
-
- if(DisplayHandle)
- {
- Window RootWindow = XDefaultRootWindow(DisplayHandle);
- int Screen = XDefaultScreen(DisplayHandle);
- int Width = 1920/2;
- int Height = 1080/2;
- int ScreenBitDepth = 24;
- XVisualInfo WindowVisualInfo = {};
- if(XMatchVisualInfo(DisplayHandle, Screen, ScreenBitDepth, TrueColor, &WindowVisualInfo))
- {
- XSetWindowAttributes WindowAttributes = {};
- WindowAttributes.bit_gravity = StaticGravity;
-#if HANDMADE_INTERNAL
- WindowAttributes.background_pixel = 0xFF00FF;
-#endif
- WindowAttributes.colormap = XCreateColormap(DisplayHandle, RootWindow, WindowVisualInfo.visual, AllocNone);
- WindowAttributes.event_mask = (StructureNotifyMask |
- KeyPressMask | KeyReleaseMask |
- EnterWindowMask | LeaveWindowMask);
- u64 WindowAttributeMask = CWBitGravity | CWBackPixel | CWColormap | CWEventMask;
-
- Window WindowHandle = XCreateWindow(DisplayHandle, RootWindow,
- 1920 - Width - 10, 10,
- Width, Height,
- 0,
- WindowVisualInfo.depth, InputOutput,
- WindowVisualInfo.visual, WindowAttributeMask, &WindowAttributes);
- if(WindowHandle)
- {
- XStoreName(DisplayHandle, WindowHandle, "Handmade Window");
- LinuxSetSizeHint(DisplayHandle, WindowHandle, Width, Height, Width, Height);
-
- Atom WM_DELETE_WINDOW = XInternAtom(DisplayHandle, "WM_DELETE_WINDOW", False);
- if(!XSetWMProtocols(DisplayHandle, WindowHandle, &WM_DELETE_WINDOW, 1))
- {
- // TODO(luca): Logging
- }
-
- XClassHint ClassHint = {};
- ClassHint.res_name = "Handmade Window";
- ClassHint.res_class = "Handmade Window";
- XSetClassHint(DisplayHandle, WindowHandle, &ClassHint);
-
- XSetLocaleModifiers("");
-
- XIM InputMethod = XOpenIM(DisplayHandle, 0, 0, 0);
- if(!InputMethod){
- XSetLocaleModifiers("@im=none");
- InputMethod = XOpenIM(DisplayHandle, 0, 0, 0);
- }
- XIC InputContext = XCreateIC(InputMethod,
- XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
- XNClientWindow, WindowHandle,
- XNFocusWindow, WindowHandle,
- NULL);
- XSetICFocus(InputContext);
-
- int BitsPerPixel = 32;
- int BytesPerPixel = BitsPerPixel/8;
- int WindowBufferSize = Width*Height*BytesPerPixel;
- char *WindowBuffer = (char *)malloc(WindowBufferSize);
-
- XImage *WindowImage = XCreateImage(DisplayHandle, WindowVisualInfo.visual, WindowVisualInfo.depth, ZPixmap, 0, WindowBuffer, Width, Height, BitsPerPixel, 0);
- GC DefaultGC = DefaultGC(DisplayHandle, Screen);
-
- linux_state LinuxState = {};
- MemCpy(LinuxState.ExecutablePath, Args[0], strlen(Args[0]));
-
- char LibraryFullPath[] = "handmade.so";
- linux_game_code Game = LinuxLoadGameCode(LibraryFullPath);
- Game.LibraryLastWriteTime = LinuxGetLastWriteTime(LibraryFullPath);
-
- game_memory GameMemory = {};
-
- GameMemory.PermanentStorageSize = Megabytes(64);
- GameMemory.TransientStorageSize = Gigabytes(1);
- GameMemory.DEBUGPlatformReadEntireFile = DEBUGPlatformReadEntireFile;
- GameMemory.DEBUGPlatformFreeFileMemory = DEBUGPlatformFreeFileMemory;
- GameMemory.DEBUGPlatformWriteEntireFile = DEBUGPlatformWriteEntireFile;
- GameMemory.PlatformRunCommandAndGetOutput = PlatformRunCommandAndGetOutput;
-
-#if HANDMADE_INTERNAL
- void *BaseAddress = (void *)Terabytes(2);
-#else
- void *BaseAddress = 0;
-#endif
- // TODO(casey): TransientStorage needs to be broken into game transient and cache transient
- LinuxState.TotalSize = GameMemory.PermanentStorageSize + GameMemory.TransientStorageSize;
- LinuxState.GameMemoryBlock = mmap(BaseAddress, LinuxState.TotalSize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0);
- GameMemory.PermanentStorage = LinuxState.GameMemoryBlock;
- GameMemory.TransientStorage = (u8 *)GameMemory.PermanentStorage + GameMemory.PermanentStorageSize;
-
- game_input Input[2] = {};
- game_input *NewInput = &Input[0];
- game_input *OldInput = &Input[1];
-
- linux_gamepad GamePads[MAX_PLAYER_COUNT] = {};
- for(int GamePadIndex = 0;
- GamePadIndex < MAX_PLAYER_COUNT;
- GamePadIndex++)
- {
- GamePads[GamePadIndex].File = -1;
- }
-
- char EventDirectoryName[] = "/dev/input/";
- DIR *EventDirectory = opendir(EventDirectoryName);
- struct dirent *Entry = 0;
- int GamePadIndex = 0;
- while((Entry = readdir(EventDirectory)))
- {
- if(!strncmp(Entry->d_name, "event", sizeof("event") - 1))
- {
- char FilePath[PATH_MAX] = {};
- CatStrings(sizeof(EventDirectoryName) - 1, EventDirectoryName,
- StrLen(Entry->d_name), Entry->d_name,
- sizeof(FilePath) - 1, FilePath);
- if(GamePadIndex < MAX_PLAYER_COUNT)
- {
- linux_gamepad *GamePadAt = &GamePads[GamePadIndex];
- LinuxOpenGamePad(FilePath, GamePadAt);
- if(GamePadAt->File != -1)
- {
-#if 0
- for(int AxesIndex = 0;
- AxesIndex < AXES_COUNT;
- AxesIndex++)
- {
- linux_gamepad_axis Axis = GamePadAt->Axes[AxesIndex];
- printf("min: %d, max: %d, fuzz: %d, flat: %d\n", Axis.Minimum, Axis.Maximum, Axis.Fuzz, Axis.Flat);
- }
-#endif
- GamePadIndex++;
- }
- }
- else
- {
- // TODO(luca): Logging
- }
- }
- }
-
- game_offscreen_buffer OffscreenBuffer = {};
- OffscreenBuffer.Memory = WindowBuffer;
- OffscreenBuffer.Width = Width;
- OffscreenBuffer.Height = Height;
- OffscreenBuffer.BytesPerPixel = BytesPerPixel;
- OffscreenBuffer.Pitch = Width*BytesPerPixel;
-
- int LastFramesWritten = 0;
- unsigned int SampleRate, ChannelCount, PeriodTime, SampleCount;
- snd_pcm_status_t *PCMStatus = 0;
- snd_pcm_t* PCMHandle = 0;
- snd_pcm_hw_params_t *PCMParams = 0;
- snd_pcm_uframes_t PeriodSize = 0;
- snd_pcm_uframes_t PCMBufferSize = 0;
-
- linux_init_alsa_result ALSAInit = LinuxInitALSA();
- PCMHandle = ALSAInit.PCMHandle;
- PCMParams = ALSAInit.PCMParams;
- snd_pcm_hw_params_get_channels(PCMParams, &ChannelCount);
- snd_pcm_hw_params_get_rate(PCMParams, &SampleRate, 0);
- snd_pcm_hw_params_get_period_size(PCMParams, &PeriodSize, 0);
- snd_pcm_hw_params_get_period_time(PCMParams, &PeriodTime, NULL);
- snd_pcm_hw_params_get_buffer_size(PCMParams, &PCMBufferSize);
- snd_pcm_status_malloc(&PCMStatus);
-
-#if 0
- {
- Assert(0);
- u32 Value, Result;
- snd_pcm_uframes_t Frames;
- Result = snd_pcm_hw_params_get_buffer_time_min(PCMParams, &Value, 0);
- Result = snd_pcm_hw_params_get_buffer_size_min(PCMParams, &Frames);
- Frames = PCMBufferSize/2;
- Result = snd_pcm_hw_params_set_period_size_near(PCMHandle, PCMParams, &Frames, 0);
- Result = snd_pcm_hw_params_get_period_size(PCMParams, &PeriodSize, 0);
- }
-#endif
-
-
- char AudioSamples[PCMBufferSize];
- u64 Periods = 2;
- u32 BytesPerSample = (sizeof(s16)*ChannelCount);
-
-#if 1
- r32 GameUpdateHz = 60;
-#else
- r32 GameUpdateHz = LinuxGetMonitorRefreshRate(DisplayHandle, RootWindow);
-#endif
-
- thread_context ThreadContext = {};
-
- XMapWindow(DisplayHandle, WindowHandle);
- XFlush(DisplayHandle);
-
- struct timespec LastCounter = LinuxGetWallClock();
- struct timespec FlipWallClock = LinuxGetWallClock();
- r32 TargetSecondsPerFrame = 1.0f / GameUpdateHz;
-
- GlobalRunning = true;
-
- u64 LastCycleCount = __rdtsc();
- while(GlobalRunning)
- {
- NewInput->dtForFrame = TargetSecondsPerFrame;
-
-#if HANDMADE_SLOW
- // NOTE(luca): Because gcc will first create an empty file and then write into it we skip trying to reload when the file is empty.
- struct stat FileStats = {};
- stat(LibraryFullPath, &FileStats);
- if(FileStats.st_size)
- {
- s64 SecondsElapsed = LinuxGetNSecondsElapsed(Game.LibraryLastWriteTime, FileStats.st_mtim) / 1000/1000;
- if(SecondsElapsed > 0)
- {
- LinuxUnloadGameCode(&Game);
- Game = LinuxLoadGameCode(LibraryFullPath);
- Game.LibraryLastWriteTime = FileStats.st_mtim;
- }
- }
-#endif
-
- game_controller_input *OldKeyboardController = GetController(OldInput, 0);
- game_controller_input *NewKeyboardController = GetController(NewInput, 0);
- NewKeyboardController->IsConnected = true;
-
- NewKeyboardController->Keyboard.TextInputCount = 0;
- for(u32 ButtonIndex = 0;
- ButtonIndex < ArrayCount(NewKeyboardController->Buttons);
- ButtonIndex++)
- {
- NewKeyboardController->Buttons[ButtonIndex].HalfTransitionCount = 0;
- }
-
- LinuxProcessPendingMessages(DisplayHandle, WindowHandle, InputContext, WM_DELETE_WINDOW,
- &LinuxState, NewKeyboardController);
-
- // TODO(luca): Use buttonpress/release events instead so we query this less frequently.
- s32 MouseX = 0, MouseY = 0, MouseZ = 0; // TODO(luca): Support mousewheel?
- u32 MouseMask = 0;
- u64 Ignored;
- if(XQueryPointer(DisplayHandle, WindowHandle,
- &Ignored, &Ignored, (int *)&Ignored, (int *)&Ignored,
- &MouseX, &MouseY, &MouseMask))
- {
- if(MouseX <= OffscreenBuffer.Width &&
- MouseX >= 0 &&
- MouseY <= OffscreenBuffer.Height &&
- MouseY >= 0)
- {
- NewInput->MouseY = MouseY;
- NewInput->MouseX = MouseX;
-
- for(u32 ButtonIndex = 0;
- ButtonIndex < PlatformMouseButton_Count;
- ButtonIndex++)
- {
- NewInput->MouseButtons[ButtonIndex].EndedDown = OldInput->MouseButtons[ButtonIndex].EndedDown;
- NewInput->MouseButtons[ButtonIndex].HalfTransitionCount = 0;
- }
-
- LinuxProcessKeyPress(&NewInput->MouseButtons[PlatformMouseButton_Left], (MouseMask & Button1Mask));
- LinuxProcessKeyPress(&NewInput->MouseButtons[PlatformMouseButton_Middle], (MouseMask & Button2Mask));
- LinuxProcessKeyPress(&NewInput->MouseButtons[PlatformMouseButton_Right], (MouseMask & Button3Mask));
- }
- }
-
- for(int GamePadIndex = 0;
- GamePadIndex < MAX_PLAYER_COUNT;
- GamePadIndex++)
- {
- linux_gamepad *GamePadAt = &GamePads[GamePadIndex];
-
- if(GamePadAt->File != -1)
- {
- game_controller_input *OldController = GetController(OldInput, 0);
- game_controller_input *NewController = GetController(NewInput, 0);
-
- // TODO(luca): Cross frame values!!!
- struct input_event InputEvents[64] = {};
- int BytesRead = 0;
-
- BytesRead = read(GamePadAt->File, InputEvents, sizeof(InputEvents));
- if(BytesRead != -1)
- {
- for(u32 EventIndex = 0;
- EventIndex < ArrayCount(InputEvents);
- EventIndex++)
- {
- struct input_event EventAt = InputEvents[EventIndex];
-
- switch(EventAt.type)
- {
- case EV_KEY:
- {
- b32 IsDown = EventAt.value;
- if(0) {}
- else if(EventAt.code == BTN_A)
- {
- NewController->ActionDown.EndedDown = IsDown;
- }
- else if(EventAt.code == BTN_B)
- {
- NewController->ActionRight.EndedDown = IsDown;
- }
- else if(EventAt.code == BTN_X)
- {
- NewController->ActionLeft.EndedDown = IsDown;
- }
- else if(EventAt.code == BTN_Y)
- {
- NewController->ActionUp.EndedDown = IsDown;
- }
- else if(EventAt.code == BTN_START)
- {
- NewController->Start.EndedDown = IsDown;
- }
- else if(EventAt.code == BTN_BACK)
- {
- NewController->Back.EndedDown = IsDown;
- }
- } break;
-
- case EV_ABS:
- {
- if(0) {}
- else if(EventAt.code == ABS_X)
- {
- NewController->IsAnalog = true;
-
- NewController->StickAverageX = LinuxNormalizeAxisValue(EventAt.value, GamePadAt->Axes[LSTICKX]);
- }
- else if(EventAt.code == ABS_Y)
- {
- NewController->StickAverageY = -1.0f * LinuxNormalizeAxisValue(EventAt.value, GamePadAt->Axes[LSTICKY]);
- }
- else if(EventAt.code == ABS_HAT0X)
- {
- NewController->StickAverageX = EventAt.value;
- NewController->IsAnalog = false;
- }
- else if(EventAt.code == ABS_HAT0Y)
- {
- NewController->StickAverageY = -EventAt.value;
- NewController->IsAnalog = false;
- }
- } break;
-#if 0
- if(EventAt.type) printf("%d %d %d\n", EventAt.type, EventAt.code, EventAt.value);
-#endif
- }
- }
- }
- }
- }
-
- if(!GlobalPaused)
- {
- if(LinuxState.InputRecordingIndex)
- {
- LinuxRecordInput(&LinuxState, NewInput);
- }
- if(LinuxState.InputPlayingIndex)
- {
- LinuxPlayBackInput(&LinuxState, NewInput);
- }
-
- // NOTE(luca): Clear buffer
- MemSet(WindowBuffer, 0, WindowBufferSize);
- if(Game.UpdateAndRender)
- {
- Game.UpdateAndRender(&ThreadContext, &GameMemory, NewInput, &OffscreenBuffer);
- }
-
- /* NOTE(luca): How sound works
-
-Check the delay
-Check the available frames in buffer
-
-1. Too few audio frames in buffer for current frame.
--> Add more
-
-2. Too many frames available
--> Add less / drain?
-
-3. Fill first time?
--> Check delay
--> Maybe we should do this every frame?
--> Output needed frames to not have lag, this means to output maybe two frames?
-
-TODO
-- check if delay never changes
-- check if buffersize never changes
--> cache these values
-*/
-
-
- r32 SamplesToWrite = 0;
- local_persist b32 AudioFillFirstTime = true;
-
- r32 SingleFrameOfAudioFrames = TargetSecondsPerFrame*SampleRate;
-
- if(AudioFillFirstTime)
- {
- struct timespec WorkCounter = LinuxGetWallClock();
- r32 WorkSecondsElapsed = LinuxGetSecondsElapsed(LastCounter, WorkCounter);
- r32 SecondsLeftUntilFlip = TargetSecondsPerFrame - WorkSecondsElapsed;
-
- if(SecondsLeftUntilFlip > 0)
- {
- SamplesToWrite = SampleRate*(TargetSecondsPerFrame + SecondsLeftUntilFlip);
- }
- else
- {
- SamplesToWrite = SingleFrameOfAudioFrames;
- }
-
- AudioFillFirstTime = false;
- }
- else
- {
- SamplesToWrite = SingleFrameOfAudioFrames;
- }
-
-
- game_sound_output_buffer SoundBuffer = {};
- SoundBuffer.SamplesPerSecond = SampleRate;
- SoundBuffer.SampleCount = SamplesToWrite;
- SoundBuffer.Samples = (s16 *)AudioSamples;
-
- if(Game.GetSoundSamples)
- {
- Game.GetSoundSamples(&ThreadContext, &GameMemory, &SoundBuffer);
- }
-
-#if 0
- snd_pcm_sframes_t AvailableFrames = 0;
- snd_pcm_sframes_t DelayFrames;
-
- //snd_pcm_avail_update(PCMHandle);
- AvailableFrames = snd_pcm_avail(PCMHandle);
- snd_pcm_delay(PCMHandle, &DelayFrames);
-
- //printf("PeriodSize: %lu, PeriodTime: %d, BufferSize: %lu\n", PeriodSize, PeriodTime, PCMBufferSize);
- printf("BeingWritten: %lu, Avail: %ld, Delay: %ld, ToWrite: %d\n", PCMBufferSize - AvailableFrames, AvailableFrames, DelayFrames, (s32)SamplesToWrite);
-#endif
-
- LastFramesWritten = snd_pcm_writei(PCMHandle, SoundBuffer.Samples, SoundBuffer.SampleCount);
-
- if(LastFramesWritten < 0)
- {
- // TODO(luca): Logging
- // NOTE(luca): We might want to silence in case of overruns ahead of time. We also probably want to handle latency differently here.
- snd_pcm_recover(PCMHandle, LastFramesWritten, ALSA_RECOVER_SILENT);
-
- // underrun
- if(LastFramesWritten == -EPIPE)
- {
- AudioFillFirstTime = true;
- }
-
- }
- }
-
-#if 0
- printf("Expected: %d, Delay: %4d, Being written: %5lu, Written: %d, Ptr: %lu\n",
- (int)SamplesToWrite, DelayFrames, PCMBufferSize - AvailableFrames, LastFramesWritten, PCMSoundStatus->hw_ptr);
-#endif
-
- struct timespec WorkCounter = LinuxGetWallClock();
- r32 SecondsElapsedForFrame = LinuxGetSecondsElapsed(LastCounter, WorkCounter);
- if(SecondsElapsedForFrame < TargetSecondsPerFrame)
- {
-
- s64 SleepUS = (s64)((TargetSecondsPerFrame - 0.001 - SecondsElapsedForFrame)*1000000.0f);
- if(SleepUS > 0)
- {
- usleep(SleepUS);
- }
- else
- {
- // TODO(luca): Logging
- }
-
- r32 TestSecondsElapsedForFrame = (r32)(LinuxGetSecondsElapsed(LastCounter, LinuxGetWallClock()));
- if(TestSecondsElapsedForFrame < TargetSecondsPerFrame)
- {
- // TODO(luca): Log missed sleep
- }
-
- // NOTE(luca): This is to help against sleep granularity.
- while(SecondsElapsedForFrame < TargetSecondsPerFrame)
- {
- SecondsElapsedForFrame = LinuxGetSecondsElapsed(LastCounter, LinuxGetWallClock());
- }
- }
- else
- {
- // TODO(luca): Log missed frame rate!
- }
-
- struct timespec EndCounter = LinuxGetWallClock();
- r32 MSPerFrame = (r32)(LinuxGetNSecondsElapsed(LastCounter, EndCounter)/1000000.f);
- LastCounter = EndCounter;
-
- XPutImage(DisplayHandle, WindowHandle, DefaultGC, WindowImage, 0, 0,
- 0, 0,
- Width, Height);
- FlipWallClock = LinuxGetWallClock();
-
- game_input *TempInput = NewInput;
- NewInput = OldInput;
- TempInput = NewInput;
-
-#if 0
- u64 EndCycleCount = __rdtsc();
- u64 CyclesElapsed = EndCycleCount - LastCycleCount;
- LastCycleCount = EndCycleCount;
-
- r64 FPS = 0;
- r64 MCPF = (r64)(CyclesElapsed/(1000.0f*1000.0f));
- printf("%.2fms/f %.2ff/s %.2fmc/f\n", MSPerFrame, FPS, MCPF);
-#endif
-
- }
-
- }
- else
- {
- // TODO: Log this bad WindowHandle
- }
- }
- else
- {
- // TODO: Log this could not match visual info
- }
- }
- else
- {
- // TODO: Log could not get x connection
- }
- return 0;
-}