Added basic text editing.
|
@ -5,9 +5,8 @@ set CommonCompilerOptions=/Zi /FC /nologo /DVN_INTERNAL=1 /DVN_SLOW=1 /Oi /W4 /W
|
|||
if not exist "../build" mkdir "../build"
|
||||
|
||||
pushd "../build/"
|
||||
|
||||
rem cl %CommonCompilerOptions% ../code/gen.cpp
|
||||
rem gen.exe
|
||||
rem cl /Zi /nologo /FC ../code/third_party/codegen/codegen.c
|
||||
codegen ../code/
|
||||
|
||||
cl %CommonCompilerOptions% ../code/vn.cpp /LD /link /export:VN_UpdateAndRender /incremental:no
|
||||
cl %CommonCompilerOptions% ../code/win32_main.cpp /link user32.lib gdi32.lib winmm.lib opengl32.lib
|
||||
|
|
|
@ -90,6 +90,12 @@ Index = ___; break;\
|
|||
}\
|
||||
}\
|
||||
|
||||
#define Bytes(n) (n)
|
||||
#define Kilobytes(n) (n << 10)
|
||||
#define Megabytes(n) (n << 20)
|
||||
#define Gigabytes(n) (((u64)n) << 30)
|
||||
#define Terabytes(n) (((u64)n) << 40)
|
||||
|
||||
//- sixten: Min, max & clamp
|
||||
|
||||
#define Minimum(A, B) (((A)<(B))?(A):(B))
|
||||
|
@ -103,6 +109,23 @@ Index = ___; break;\
|
|||
#define IsNull(x) ((x) == 0)
|
||||
#define SetNull(x) ((x) = 0)
|
||||
|
||||
#define QueuePush_N(f,l,n,next) (IsNull(f)?\
|
||||
(((f)=(l)=(n)), SetNull((n)->next)):\
|
||||
((l)->next=(n),(l)=(n),SetNull((n)->next)))
|
||||
#define QueuePushFront_N(f,l,n,next) (IsNull(f) ? (((f) = (l) = (n)), SetNull((n)->next)) :\
|
||||
((n)->next = (f)), ((f) = (n)))
|
||||
#define QueuePop_N(f,l,next) ((f)==(l)?\
|
||||
(SetNull(f),SetNull(l)):\
|
||||
((f)=(f)->next))
|
||||
#define StackPush_N(f,n,next) ((n)->next=(f),(f)=(n))
|
||||
#define StackPop_N(f,next) (IsNull(f)?0:((f)=(f)->next))
|
||||
|
||||
#define QueuePush(f,l,n) QueuePush_N(f,l,n,Next)
|
||||
#define QueuePushFront(f,l,n) QueuePushFront_N(f,l,n,Next)
|
||||
#define QueuePop(f,l) QueuePop_N(f,l,Next)
|
||||
#define StackPush(f,n) StackPush_N(f,n,Next)
|
||||
#define StackPop(f) StackPop_N(f,Next)
|
||||
|
||||
#define DLLInsert_NP(f,l,p,n,next,prev) \
|
||||
(IsNull(f) ? (((f) = (l) = (n)), SetNull((n)->next), SetNull((n)->prev)) :\
|
||||
IsNull(p) ? (SetNull((n)->prev), (n)->next = (f), (IsNull(f) ? (0) : ((f)->prev = (n))), (f) = (n)) :\
|
||||
|
@ -168,7 +191,6 @@ inline void EndTicketMutex(ticket_mutex *Mutex)
|
|||
}
|
||||
|
||||
//- sixten: Axes
|
||||
|
||||
enum axis2
|
||||
{
|
||||
Axis2_X,
|
||||
|
@ -178,4 +200,15 @@ enum axis2
|
|||
|
||||
inline axis2 Opposite(axis2 Axis) { axis2 Result = (axis2)(!(u32)Axis); return(Result); }
|
||||
|
||||
////////////////////////////////
|
||||
//- sixten: Corners
|
||||
enum corner
|
||||
{
|
||||
Corner_00 = 0,
|
||||
Corner_10 = (1 << Axis2_X),
|
||||
Corner_01 = (1 << Axis2_Y),
|
||||
Corner_11 = (1 << Axis2_X)|(1 << Axis2_Y),
|
||||
Corner_Count,
|
||||
};
|
||||
|
||||
#endif //CORE_H
|
||||
|
|
|
@ -489,9 +489,9 @@ inline v4_s64 Max(v4_s64 A, v4_s64 B)
|
|||
|
||||
//- sixten: Range functions
|
||||
|
||||
inline range1_r32 Range1R32(r32 Min, r32 Max)
|
||||
inline range1_r32 Range1R32(r32 A, r32 B)
|
||||
{
|
||||
range1_r32 Result = {Min, Max};
|
||||
range1_r32 Result = {Min(A, B), Max(A, B)};
|
||||
return(Result);
|
||||
}
|
||||
|
||||
|
@ -501,6 +501,12 @@ inline b32 InRange(range1_r32 Range, r32 Value)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline b32 Contains(range1_r32 Range, r32 Value)
|
||||
{
|
||||
b32 Result = (Value >= Range.Min) && (Value <= Range.Max);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline r32 DimOfRange(range1_r32 Range)
|
||||
{
|
||||
r32 Result = Range.Max - Range.Min;
|
||||
|
@ -513,9 +519,9 @@ inline range1_r32 Intersection(range1_r32 A, range1_r32 B)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline range1_s32 Range1S32(s32 Min, s32 Max)
|
||||
inline range1_s32 Range1S32(s32 A, s32 B)
|
||||
{
|
||||
range1_s32 Result = {Min, Max};
|
||||
range1_s32 Result = {Min(A, B), Max(A, B)};
|
||||
return(Result);
|
||||
}
|
||||
|
||||
|
@ -525,6 +531,12 @@ inline b32 InRange(range1_s32 Range, s32 Value)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline b32 Contains(range1_s32 Range, s32 Value)
|
||||
{
|
||||
b32 Result = (Value >= Range.Min) && (Value <= Range.Max);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline s32 DimOfRange(range1_s32 Range)
|
||||
{
|
||||
s32 Result = Range.Max - Range.Min;
|
||||
|
@ -537,9 +549,9 @@ inline range1_s32 Intersection(range1_s32 A, range1_s32 B)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline range1_s64 Range1S64(s64 Min, s64 Max)
|
||||
inline range1_s64 Range1S64(s64 A, s64 B)
|
||||
{
|
||||
range1_s64 Result = {Min, Max};
|
||||
range1_s64 Result = {Min(A, B), Max(A, B)};
|
||||
return(Result);
|
||||
}
|
||||
|
||||
|
@ -549,6 +561,12 @@ inline b32 InRange(range1_s64 Range, s64 Value)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline b32 Contains(range1_s64 Range, s64 Value)
|
||||
{
|
||||
b32 Result = (Value >= Range.Min) && (Value <= Range.Max);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline s64 DimOfRange(range1_s64 Range)
|
||||
{
|
||||
s64 Result = Range.Max - Range.Min;
|
||||
|
@ -561,9 +579,9 @@ inline range1_s64 Intersection(range1_s64 A, range1_s64 B)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline range2_r32 Range2R32(v2_r32 Min, v2_r32 Max)
|
||||
inline range2_r32 Range2R32(v2_r32 A, v2_r32 B)
|
||||
{
|
||||
range2_r32 Result = {Min, Max};
|
||||
range2_r32 Result = {Min(A, B), Max(A, B)};
|
||||
return(Result);
|
||||
}
|
||||
|
||||
|
@ -574,6 +592,13 @@ inline b32 InRange(range2_r32 Range, v2_r32 Value)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline b32 Contains(range2_r32 Range, v2_r32 Value)
|
||||
{
|
||||
b32 Result = ((Value.x >= Range.Min.x) && (Value.y >= Range.Min.y) &&
|
||||
(Value.x <= Range.Max.x) && (Value.y <= Range.Max.y));
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline v2_r32 DimOfRange(range2_r32 Range)
|
||||
{
|
||||
v2_r32 Result = Range.Max - Range.Min;
|
||||
|
@ -586,9 +611,16 @@ inline range2_r32 Intersection(range2_r32 A, range2_r32 B)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline range2_s32 Range2S32(v2_s32 Min, v2_s32 Max)
|
||||
inline v2_r32 CornerFromRange(range2_r32 Range, corner Corner)
|
||||
{
|
||||
range2_s32 Result = {Min, Max};
|
||||
v2_r32 Result = V2R32((Corner & (1 << Axis2_X))?Range.Min.x:Range.Max.x,
|
||||
(Corner & (1 << Axis2_Y))?Range.Min.y:Range.Max.y);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline range2_s32 Range2S32(v2_s32 A, v2_s32 B)
|
||||
{
|
||||
range2_s32 Result = {Min(A, B), Max(A, B)};
|
||||
return(Result);
|
||||
}
|
||||
|
||||
|
@ -599,6 +631,13 @@ inline b32 InRange(range2_s32 Range, v2_s32 Value)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline b32 Contains(range2_s32 Range, v2_s32 Value)
|
||||
{
|
||||
b32 Result = ((Value.x >= Range.Min.x) && (Value.y >= Range.Min.y) &&
|
||||
(Value.x <= Range.Max.x) && (Value.y <= Range.Max.y));
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline v2_s32 DimOfRange(range2_s32 Range)
|
||||
{
|
||||
v2_s32 Result = Range.Max - Range.Min;
|
||||
|
@ -611,9 +650,16 @@ inline range2_s32 Intersection(range2_s32 A, range2_s32 B)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline range2_s64 Range2S64(v2_s64 Min, v2_s64 Max)
|
||||
inline v2_s32 CornerFromRange(range2_s32 Range, corner Corner)
|
||||
{
|
||||
range2_s64 Result = {Min, Max};
|
||||
v2_s32 Result = V2S32((Corner & (1 << Axis2_X))?Range.Min.x:Range.Max.x,
|
||||
(Corner & (1 << Axis2_Y))?Range.Min.y:Range.Max.y);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline range2_s64 Range2S64(v2_s64 A, v2_s64 B)
|
||||
{
|
||||
range2_s64 Result = {Min(A, B), Max(A, B)};
|
||||
return(Result);
|
||||
}
|
||||
|
||||
|
@ -624,6 +670,13 @@ inline b32 InRange(range2_s64 Range, v2_s64 Value)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline b32 Contains(range2_s64 Range, v2_s64 Value)
|
||||
{
|
||||
b32 Result = ((Value.x >= Range.Min.x) && (Value.y >= Range.Min.y) &&
|
||||
(Value.x <= Range.Max.x) && (Value.y <= Range.Max.y));
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline v2_s64 DimOfRange(range2_s64 Range)
|
||||
{
|
||||
v2_s64 Result = Range.Max - Range.Min;
|
||||
|
@ -635,3 +688,10 @@ inline range2_s64 Intersection(range2_s64 A, range2_s64 B)
|
|||
range2_s64 Result = {Max(A.Min, B.Min), Min(A.Max, B.Max)};
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline v2_s64 CornerFromRange(range2_s64 Range, corner Corner)
|
||||
{
|
||||
v2_s64 Result = V2S64((Corner & (1 << Axis2_X))?Range.Min.x:Range.Max.x,
|
||||
(Corner & (1 << Axis2_Y))?Range.Min.y:Range.Max.y);
|
||||
return(Result);
|
||||
}
|
|
@ -325,35 +325,47 @@ inline v4_s64 Max(v4_s64 A, v4_s64 B);
|
|||
|
||||
//- sixten: Range functions
|
||||
|
||||
inline range1_r32 Range1R32(r32 Min, r32 Max);
|
||||
// sixten(NOTE): InRange means that the value is in [Min, Max) and
|
||||
// Contains means that the value is in [Min, Max]
|
||||
|
||||
inline range1_r32 Range1R32(r32 A, r32 B);
|
||||
inline b32 InRange(range1_r32 Range, r32 Value);
|
||||
inline b32 Contains(range1_r32 Range, r32 Value);
|
||||
inline r32 DimOfRange(range1_r32 Range);
|
||||
inline range1_r32 Intersection(range1_r32 A, range1_r32 B);
|
||||
|
||||
inline range1_s32 Range1S32(s32 Min, s32 Max);
|
||||
inline range1_s32 Range1S32(s32 A, s32 B);
|
||||
inline b32 InRange(range1_s32 Range, s32 Value);
|
||||
inline b32 Contains(range1_s32 Range, s32 Value);
|
||||
inline s32 DimOfRange(range1_s32 Range);
|
||||
inline range1_s32 Intersection(range1_s32 A, range1_s32 B);
|
||||
|
||||
inline range1_s64 Range1S64(s64 Min, s64 Max);
|
||||
inline range1_s64 Range1S64(s64 A, s64 B);
|
||||
inline b32 InRange(range1_s64 Range, s64 Value);
|
||||
inline b32 Contains(range1_s64 Range, s64 Value);
|
||||
inline s64 DimOfRange(range1_s64 Range);
|
||||
inline range1_s64 Intersection(range1_s64 A, range1_s64 B);
|
||||
|
||||
inline range2_r32 Range2R32(v2_r32 Min, v2_r32 Max);
|
||||
inline range2_r32 Range2R32(v2_r32 A, v2_r32 B);
|
||||
inline b32 InRange(range2_r32 Range, v2_r32 Value);
|
||||
inline b32 Contains(range2_r32 Range, v2_r32 Value);
|
||||
inline v2_r32 DimOfRange(range2_r32 Range);
|
||||
inline range2_r32 Intersection(range2_r32 A, range2_r32 B);
|
||||
inline v2_r32 CornerFromRange(range2_r32 Range, corner Corner);
|
||||
|
||||
inline range2_s32 Range2S32(v2_s32 Min, v2_s32 Max);
|
||||
inline range2_s32 Range2S32(v2_s32 A, v2_s32 B);
|
||||
inline b32 InRange(range2_s32 Range, v2_s32 Value);
|
||||
inline b32 Contains(range2_s32 Range, v2_s32 Value);
|
||||
inline v2_s32 DimOfRange(range2_s32 Range);
|
||||
inline range2_s32 Intersection(range2_s32 A, range2_s32 B);
|
||||
inline v2_s32 CornerFromRange(range2_s32 Range, corner Corner);
|
||||
|
||||
inline range2_s64 Range2S64(v2_s64 Min, v2_s64 Max);
|
||||
inline range2_s64 Range2S64(v2_s64 A, v2_s64 B);
|
||||
inline b32 InRange(range2_s64 Range, v2_s64 Value);
|
||||
inline b32 Contains(range2_s64 Range, v2_s64 Value);
|
||||
inline v2_s64 DimOfRange(range2_s64 Range);
|
||||
inline range2_s64 Intersection(range2_s64 A, range2_s64 B);
|
||||
inline v2_s64 CornerFromRange(range2_s64 Range, corner Corner);
|
||||
|
||||
//- sixten: Shorthand base types
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
//- sixten: Common memory functions
|
||||
////////////////////////////////
|
||||
//- sixten: Common Memory Functions
|
||||
|
||||
static void Copy(void *Dest, void *Source, umm Count)
|
||||
{
|
||||
if(Count <= S64_Max)
|
||||
{
|
||||
u8 *Dest8 = (u8 *)Dest;
|
||||
u8 *Source8 = (u8 *)Source;
|
||||
|
@ -10,8 +13,25 @@ static void Copy(void *Dest, void *Source, umm Count)
|
|||
*Dest8++ = *Source8++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyReverse(void *Dest, void *Source, umm Count)
|
||||
{
|
||||
if(Count <= S64_Max)
|
||||
{
|
||||
u8 *Dest8 = (u8 *)Dest + Count;
|
||||
u8 *Source8 = (u8 *)Source + Count;
|
||||
|
||||
while(Count--)
|
||||
{
|
||||
*--Dest8 = *--Source8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Fill(void *Dest, u8 Value, umm Count)
|
||||
{
|
||||
if(Count <= S64_Max)
|
||||
{
|
||||
u8 *Dest8 = (u8 *)Dest;
|
||||
|
||||
|
@ -20,134 +40,103 @@ static void Fill(void *Dest, u8 Value, umm Count)
|
|||
*Dest8++ = Value;
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: Memory arena functions
|
||||
|
||||
static void Release(memory_arena *Arena)
|
||||
{
|
||||
while(Arena->CurrentBlock != 0)
|
||||
{
|
||||
platform_memory_block *MemoryBlock = Arena->CurrentBlock;
|
||||
b32 IsLastBlock = (MemoryBlock->ArenaPrev == 0);
|
||||
Arena->CurrentBlock = MemoryBlock->ArenaPrev;
|
||||
Platform.DeallocateMemory(MemoryBlock);
|
||||
|
||||
if(IsLastBlock)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline umm GetAlignmentOffset(memory_arena *Arena, umm Alignment)
|
||||
{
|
||||
umm AlignmentOffset = 0;
|
||||
////////////////////////////////
|
||||
//- sixten: Memory Arena Functions
|
||||
|
||||
umm ResultPointer = (umm)Arena->CurrentBlock + Arena->CurrentBlock->Used;
|
||||
umm AlignmentMask = Alignment - 1;
|
||||
if(ResultPointer & AlignmentMask)
|
||||
static memory_arena *ArenaAllocate(u64 Size)
|
||||
{
|
||||
AlignmentOffset = Alignment - (ResultPointer & AlignmentMask);
|
||||
u64 RoundedSize = Size+Megabytes(64)-1;
|
||||
RoundedSize -= RoundedSize&Megabytes(64);
|
||||
void *Memory = Platform.Reserve(RoundedSize);
|
||||
u64 InitialCommitSize = MEMORY_ARENA_COMMIT_SIZE;
|
||||
Platform.Commit(Memory, InitialCommitSize);
|
||||
memory_arena *Arena = (memory_arena *)Memory;
|
||||
Arena->Position = sizeof(memory_arena);
|
||||
Arena->CommitPosition = InitialCommitSize;
|
||||
Arena->Size = Size;
|
||||
Arena->Align = 8;
|
||||
return(Arena);
|
||||
}
|
||||
|
||||
return(AlignmentOffset);
|
||||
}
|
||||
|
||||
inline umm GetEffectiveSizeFor(memory_arena *Arena, umm InitialSize, arena_push_params Params)
|
||||
static void ArenaRelease(memory_arena *Arena)
|
||||
{
|
||||
umm Size = InitialSize;
|
||||
|
||||
umm AlignmentOffset = GetAlignmentOffset(Arena, Params.Alignment);
|
||||
Size += AlignmentOffset;
|
||||
|
||||
return(Size);
|
||||
Platform.Release(Arena);
|
||||
}
|
||||
|
||||
static void *PushSize_(memory_arena *Arena, umm InitialSize, arena_push_params Params)
|
||||
static void *ArenaPushNoClear(memory_arena *Arena, u64 Size)
|
||||
{
|
||||
void *Result = 0;
|
||||
|
||||
umm Size = 0;
|
||||
if(Arena->CurrentBlock)
|
||||
if(Arena->Position + Size <= Arena->Size)
|
||||
{
|
||||
Size = GetEffectiveSizeFor(Arena, InitialSize, Params);
|
||||
}
|
||||
|
||||
if(!Arena->CurrentBlock || ((Arena->CurrentBlock->Used + Size) > Arena->CurrentBlock->Size))
|
||||
u8 *Base = (u8 *)Arena;
|
||||
u64 PostAlignPos = (Arena->Position+Arena->Align-1);
|
||||
PostAlignPos -= PostAlignPos%Arena->Align;
|
||||
u64 Align = PostAlignPos - Arena->Position;
|
||||
Result = Base + Arena->Position + Align;
|
||||
Arena->Position += Size + Align;
|
||||
if(Arena->CommitPosition < Arena->Position)
|
||||
{
|
||||
Size = InitialSize;
|
||||
|
||||
if(!Arena->MinimumBlockSize)
|
||||
u64 ToCommit = Arena->Position - Arena->CommitPosition;
|
||||
ToCommit += MEMORY_ARENA_COMMIT_SIZE - 1;
|
||||
ToCommit -= ToCommit%MEMORY_ARENA_COMMIT_SIZE;
|
||||
Platform.Commit(Base + Arena->CommitPosition, ToCommit);
|
||||
Arena->CommitPosition += ToCommit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Arena->MinimumBlockSize = 1024*1024;
|
||||
InvalidCodepath;
|
||||
}
|
||||
|
||||
umm BlockSize = Maximum(Size, Arena->MinimumBlockSize);
|
||||
|
||||
platform_memory_block *NewBlock = Platform.AllocateMemory(BlockSize);
|
||||
NewBlock->ArenaPrev = Arena->CurrentBlock;
|
||||
Arena->CurrentBlock = NewBlock;
|
||||
}
|
||||
|
||||
Assert((Arena->CurrentBlock->Used + Size) <= Arena->CurrentBlock->Size);
|
||||
|
||||
umm AlignmentOffset = GetAlignmentOffset(Arena, Params.Alignment);
|
||||
umm OffsetInBlock = Arena->CurrentBlock->Used + AlignmentOffset;
|
||||
Result = Arena->CurrentBlock->Base + OffsetInBlock;
|
||||
Arena->CurrentBlock->Used += Size;
|
||||
|
||||
Assert(Size >= InitialSize);
|
||||
Assert(Arena->CurrentBlock->Used <= Arena->CurrentBlock->Size);
|
||||
|
||||
if(Params.Flags & ArenaFlag_ClearToZero)
|
||||
{
|
||||
Fill(Result, 0, InitialSize);
|
||||
}
|
||||
|
||||
return(Result);
|
||||
}
|
||||
|
||||
//- sixten: Bootsrapping helpers
|
||||
|
||||
static void *BootstrapPushSize(umm Size, umm OffsetToArena)
|
||||
static void *ArenaPush(memory_arena *Arena, u64 Size)
|
||||
{
|
||||
memory_arena Arena = {};
|
||||
void *Result = PushSize(&Arena, Size);
|
||||
*(memory_arena *)((u8 *)Result + OffsetToArena) = Arena;
|
||||
|
||||
void *Result = ArenaPushNoClear(Arena, Size);
|
||||
Fill(Result, 0, Size);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
//- sixten: Temporary memory functions
|
||||
static void ArenaPopTo(memory_arena *Arena, u64 Position)
|
||||
{
|
||||
u64 MinPosition = sizeof(*Arena);
|
||||
u64 NewPosition = Maximum(MinPosition, Position);
|
||||
Arena->Position = NewPosition;
|
||||
u64 CommitAlignedPosition = Arena->Position+MEMORY_ARENA_COMMIT_SIZE-1;
|
||||
CommitAlignedPosition -= CommitAlignedPosition%MEMORY_ARENA_COMMIT_SIZE;
|
||||
if(CommitAlignedPosition + MEMORY_ARENA_DECOMMIT_THRESHOLD <= Arena->CommitPosition)
|
||||
{
|
||||
u8 *Base = (u8 *)Arena;
|
||||
u64 ToDecommit = Arena->CommitPosition-CommitAlignedPosition;
|
||||
Platform.Decommit(Base+CommitAlignedPosition, ToDecommit);
|
||||
Arena->CommitPosition -= ToDecommit;
|
||||
}
|
||||
}
|
||||
|
||||
static void ArenaClear(memory_arena *Arena)
|
||||
{
|
||||
ArenaPopTo(Arena, sizeof(*Arena));
|
||||
}
|
||||
|
||||
static void ArenaSetAlign(memory_arena *Arena, u64 Align)
|
||||
{
|
||||
Arena->Align = Align;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//- sixten: Temporary Memory Functions
|
||||
|
||||
static temporary_memory BeginTemporaryMemory(memory_arena *Arena)
|
||||
{
|
||||
temporary_memory Result;
|
||||
Result.Arena = Arena;
|
||||
Result.Block = Arena->CurrentBlock;
|
||||
Result.Used = Arena->CurrentBlock ? Arena->CurrentBlock->Used : 0;
|
||||
|
||||
++Arena->TemporaryMemoryCount;
|
||||
|
||||
return(Result);
|
||||
temporary_memory Temp;
|
||||
Temp.Arena = Arena;
|
||||
Temp.Position = Arena->Position;
|
||||
return(Temp);
|
||||
}
|
||||
|
||||
static void EndTemporaryMemory(temporary_memory Temp)
|
||||
{
|
||||
memory_arena *Arena = Temp.Arena;
|
||||
while(Arena->CurrentBlock != Temp.Block)
|
||||
{
|
||||
platform_memory_block *MemoryBlock = Arena->CurrentBlock;
|
||||
Arena->CurrentBlock = MemoryBlock->ArenaPrev;
|
||||
Platform.DeallocateMemory(MemoryBlock);
|
||||
}
|
||||
|
||||
if(Arena->CurrentBlock)
|
||||
{
|
||||
Assert(Arena->CurrentBlock->Used >= Temp.Used);
|
||||
Arena->CurrentBlock->Used = Temp.Used;
|
||||
}
|
||||
|
||||
Assert(Arena->TemporaryMemoryCount > 0);
|
||||
--Arena->TemporaryMemoryCount;
|
||||
ArenaPopTo(Temp.Arena, Temp.Position);
|
||||
}
|
|
@ -3,72 +3,54 @@
|
|||
#ifndef CORE_MEMORY_H
|
||||
#define CORE_MEMORY_H
|
||||
|
||||
//- sixten: Common memory functions
|
||||
////////////////////////////////
|
||||
//- sixten: Common Memory Functions
|
||||
|
||||
static void Copy(void *Dest, void *Source, umm Count);
|
||||
static void CopyReverse(void *Dest, void *Source, umm Count);
|
||||
static void Fill(void *Dest, u8 Value, umm Count);
|
||||
#define Move(Dest, Source, Count) memmove(Dest, Source, Count)
|
||||
|
||||
//- sixten: Memory arena types
|
||||
|
||||
struct platform_memory_block;
|
||||
////////////////////////////////
|
||||
//- sixten: Memory Arena Types
|
||||
|
||||
struct memory_arena
|
||||
{
|
||||
platform_memory_block *CurrentBlock;
|
||||
umm MinimumBlockSize;
|
||||
s32 TemporaryMemoryCount;
|
||||
u64 Position;
|
||||
u64 CommitPosition;
|
||||
u64 Size;
|
||||
u64 Align;
|
||||
u64 Unused[4];
|
||||
};
|
||||
|
||||
struct temporary_memory
|
||||
{
|
||||
memory_arena *Arena;
|
||||
platform_memory_block *Block;
|
||||
umm Used;
|
||||
u64 Position;
|
||||
};
|
||||
|
||||
//- sixten: Arena push flags
|
||||
////////////////////////////////
|
||||
//~ sixten: Memory Arena Settings
|
||||
#define MEMORY_ARENA_COMMIT_SIZE Kilobytes(4)
|
||||
#define MEMORY_ARENA_DECOMMIT_THRESHOLD Megabytes(64)
|
||||
|
||||
enum arena_push_flag
|
||||
{
|
||||
ArenaFlag_ClearToZero = 0x1,
|
||||
};
|
||||
////////////////////////////////
|
||||
//- sixten: Memory Arena Functions
|
||||
|
||||
struct arena_push_params
|
||||
{
|
||||
u32 Flags;
|
||||
u32 Alignment;
|
||||
};
|
||||
static memory_arena *ArenaAllocate(u64 Size);
|
||||
static void ArenaRelease(memory_arena *Arena);
|
||||
static void *ArenaPushNoClear(memory_arena *Arena, u64 Size);
|
||||
static void *ArenaPush(memory_arena *Arena, u64 Size);
|
||||
static void ArenaPopTo(memory_arena *Arena, u64 Position);
|
||||
static void ArenaClear(memory_arena *Arena);
|
||||
static void ArenaSetAlign(memory_arena *Arena, u64 Align);
|
||||
#define PushArray(Arena, type, Count) (type *)ArenaPush((Arena), sizeof(type)*(Count))
|
||||
#define PushArrayNoClear(Arena, type, Count) (type *)ArenaPushNoClear((Arena), sizeof(type)*(Count))
|
||||
#define PushStruct(Arena, type) (type *)ArenaPush((Arena), sizeof(type))
|
||||
#define PushStructNoClear(Arena, type) (type *)ArenaPushNoClear((Arena), sizeof(type))
|
||||
|
||||
inline arena_push_params DefaultArenaParams(void)
|
||||
{
|
||||
arena_push_params Params = {};
|
||||
Params.Flags = ArenaFlag_ClearToZero;
|
||||
Params.Alignment = 4;
|
||||
return(Params);
|
||||
}
|
||||
|
||||
inline arena_push_params NoClear(void)
|
||||
{
|
||||
arena_push_params Params = DefaultArenaParams();
|
||||
Params.Flags &= ~ArenaFlag_ClearToZero;
|
||||
return(Params);
|
||||
}
|
||||
|
||||
//- sixten: Memory arena functions
|
||||
|
||||
static void Release(memory_arena *Arena);
|
||||
|
||||
#define PushSize(Arena, InitialSize, ...) PushSize_(Arena, InitialSize, __VA_ARGS__)
|
||||
#define PushStruct(Arena, type, ...) (type *)PushSize_(Arena, sizeof(type), __VA_ARGS__)
|
||||
#define PushArray(Arena, type, Count, ...) (type *)PushSize_(Arena, sizeof(type)*Count, __VA_ARGS__)
|
||||
static void *PushSize_(memory_arena *Arena, umm InitialSize, arena_push_params Params = DefaultArenaParams());
|
||||
|
||||
//- sixten: Bootstrapping helpers
|
||||
|
||||
#define BootstrapPushStruct(type, Member) (type *)BootstrapPushSize(sizeof(type), OffsetOf(type, Member))
|
||||
static void *BootstrapPushSize(umm Size, umm OffsetToArena);
|
||||
|
||||
//- sixten: Temporary memory functions
|
||||
////////////////////////////////
|
||||
//- sixten: Temporary Memory Functions
|
||||
|
||||
static temporary_memory BeginTemporaryMemory(memory_arena *Arena);
|
||||
static void EndTemporaryMemory(temporary_memory Temp);
|
||||
|
|
|
@ -186,7 +186,7 @@ static s64 LastIndexOf(string String, string Sub)
|
|||
static string PushString(memory_arena *Arena, string String)
|
||||
{
|
||||
string Result;
|
||||
Result.Data = PushArray(Arena, u8, String.Count, NoClear());
|
||||
Result.Data = PushArrayNoClear(Arena, u8, String.Count);
|
||||
Result.Count = String.Count;
|
||||
Copy(Result.Data, String.Data, String.Count);
|
||||
|
||||
|
@ -200,7 +200,7 @@ static string PushFormatVariadic(memory_arena *Arena, char *Format, va_list Argu
|
|||
|
||||
string Result;
|
||||
Result.Count = stbsp_vsnprintf(0, 0, Format, ArgumentsCopy);
|
||||
Result.Data = PushArray(Arena, u8, Result.Count + 1, NoClear());
|
||||
Result.Data = PushArrayNoClear(Arena, u8, Result.Count + 1);
|
||||
Result.Data[Result.Count] = 0;
|
||||
|
||||
stbsp_vsnprintf((char *)Result.Data, (s32)Result.Count + 1, Format, Arguments);
|
||||
|
@ -294,6 +294,41 @@ static string StringFromCodepoint(memory_arena *Arena, u32 Codepoint)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
//- sixten: Replacing
|
||||
|
||||
static string RemoveAll(memory_arena *Arena, string Text, char ToRemove)
|
||||
{
|
||||
//- sixten: get new count
|
||||
s64 Occurrences = 0;
|
||||
u8 *TextBegin = Text.Data;
|
||||
u8 *TextEnd = TextBegin+Text.Count;
|
||||
for(u8 *Char = TextBegin; Char != TextEnd; Char += 1)
|
||||
{
|
||||
if(*Char == ToRemove)
|
||||
{
|
||||
Occurrences += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: copy over all other bytes
|
||||
s64 Count = Text.Count - Occurrences;
|
||||
u8 *Data = PushArrayNoClear(Arena, u8, Count + 1);
|
||||
Data[Count] = 0;
|
||||
|
||||
s64 Index = 0;
|
||||
for(u8 *Char = TextBegin; Char != TextEnd; Char += 1)
|
||||
{
|
||||
if(*Char != ToRemove)
|
||||
{
|
||||
Data[Index] = *Char;
|
||||
Index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
string Result = MakeString(Data, Count);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
//- sixten: "C Style" strings
|
||||
|
||||
static s64 StringLength(char *String)
|
||||
|
@ -344,9 +379,136 @@ static string JoinStringList(string_list *List, memory_arena *Arena)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
//~ sixten: String Chunk Functions
|
||||
static string_chunk_list MakeStringChunkList(s64 ChunkSize)
|
||||
{
|
||||
string_chunk_list Result = {};
|
||||
Result.ChunkSize = ChunkSize;
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static string JoinStringChunkList(memory_arena *Arena, string_chunk_list *List)
|
||||
{
|
||||
string Result = {};
|
||||
Result.Count = List->TotalCount;
|
||||
Result.Data = PushArrayNoClear(Arena, u8, List->TotalCount + 1);
|
||||
s64 Index = 0;
|
||||
s64 CountRemaining = List->TotalCount;
|
||||
for(string_node *Node = List->First; Node != 0; Node = Node->Next)
|
||||
{
|
||||
string String = Node->String;
|
||||
Copy(Result.Data + Index, String.Data, Min(CountRemaining, List->ChunkSize));
|
||||
CountRemaining -= List->ChunkSize;
|
||||
Index += String.Count;
|
||||
}
|
||||
return(Result);
|
||||
}
|
||||
|
||||
// sixten(TODO): Incomplete, remove maybe?
|
||||
static void ReplaceRange(memory_arena *Arena, string_chunk_list *List, string Text, range1_s64 Range)
|
||||
{
|
||||
s64 NewTotalCount = Max(0ULL, List->TotalCount - DimOfRange(Range)) + Text.Count;
|
||||
|
||||
//- sixten: do we need to allocate more chunks?
|
||||
if(List->ChunkSize*List->ChunkCount < NewTotalCount)
|
||||
{
|
||||
s64 ChunksToAlloc = (NewTotalCount - List->ChunkSize*List->ChunkCount)/List->ChunkSize + 1;
|
||||
for(s64 Index = 0; Index < ChunksToAlloc; Index += 1)
|
||||
{
|
||||
if(DLLIsEmpty(List->FirstFree))
|
||||
{
|
||||
string_node *Node = PushStructNoClear(Arena, string_node);
|
||||
Node->String.Count = 0;
|
||||
Node->String.Data = PushArrayNoClear(Arena, u8, List->ChunkSize);
|
||||
DLLInsertLast(List->First, List->Last, Node);
|
||||
}
|
||||
else
|
||||
{
|
||||
string_node *Node = List->FirstFree;
|
||||
Node->String.Count = 0;
|
||||
DLLRemove(List->FirstFree, List->LastFree, Node);
|
||||
DLLInsertLast(List->First, List->Last, Node);
|
||||
}
|
||||
}
|
||||
|
||||
List->ChunkCount += ChunksToAlloc;
|
||||
}
|
||||
|
||||
s64 CountDelta = NewTotalCount - List->TotalCount;
|
||||
|
||||
// sixten: I cannot be bothered enough to figure out the correct implementation for this. However, if I do this - remember that you can rearrange
|
||||
// the ordering of the linked list, instead of actually copying over the bytes for the majority of this.
|
||||
Assert(AbsoluteValue(CountDelta) < List->ChunkSize);
|
||||
|
||||
//- sixten: find the first and last affected nodes
|
||||
s64 FirstAffectedNodeIndex = Range.Min/List->ChunkSize;
|
||||
s64 LastAffectedNodeIndex = Range.Max/List->ChunkSize;
|
||||
string_node *FirstAffectedNode = List->First;
|
||||
for(s64 WalkIndex = 0; WalkIndex < FirstAffectedNodeIndex; WalkIndex += 1)
|
||||
{
|
||||
FirstAffectedNode = FirstAffectedNode->Next;
|
||||
}
|
||||
string_node *LastAffectedNode = FirstAffectedNode;
|
||||
for(s64 WalkIndex = 0; WalkIndex < LastAffectedNodeIndex-FirstAffectedNodeIndex; WalkIndex += 1)
|
||||
{
|
||||
LastAffectedNode = LastAffectedNode->Next;
|
||||
}
|
||||
|
||||
if(CountDelta >= 0)
|
||||
{
|
||||
//- sixten: insertion - make room and the copy the data
|
||||
s64 WriteOffset = Range.Min%List->ChunkSize;
|
||||
for(string_node *Node = List->Last; Node != 0; Node = Node->Prev)
|
||||
{
|
||||
CopyReverse(Node->String.Data+CountDelta+WriteOffset, Node->String.Data+WriteOffset, List->ChunkSize-CountDelta-WriteOffset);
|
||||
|
||||
if(Node == LastAffectedNode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Copy(Node->String.Data, Node->Prev->String.Data+List->ChunkSize-CountDelta, CountDelta);
|
||||
}
|
||||
}
|
||||
s64 SourceOffset = 0;
|
||||
for(string_node *Node = FirstAffectedNode; Node != 0; Node = Node->Next)
|
||||
{
|
||||
Copy(Node->String.Data+WriteOffset, Text.Data+SourceOffset, Min(List->ChunkSize-WriteOffset, Text.Count-SourceOffset));
|
||||
SourceOffset += List->ChunkSize;
|
||||
if(Node == LastAffectedNode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(CountDelta < 0)
|
||||
{
|
||||
//- sixten: deletion
|
||||
}
|
||||
|
||||
List->TotalCount = NewTotalCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//~ sixten: Unicode
|
||||
|
||||
read_only u8 UTF8Lengths[] =
|
||||
{
|
||||
1, 1, 1, 1, // 000xx
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
0, 0, 0, 0, // 100xx
|
||||
0, 0, 0, 0,
|
||||
2, 2, 2, 2, // 110xx
|
||||
3, 3, // 1110x
|
||||
4, // 11110
|
||||
0, // 11111
|
||||
};
|
||||
|
||||
static utf8_iterator IterateUTF8String(string String)
|
||||
{
|
||||
utf8_iterator Iter = {};
|
||||
|
@ -432,3 +594,114 @@ static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint)
|
|||
|
||||
return(Length);
|
||||
}
|
||||
|
||||
//~ sixten: Text point
|
||||
|
||||
static text_point TextPointFromOffset(string String, s64 Offset)
|
||||
{
|
||||
text_point Point = {1, 1};
|
||||
for(s64 Index = 0;
|
||||
Index < String.Count && Index < Offset;
|
||||
++Index)
|
||||
{
|
||||
if(String.Data[Index] == '\n')
|
||||
{
|
||||
++Point.Line;
|
||||
Point.Column = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(String.Data[Index] != '\r')
|
||||
{
|
||||
++Point.Column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(Point);
|
||||
}
|
||||
|
||||
static s64 OffsetFromTextPoint(string String, text_point Point)
|
||||
{
|
||||
s64 Offset = 0;
|
||||
|
||||
Point.Line -= 1;
|
||||
Point.Column -= 1;
|
||||
|
||||
u8 *StringBegin = String.Data;
|
||||
u8 *StringEnd = StringBegin+String.Count;
|
||||
u8 *Char = StringBegin;
|
||||
//- sixten: find the start of the correct line
|
||||
for(;Char < StringEnd && Point.Line > 0; Char += 1, Offset += 1)
|
||||
{
|
||||
if(*Char == '\n')
|
||||
{
|
||||
Point.Line -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
for(;Char < StringEnd && Point.Column > 0; Char += 1, Offset += 1)
|
||||
{
|
||||
//- sixten: if a newline has been reached, the initial column was out of bounds
|
||||
if(*Char == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
//- sixten: tabs are two-spaces, so we must take that into account
|
||||
#if 0
|
||||
if(*Char == '\t')
|
||||
{
|
||||
Point.Column -= 1;
|
||||
}
|
||||
#endif
|
||||
Point.Column -= 1;
|
||||
}
|
||||
|
||||
return(Offset);
|
||||
}
|
||||
|
||||
static text_range TextRange(text_point A, text_point B)
|
||||
{
|
||||
text_range Result;
|
||||
if(A.Line > B.Line || (A.Line == B.Line && A.Column > B.Column))
|
||||
{
|
||||
Result = {B, A};
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = {A, B};
|
||||
}
|
||||
return(Result);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: 1D Interval List & Array Functions
|
||||
static void Range1S64ListPush(memory_arena *Arena, range1_s64_list *List, range1_s64 Range)
|
||||
{
|
||||
range1_s64_node *Node = PushStructNoClear(Arena, range1_s64_node);
|
||||
Node->Range = Range;
|
||||
QueuePush(List->First, List->Last, Node);
|
||||
List->Count += 1;
|
||||
}
|
||||
|
||||
static range1_s64_array Range1S64ArrayFromList(memory_arena *Arena, range1_s64_list *List)
|
||||
{
|
||||
range1_s64_array Result = {};
|
||||
Result.Count = List->Count;
|
||||
Result.Ranges = PushArray(Arena, range1_s64, List->Count);
|
||||
s64 Index = 0;
|
||||
for(range1_s64_node *Node = List->First; Node != 0; Node = Node->Next, Index += 1)
|
||||
{
|
||||
Result.Ranges[Index] = Node->Range;
|
||||
}
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static s64 OffsetFromTextPoint(string String, range1_s64_array Lines, text_point Point)
|
||||
{
|
||||
s64 LineIndex = Clamp(Point.Line, 1, Lines.Count) - 1;
|
||||
range1_s64 Range = Lines.Ranges[LineIndex];
|
||||
s64 ColumnIndex = Clamp(Point.Column, 1, DimOfRange(Range)) - 1;
|
||||
s64 Offset = Range.Min+ColumnIndex;
|
||||
return(Offset);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,22 @@ struct string_list
|
|||
s64 TotalCount;
|
||||
};
|
||||
|
||||
/////////////////////////////////////
|
||||
//~ sixten: String Chunk Types
|
||||
struct string_chunk_list
|
||||
{
|
||||
s64 ChunkSize;
|
||||
s64 ChunkCount;
|
||||
s64 TotalCount;
|
||||
|
||||
string_node *First;
|
||||
string_node *Last;
|
||||
string_node *FirstFree;
|
||||
string_node *LastFree;
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////
|
||||
//~ sixten: Char funcitons
|
||||
|
||||
inline b32 IsWhitespace(char C);
|
||||
|
@ -37,6 +52,7 @@ inline b32 IsDigit(char C);
|
|||
inline b32 IsLetter(char C);
|
||||
|
||||
|
||||
/////////////////////////////////////
|
||||
//~ sixten: String functions
|
||||
|
||||
//- sixten: Basic constructors
|
||||
|
@ -79,11 +95,21 @@ static s64 ConvertStringToS64(string String);
|
|||
static string ConvertS64ToString(memory_arena *Arena, s64 Value);
|
||||
static string StringFromCodepoint(memory_arena *Arena, u32 Codepoint);
|
||||
|
||||
//- sixten: Replacing
|
||||
|
||||
static string RemoveAll(memory_arena *Arena, string Text, char ToRemove);
|
||||
|
||||
//- sixten: "C Style" strings
|
||||
|
||||
static s64 StringLength(char *String);
|
||||
|
||||
|
||||
/////////////////////////////////////
|
||||
//~ sixten: String Chunk Functions
|
||||
static string_chunk_list MakeStringChunkList(s64 ChunkSize);
|
||||
static string JoinStringChunkList(memory_arena *Arena, string_chunk_list *List);
|
||||
static void ReplaceRange(memory_arena *Arena, string_chunk_list *List, string Text, range1_s64 Range);
|
||||
|
||||
//~ sixten: String list
|
||||
|
||||
static void AppendString(string_list *List, string String, memory_arena *Arena);
|
||||
|
@ -108,4 +134,50 @@ static b32 IsValid(utf8_iterator *Iter);
|
|||
|
||||
static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint);
|
||||
|
||||
//~ sixten: Text point
|
||||
|
||||
struct text_point
|
||||
{
|
||||
s64 Line;
|
||||
s64 Column;
|
||||
};
|
||||
|
||||
struct text_range
|
||||
{
|
||||
text_point Min;
|
||||
text_point Max;
|
||||
};
|
||||
|
||||
static text_point TextPointFromOffset(string String, s64 Offset);
|
||||
static s64 OffsetFromTextPoint(string String, text_point Point);
|
||||
|
||||
static text_range TextRange(text_point A, text_point B);
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: 1D Interval List & Array Types
|
||||
struct range1_s64_node
|
||||
{
|
||||
range1_s64 Range;
|
||||
range1_s64_node *Next;
|
||||
};
|
||||
|
||||
struct range1_s64_list
|
||||
{
|
||||
s64 Count;
|
||||
range1_s64_node *First;
|
||||
range1_s64_node *Last;
|
||||
};
|
||||
|
||||
struct range1_s64_array
|
||||
{
|
||||
s64 Count;
|
||||
range1_s64 *Ranges;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: 1D Interval List & Array Functions
|
||||
static void Range1S64ListPush(memory_arena *Arena, range1_s64_list *List, range1_s64 Range);
|
||||
static range1_s64_array Range1S64ArrayFromList(memory_arena *Arena, range1_s64_list *List);
|
||||
static s64 OffsetFromTextPoint(string Text, range1_s64_array Lines, text_point Point);
|
||||
|
||||
#endif //CORE_STRING_H
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
per_thread thread_context *ThreadLocal_ThreadContext = 0;
|
||||
|
||||
static thread_context AllocateThreadContext(void)
|
||||
{
|
||||
thread_context Context = {};
|
||||
for(s64 ArenaIndex = 0; ArenaIndex < ArrayCount(Context.Arenas); ArenaIndex += 1)
|
||||
{
|
||||
Context.Arenas[ArenaIndex] = ArenaAllocate(Gigabytes(1));
|
||||
}
|
||||
return(Context);
|
||||
}
|
||||
|
||||
static void SetThreadContext(thread_context *Context)
|
||||
{
|
||||
ThreadLocal_ThreadContext = Context;
|
||||
|
@ -25,7 +35,7 @@ static temporary_memory GetScratch(memory_arena **Conflicts, u64 ConflictCount)
|
|||
++ConflictIndex)
|
||||
{
|
||||
memory_arena *Conflict = Conflicts[ConflictIndex];
|
||||
if(Conflict == Context->Arenas + ArenaIndex)
|
||||
if(Conflict == Context->Arenas[ArenaIndex])
|
||||
{
|
||||
FoundConflict = true;
|
||||
break;
|
||||
|
@ -34,7 +44,7 @@ static temporary_memory GetScratch(memory_arena **Conflicts, u64 ConflictCount)
|
|||
|
||||
if(!FoundConflict)
|
||||
{
|
||||
Scratch = BeginTemporaryMemory(Context->Arenas + ArenaIndex);
|
||||
Scratch = BeginTemporaryMemory(Context->Arenas[ArenaIndex]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
struct thread_context
|
||||
{
|
||||
memory_arena Arenas[2];
|
||||
memory_arena *Arenas[2];
|
||||
};
|
||||
|
||||
//- sixten: Thread state management
|
||||
|
|
15
code/gen.cpp
|
@ -1,15 +0,0 @@
|
|||
#include "core/core.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define ArrayCount(Array) (sizeof(Array)/sizeof((Array)[0]))
|
||||
|
||||
#include "gen_opengl.cpp"
|
||||
#include "gen_ui.cpp"
|
||||
|
||||
int main(int ArgumentCount, char **Arguments)
|
||||
{
|
||||
GenOpenGL();
|
||||
GenUI();
|
||||
|
||||
return(0);
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
struct opengl_function
|
||||
{
|
||||
char *Name;
|
||||
char *Type;
|
||||
char *Arguments;
|
||||
};
|
||||
|
||||
struct opengl_function OpenGLFunctionEntries[] =
|
||||
{
|
||||
{"BindTexture", "void", "GLenum target, GLuint texture"},
|
||||
{"BlendFunc", "void", "GLenum sfactor, GLenum dfactor"},
|
||||
{"Clear", "void", "GLbitfield mask"},
|
||||
{"ClearAccum", "void", "GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha"},
|
||||
{"ClearColor", "void", "GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha"},
|
||||
{"ClearDepth", "void", "GLclampd depth"},
|
||||
{"ClearIndex", "void", "GLfloat c"},
|
||||
{"ClearStencil", "void", "GLint s"},
|
||||
{"ClipPlane", "void", "GLenum plane, const GLdouble *equation"},
|
||||
{"CullFace", "void", "GLenum mode"},
|
||||
{"DeleteTextures", "void", "GLsizei n, const GLuint *textures"},
|
||||
{"Disable", "void", "GLenum cap"},
|
||||
{"DrawArrays", "void", "GLenum mode, GLint first, GLsizei count"},
|
||||
{"DrawBuffer", "void", "GLenum mode"},
|
||||
{"DrawElements", "void", "GLenum mode, GLsizei count, GLenum type, const GLvoid *indices"},
|
||||
{"DrawArraysInstanced", "void", "GLenum mode, GLint first, GLsizei count, GLsizei instancecount"},
|
||||
{"Enable", "void", "GLenum cap"},
|
||||
{"GenTextures", "void", "GLsizei n, GLuint *textures"},
|
||||
{"GetClipPlane", "void", "GLenum plane, GLdouble *equation"},
|
||||
{"GetDoublev", "void", "GLenum pname, GLdouble *params"},
|
||||
{"GetError", "GLenum", "void"},
|
||||
{"GetFloatv", "void", "GLenum pname, GLfloat *params"},
|
||||
{"GetIntegerv", "void", "GLenum pname, GLint *params"},
|
||||
{"GetPointerv", "void", "GLenum pname, GLvoid* *params"},
|
||||
{"GetString", "const GLubyte *", "GLenum name"},
|
||||
{"GetTexEnvfv", "void", "GLenum target, GLenum pname, GLfloat *params"},
|
||||
{"GetTexEnviv", "void", "GLenum target, GLenum pname, GLint *params"},
|
||||
{"GetTexGendv", "void", "GLenum coord, GLenum pname, GLdouble *params"},
|
||||
{"GetTexGenfv", "void", "GLenum coord, GLenum pname, GLfloat *params"},
|
||||
{"GetTexGeniv", "void", "GLenum coord, GLenum pname, GLint *params"},
|
||||
{"GetTexImage", "void", "GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels"},
|
||||
{"GetTexLevelParameterfv", "void", "GLenum target, GLint level, GLenum pname, GLfloat *params"},
|
||||
{"GetTexLevelParameteriv", "void", "GLenum target, GLint level, GLenum pname, GLint *params"},
|
||||
{"GetTexParameterfv", "void", "GLenum target, GLenum pname, GLfloat *params"},
|
||||
{"GetTexParameteriv", "void", "GLenum target, GLenum pname, GLint *params"},
|
||||
{"Hint", "void", "GLenum target, GLenum mode"},
|
||||
{"IsTexture", "GLboolean", "GLuint texture"},
|
||||
{"LineWidth", "void", "GLfloat width"},
|
||||
{"ListBase", "void", "GLuint base"},
|
||||
{"LoadName", "void", "GLuint name"},
|
||||
{"LogicOp", "void", "GLenum opcode"},
|
||||
{"PointSize", "void", "GLfloat size"},
|
||||
{"PolygonMode", "void", "GLenum face, GLenum mode"},
|
||||
{"Scissor", "void", "GLint x, GLint y, GLsizei width, GLsizei height"},
|
||||
{"TexImage1D", "void", "GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels"},
|
||||
{"TexImage2D", "void", "GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels"},
|
||||
{"TexParameterf", "void", "GLenum target, GLenum pname, GLfloat param"},
|
||||
{"TexParameterfv", "void", "GLenum target, GLenum pname, const GLfloat *params"},
|
||||
{"TexParameteri", "void", "GLenum target, GLenum pname, GLint param"},
|
||||
{"TexParameteriv", "void", "GLenum target, GLenum pname, const GLint *params"},
|
||||
{"TexSubImage1D", "void", "GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels"},
|
||||
{"TexSubImage2D", "void", "GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels"},
|
||||
{"CompressedTexImage2D", "void", "GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *pixels"},
|
||||
{"ActiveTexture", "void", "GLenum texture"},
|
||||
{"Viewport", "void", "GLint x, GLint y, GLsizei width, GLsizei height"},
|
||||
{"GenBuffers", "void", "GLsizei n, GLuint *buffers"},
|
||||
{"BindBuffer", "void", "GLenum target, GLuint buffer"},
|
||||
{"BufferData", "void", "GLenum target, GLsizeiptr size, const void *data, GLenum usage"},
|
||||
{"BufferSubData", "void", "GLenum target, GLintptr offset, GLsizeiptr size, const void *data"},
|
||||
{"GenVertexArrays", "void", "GLsizei n, GLuint *arrays"},
|
||||
{"BindVertexArray", "void", "GLenum array"},
|
||||
{"GetAttribLocation", "GLint", "GLuint program, const GLchar *name"},
|
||||
{"EnableVertexAttribArray", "void", "GLuint index"},
|
||||
{"DisableVertexAttribArray", "void", "GLuint index"},
|
||||
{"VertexAttribPointer", "void", "GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer"},
|
||||
{"VertexAttribIPointer", "void", "GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer"},
|
||||
{"VertexAttribLPointer", "void", "GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer"},
|
||||
{"VertexAttribDivisor", "void", "GLuint index, GLuint divisor"},
|
||||
{"CreateShader", "GLuint", "GLenum type"},
|
||||
{"ShaderSource", "void", "GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length"},
|
||||
{"CompileShader", "void", "GLuint shader"},
|
||||
{"DeleteShader", "void", "GLuint shader"},
|
||||
{"GetShaderiv", "void", "GLuint shader, GLenum pname, GLint *params"},
|
||||
{"GetShaderInfoLog", "void", "GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog"},
|
||||
{"CreateProgram", "GLuint", "void"},
|
||||
{"UseProgram", "void", "GLuint program"},
|
||||
{"AttachShader", "void", "GLuint program, GLuint shader"},
|
||||
{"DeleteProgram", "void", "GLuint program"},
|
||||
{"LinkProgram", "void", "GLuint program"},
|
||||
{"GetProgramiv", "void", "GLuint program, GLenum pname, GLint *params"},
|
||||
{"GetProgramInfoLog", "void", "GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog"},
|
||||
{"GetUniformLocation", "GLint", "GLuint program, const GLchar *name"},
|
||||
{"Uniform1i", "void", "GLint location, GLint v0"},
|
||||
{"Uniform2f", "void", "GLint location, GLfloat v0, GLfloat v1"},
|
||||
{"Uniform3f", "void", "GLint location, GLfloat v0, GLfloat v1, GLfloat v2"},
|
||||
{"UniformMatrix4fv", "void", "GLint location, GLsizei count, GLboolean transpose, const GLfloat *value"},
|
||||
{"DebugMessageCallback", "void", "void (*)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar *, const void *), const void *userParam"},
|
||||
};
|
||||
|
||||
static void GenOpenGL(void)
|
||||
{
|
||||
FILE *Out = fopen("../code/generated/vn_opengl_functions.h", "wb");
|
||||
if(Out)
|
||||
{
|
||||
for(s32 Index = 0;
|
||||
Index < ArrayCount(OpenGLFunctionEntries);
|
||||
++Index)
|
||||
{
|
||||
struct opengl_function *Function = OpenGLFunctionEntries + Index;
|
||||
fprintf(Out, "typedef %s opengl_%s(%s);\n", Function->Type, Function->Name, Function->Arguments);
|
||||
}
|
||||
|
||||
fprintf(Out, "\n");
|
||||
|
||||
for(s32 Index = 0;
|
||||
Index < ArrayCount(OpenGLFunctionEntries);
|
||||
++Index)
|
||||
{
|
||||
struct opengl_function *Function = OpenGLFunctionEntries + Index;
|
||||
fprintf(Out, "global opengl_%s *gl%s = 0;\n", Function->Name, Function->Name);
|
||||
}
|
||||
|
||||
fprintf(Out, "\n");
|
||||
fprintf(Out, "extern void *OpenGL_LoadFunction(char *);\n");
|
||||
fprintf(Out, "\n");
|
||||
fprintf(Out, "static void OpenGL_LoadAllFunctions(void)\n");
|
||||
fprintf(Out, "{\n");
|
||||
|
||||
for(s32 Index = 0;
|
||||
Index < ArrayCount(OpenGLFunctionEntries);
|
||||
++Index)
|
||||
{
|
||||
struct opengl_function *Function = OpenGLFunctionEntries + Index;
|
||||
fprintf(Out, "\tgl%s = (opengl_%s *)OpenGL_LoadFunction(\"gl%s\");\n", Function->Name, Function->Name, Function->Name);
|
||||
}
|
||||
|
||||
fprintf(Out, "}\n");
|
||||
|
||||
fclose(Out);
|
||||
}
|
||||
}
|
129
code/gen_ui.cpp
|
@ -1,129 +0,0 @@
|
|||
struct ui_style_stack
|
||||
{
|
||||
char *Name;
|
||||
char *Type;
|
||||
char *MemberName;
|
||||
};
|
||||
|
||||
ui_style_stack UIStyleStacks[] =
|
||||
{
|
||||
{ "Parent", "ui_box *", "Parent" },
|
||||
{ "Width", "ui_size", "SemanticSize[Axis2_X]" },
|
||||
{ "Height", "ui_size", "SemanticSize[Axis2_Y]" },
|
||||
{ "FixedX", "r32", "FixedP.E[Axis2_X]" },
|
||||
{ "FixedY", "r32", "FixedP.E[Axis2_Y]" },
|
||||
{ "TextColor", "v4", "TextColor" },
|
||||
{ "BackgroundColor", "v4", "BackgroundColor" },
|
||||
{ "BorderColor", "v4", "BorderColor" },
|
||||
{ "BorderThickness", "r32", "BorderThickness" },
|
||||
{ "LayoutAxis", "axis2", "LayoutAxis" },
|
||||
{ "CornerRadius", "r32", "CornerRadius" },
|
||||
{ "Font", "font_id", "Font" },
|
||||
{ "FontSize", "r32", "FontSize" },
|
||||
{ "OffsetX", "r32", "Offset.x" },
|
||||
{ "OffsetY", "r32", "Offset.y" },
|
||||
};
|
||||
|
||||
static void GenUI(void)
|
||||
{
|
||||
FILE *Out = fopen("../code/generated/vn_generated_ui.h", "wb");
|
||||
if(Out)
|
||||
{
|
||||
fprintf(Out, "struct ui_style_stacks\n");
|
||||
fprintf(Out, "{\n");
|
||||
for(s32 Index = 0;
|
||||
Index < ArrayCount(UIStyleStacks);
|
||||
++Index)
|
||||
{
|
||||
ui_style_stack Stack = UIStyleStacks[Index];
|
||||
fprintf(Out, "\t%s %sStack[64];\n", Stack.Type, Stack.Name);
|
||||
fprintf(Out, "\ts32 %sStackUsed;\n", Stack.Name);
|
||||
fprintf(Out, "\tb32 AutoPop%s;\n", Stack.Name);
|
||||
}
|
||||
fprintf(Out, "};\n");
|
||||
fclose(Out);
|
||||
}
|
||||
|
||||
Out = fopen("../code/generated/vn_generated_ui.cpp", "wb");
|
||||
if(Out)
|
||||
{
|
||||
for(s32 Index = 0;
|
||||
Index < ArrayCount(UIStyleStacks);
|
||||
++Index)
|
||||
{
|
||||
ui_style_stack Stack = UIStyleStacks[Index];
|
||||
fprintf(Out, "inline void UI_Push%s(%s Element)\n", Stack.Name, Stack.Type);
|
||||
fprintf(Out, "{\n");
|
||||
fprintf(Out, "\tui *UI = UI_GetState();\n");
|
||||
fprintf(Out, "\tAssert(UI->Stacks.%sStackUsed + 1 < ArrayCount(UI->Stacks.%sStack));\n", Stack.Name, Stack.Name);
|
||||
fprintf(Out, "\tUI->Stacks.%sStack[UI->Stacks.%sStackUsed++] = Element;\n", Stack.Name, Stack.Name);
|
||||
fprintf(Out, "}\n");
|
||||
|
||||
fprintf(Out, "\n");
|
||||
|
||||
fprintf(Out, "inline void UI_Pop%s(void)\n", Stack.Name);
|
||||
fprintf(Out, "{\n");
|
||||
fprintf(Out, "\tui *UI = UI_GetState();\n");
|
||||
fprintf(Out, "\tAssert(UI->Stacks.%sStackUsed > 0);\n", Stack.Name);
|
||||
fprintf(Out, "\t--UI->Stacks.%sStackUsed;\n", Stack.Name);
|
||||
fprintf(Out, "}\n");
|
||||
|
||||
fprintf(Out, "\n");
|
||||
|
||||
fprintf(Out, "inline void UI_SetNext%s(%s Element)\n", Stack.Name, Stack.Type);
|
||||
fprintf(Out, "{\n");
|
||||
fprintf(Out, "\tui *UI = UI_GetState();\n");
|
||||
fprintf(Out, "\tUI_Push%s(Element);\n", Stack.Name);
|
||||
fprintf(Out, "\tUI->Stacks.AutoPop%s = true;\n", Stack.Name);
|
||||
fprintf(Out, "}\n");
|
||||
|
||||
fprintf(Out, "\n");
|
||||
|
||||
fprintf(Out, "inline %s UI_First%s(void)\n", Stack.Type, Stack.Name);
|
||||
fprintf(Out, "{\n");
|
||||
fprintf(Out, "\tui *UI = UI_GetState();\n");
|
||||
fprintf(Out, "\treturn(UI->Stacks.%sStack[0]);\n", Stack.Name);
|
||||
fprintf(Out, "}\n");
|
||||
|
||||
fprintf(Out, "\n");
|
||||
|
||||
fprintf(Out, "inline %s UI_Top%s(void)\n", Stack.Type, Stack.Name);
|
||||
fprintf(Out, "{\n");
|
||||
fprintf(Out, "\tui *UI = UI_GetState();\n");
|
||||
fprintf(Out, "\treturn(UI->Stacks.%sStack[UI->Stacks.%sStackUsed - 1]);\n", Stack.Name, Stack.Name);
|
||||
fprintf(Out, "}\n");
|
||||
|
||||
fprintf(Out, "\n");
|
||||
|
||||
fprintf(Out, "#define UI_%s(Element) DeferLoop(UI_Push%s(Element), UI_Pop%s())\n",
|
||||
Stack.Name, Stack.Name, Stack.Name);
|
||||
|
||||
fprintf(Out, "\n");
|
||||
}
|
||||
|
||||
fprintf(Out, "\n");
|
||||
|
||||
fprintf(Out, "inline void UI_ApplyStyles(ui_box *Box)\n");
|
||||
fprintf(Out, "{\n");
|
||||
fprintf(Out, "\tui *UI = UI_GetState();\n");
|
||||
for(s32 Index = 0;
|
||||
Index < ArrayCount(UIStyleStacks);
|
||||
++Index)
|
||||
{
|
||||
ui_style_stack Stack = UIStyleStacks[Index];
|
||||
fprintf(Out, "\tAssert(UI->Stacks.%sStackUsed > 0);\n", Stack.Name);
|
||||
fprintf(Out, "\tBox->%s = UI->Stacks.%sStack[UI->Stacks.%sStackUsed - 1];\n",
|
||||
Stack.MemberName, Stack.Name, Stack.Name);
|
||||
fprintf(Out, "\tif(UI->Stacks.AutoPop%s)\n", Stack.Name);
|
||||
fprintf(Out, "\t{\n");
|
||||
fprintf(Out, "\t\tUI_Pop%s();\n", Stack.Name);
|
||||
fprintf(Out, "\t\tUI->Stacks.AutoPop%s = false;\n", Stack.Name);
|
||||
fprintf(Out, "\t}\n\n");
|
||||
}
|
||||
|
||||
fprintf(Out, "}\n");
|
||||
|
||||
|
||||
fclose(Out);
|
||||
}
|
||||
}
|
|
@ -1,650 +0,0 @@
|
|||
inline void UI_PushParent(ui_box * Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.ParentStackUsed + 1 < ArrayCount(UI->Stacks.ParentStack));
|
||||
UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopParent(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.ParentStackUsed > 0);
|
||||
--UI->Stacks.ParentStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextParent(ui_box * Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushParent(Element);
|
||||
UI->Stacks.AutoPopParent = true;
|
||||
}
|
||||
|
||||
inline ui_box * UI_FirstParent(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.ParentStack[0]);
|
||||
}
|
||||
|
||||
inline ui_box * UI_TopParent(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_Parent(Element) DeferLoop(UI_PushParent(Element), UI_PopParent())
|
||||
|
||||
inline void UI_PushWidth(ui_size Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.WidthStackUsed + 1 < ArrayCount(UI->Stacks.WidthStack));
|
||||
UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopWidth(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.WidthStackUsed > 0);
|
||||
--UI->Stacks.WidthStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextWidth(ui_size Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushWidth(Element);
|
||||
UI->Stacks.AutoPopWidth = true;
|
||||
}
|
||||
|
||||
inline ui_size UI_FirstWidth(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.WidthStack[0]);
|
||||
}
|
||||
|
||||
inline ui_size UI_TopWidth(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_Width(Element) DeferLoop(UI_PushWidth(Element), UI_PopWidth())
|
||||
|
||||
inline void UI_PushHeight(ui_size Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.HeightStackUsed + 1 < ArrayCount(UI->Stacks.HeightStack));
|
||||
UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopHeight(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.HeightStackUsed > 0);
|
||||
--UI->Stacks.HeightStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextHeight(ui_size Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushHeight(Element);
|
||||
UI->Stacks.AutoPopHeight = true;
|
||||
}
|
||||
|
||||
inline ui_size UI_FirstHeight(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.HeightStack[0]);
|
||||
}
|
||||
|
||||
inline ui_size UI_TopHeight(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_Height(Element) DeferLoop(UI_PushHeight(Element), UI_PopHeight())
|
||||
|
||||
inline void UI_PushFixedX(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.FixedXStackUsed + 1 < ArrayCount(UI->Stacks.FixedXStack));
|
||||
UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopFixedX(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.FixedXStackUsed > 0);
|
||||
--UI->Stacks.FixedXStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextFixedX(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushFixedX(Element);
|
||||
UI->Stacks.AutoPopFixedX = true;
|
||||
}
|
||||
|
||||
inline r32 UI_FirstFixedX(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.FixedXStack[0]);
|
||||
}
|
||||
|
||||
inline r32 UI_TopFixedX(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_FixedX(Element) DeferLoop(UI_PushFixedX(Element), UI_PopFixedX())
|
||||
|
||||
inline void UI_PushFixedY(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.FixedYStackUsed + 1 < ArrayCount(UI->Stacks.FixedYStack));
|
||||
UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopFixedY(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.FixedYStackUsed > 0);
|
||||
--UI->Stacks.FixedYStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextFixedY(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushFixedY(Element);
|
||||
UI->Stacks.AutoPopFixedY = true;
|
||||
}
|
||||
|
||||
inline r32 UI_FirstFixedY(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.FixedYStack[0]);
|
||||
}
|
||||
|
||||
inline r32 UI_TopFixedY(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_FixedY(Element) DeferLoop(UI_PushFixedY(Element), UI_PopFixedY())
|
||||
|
||||
inline void UI_PushTextColor(v4 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.TextColorStackUsed + 1 < ArrayCount(UI->Stacks.TextColorStack));
|
||||
UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopTextColor(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.TextColorStackUsed > 0);
|
||||
--UI->Stacks.TextColorStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextTextColor(v4 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushTextColor(Element);
|
||||
UI->Stacks.AutoPopTextColor = true;
|
||||
}
|
||||
|
||||
inline v4 UI_FirstTextColor(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.TextColorStack[0]);
|
||||
}
|
||||
|
||||
inline v4 UI_TopTextColor(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_TextColor(Element) DeferLoop(UI_PushTextColor(Element), UI_PopTextColor())
|
||||
|
||||
inline void UI_PushBackgroundColor(v4 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.BackgroundColorStackUsed + 1 < ArrayCount(UI->Stacks.BackgroundColorStack));
|
||||
UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopBackgroundColor(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.BackgroundColorStackUsed > 0);
|
||||
--UI->Stacks.BackgroundColorStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextBackgroundColor(v4 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushBackgroundColor(Element);
|
||||
UI->Stacks.AutoPopBackgroundColor = true;
|
||||
}
|
||||
|
||||
inline v4 UI_FirstBackgroundColor(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.BackgroundColorStack[0]);
|
||||
}
|
||||
|
||||
inline v4 UI_TopBackgroundColor(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_BackgroundColor(Element) DeferLoop(UI_PushBackgroundColor(Element), UI_PopBackgroundColor())
|
||||
|
||||
inline void UI_PushBorderColor(v4 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.BorderColorStackUsed + 1 < ArrayCount(UI->Stacks.BorderColorStack));
|
||||
UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopBorderColor(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.BorderColorStackUsed > 0);
|
||||
--UI->Stacks.BorderColorStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextBorderColor(v4 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushBorderColor(Element);
|
||||
UI->Stacks.AutoPopBorderColor = true;
|
||||
}
|
||||
|
||||
inline v4 UI_FirstBorderColor(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.BorderColorStack[0]);
|
||||
}
|
||||
|
||||
inline v4 UI_TopBorderColor(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_BorderColor(Element) DeferLoop(UI_PushBorderColor(Element), UI_PopBorderColor())
|
||||
|
||||
inline void UI_PushBorderThickness(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.BorderThicknessStackUsed + 1 < ArrayCount(UI->Stacks.BorderThicknessStack));
|
||||
UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopBorderThickness(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.BorderThicknessStackUsed > 0);
|
||||
--UI->Stacks.BorderThicknessStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextBorderThickness(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushBorderThickness(Element);
|
||||
UI->Stacks.AutoPopBorderThickness = true;
|
||||
}
|
||||
|
||||
inline r32 UI_FirstBorderThickness(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.BorderThicknessStack[0]);
|
||||
}
|
||||
|
||||
inline r32 UI_TopBorderThickness(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_BorderThickness(Element) DeferLoop(UI_PushBorderThickness(Element), UI_PopBorderThickness())
|
||||
|
||||
inline void UI_PushLayoutAxis(axis2 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.LayoutAxisStackUsed + 1 < ArrayCount(UI->Stacks.LayoutAxisStack));
|
||||
UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopLayoutAxis(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.LayoutAxisStackUsed > 0);
|
||||
--UI->Stacks.LayoutAxisStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextLayoutAxis(axis2 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushLayoutAxis(Element);
|
||||
UI->Stacks.AutoPopLayoutAxis = true;
|
||||
}
|
||||
|
||||
inline axis2 UI_FirstLayoutAxis(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.LayoutAxisStack[0]);
|
||||
}
|
||||
|
||||
inline axis2 UI_TopLayoutAxis(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_LayoutAxis(Element) DeferLoop(UI_PushLayoutAxis(Element), UI_PopLayoutAxis())
|
||||
|
||||
inline void UI_PushCornerRadius(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.CornerRadiusStackUsed + 1 < ArrayCount(UI->Stacks.CornerRadiusStack));
|
||||
UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopCornerRadius(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.CornerRadiusStackUsed > 0);
|
||||
--UI->Stacks.CornerRadiusStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextCornerRadius(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushCornerRadius(Element);
|
||||
UI->Stacks.AutoPopCornerRadius = true;
|
||||
}
|
||||
|
||||
inline r32 UI_FirstCornerRadius(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.CornerRadiusStack[0]);
|
||||
}
|
||||
|
||||
inline r32 UI_TopCornerRadius(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_CornerRadius(Element) DeferLoop(UI_PushCornerRadius(Element), UI_PopCornerRadius())
|
||||
|
||||
inline void UI_PushFont(font_id Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.FontStackUsed + 1 < ArrayCount(UI->Stacks.FontStack));
|
||||
UI->Stacks.FontStack[UI->Stacks.FontStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopFont(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.FontStackUsed > 0);
|
||||
--UI->Stacks.FontStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextFont(font_id Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushFont(Element);
|
||||
UI->Stacks.AutoPopFont = true;
|
||||
}
|
||||
|
||||
inline font_id UI_FirstFont(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.FontStack[0]);
|
||||
}
|
||||
|
||||
inline font_id UI_TopFont(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.FontStack[UI->Stacks.FontStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_Font(Element) DeferLoop(UI_PushFont(Element), UI_PopFont())
|
||||
|
||||
inline void UI_PushFontSize(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.FontSizeStackUsed + 1 < ArrayCount(UI->Stacks.FontSizeStack));
|
||||
UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopFontSize(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.FontSizeStackUsed > 0);
|
||||
--UI->Stacks.FontSizeStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextFontSize(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushFontSize(Element);
|
||||
UI->Stacks.AutoPopFontSize = true;
|
||||
}
|
||||
|
||||
inline r32 UI_FirstFontSize(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.FontSizeStack[0]);
|
||||
}
|
||||
|
||||
inline r32 UI_TopFontSize(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_FontSize(Element) DeferLoop(UI_PushFontSize(Element), UI_PopFontSize())
|
||||
|
||||
inline void UI_PushOffsetX(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.OffsetXStackUsed + 1 < ArrayCount(UI->Stacks.OffsetXStack));
|
||||
UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopOffsetX(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.OffsetXStackUsed > 0);
|
||||
--UI->Stacks.OffsetXStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextOffsetX(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushOffsetX(Element);
|
||||
UI->Stacks.AutoPopOffsetX = true;
|
||||
}
|
||||
|
||||
inline r32 UI_FirstOffsetX(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.OffsetXStack[0]);
|
||||
}
|
||||
|
||||
inline r32 UI_TopOffsetX(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_OffsetX(Element) DeferLoop(UI_PushOffsetX(Element), UI_PopOffsetX())
|
||||
|
||||
inline void UI_PushOffsetY(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.OffsetYStackUsed + 1 < ArrayCount(UI->Stacks.OffsetYStack));
|
||||
UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed++] = Element;
|
||||
}
|
||||
|
||||
inline void UI_PopOffsetY(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.OffsetYStackUsed > 0);
|
||||
--UI->Stacks.OffsetYStackUsed;
|
||||
}
|
||||
|
||||
inline void UI_SetNextOffsetY(r32 Element)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
UI_PushOffsetY(Element);
|
||||
UI->Stacks.AutoPopOffsetY = true;
|
||||
}
|
||||
|
||||
inline r32 UI_FirstOffsetY(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.OffsetYStack[0]);
|
||||
}
|
||||
|
||||
inline r32 UI_TopOffsetY(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1]);
|
||||
}
|
||||
|
||||
#define UI_OffsetY(Element) DeferLoop(UI_PushOffsetY(Element), UI_PopOffsetY())
|
||||
|
||||
|
||||
inline void UI_ApplyStyles(ui_box *Box)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.ParentStackUsed > 0);
|
||||
Box->Parent = UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopParent)
|
||||
{
|
||||
UI_PopParent();
|
||||
UI->Stacks.AutoPopParent = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.WidthStackUsed > 0);
|
||||
Box->SemanticSize[Axis2_X] = UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopWidth)
|
||||
{
|
||||
UI_PopWidth();
|
||||
UI->Stacks.AutoPopWidth = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.HeightStackUsed > 0);
|
||||
Box->SemanticSize[Axis2_Y] = UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopHeight)
|
||||
{
|
||||
UI_PopHeight();
|
||||
UI->Stacks.AutoPopHeight = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.FixedXStackUsed > 0);
|
||||
Box->FixedP.E[Axis2_X] = UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopFixedX)
|
||||
{
|
||||
UI_PopFixedX();
|
||||
UI->Stacks.AutoPopFixedX = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.FixedYStackUsed > 0);
|
||||
Box->FixedP.E[Axis2_Y] = UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopFixedY)
|
||||
{
|
||||
UI_PopFixedY();
|
||||
UI->Stacks.AutoPopFixedY = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.TextColorStackUsed > 0);
|
||||
Box->TextColor = UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopTextColor)
|
||||
{
|
||||
UI_PopTextColor();
|
||||
UI->Stacks.AutoPopTextColor = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.BackgroundColorStackUsed > 0);
|
||||
Box->BackgroundColor = UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopBackgroundColor)
|
||||
{
|
||||
UI_PopBackgroundColor();
|
||||
UI->Stacks.AutoPopBackgroundColor = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.BorderColorStackUsed > 0);
|
||||
Box->BorderColor = UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopBorderColor)
|
||||
{
|
||||
UI_PopBorderColor();
|
||||
UI->Stacks.AutoPopBorderColor = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.BorderThicknessStackUsed > 0);
|
||||
Box->BorderThickness = UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopBorderThickness)
|
||||
{
|
||||
UI_PopBorderThickness();
|
||||
UI->Stacks.AutoPopBorderThickness = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.LayoutAxisStackUsed > 0);
|
||||
Box->LayoutAxis = UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopLayoutAxis)
|
||||
{
|
||||
UI_PopLayoutAxis();
|
||||
UI->Stacks.AutoPopLayoutAxis = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.CornerRadiusStackUsed > 0);
|
||||
Box->CornerRadius = UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopCornerRadius)
|
||||
{
|
||||
UI_PopCornerRadius();
|
||||
UI->Stacks.AutoPopCornerRadius = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.FontStackUsed > 0);
|
||||
Box->Font = UI->Stacks.FontStack[UI->Stacks.FontStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopFont)
|
||||
{
|
||||
UI_PopFont();
|
||||
UI->Stacks.AutoPopFont = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.FontSizeStackUsed > 0);
|
||||
Box->FontSize = UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopFontSize)
|
||||
{
|
||||
UI_PopFontSize();
|
||||
UI->Stacks.AutoPopFontSize = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.OffsetXStackUsed > 0);
|
||||
Box->Offset.x = UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopOffsetX)
|
||||
{
|
||||
UI_PopOffsetX();
|
||||
UI->Stacks.AutoPopOffsetX = false;
|
||||
}
|
||||
|
||||
Assert(UI->Stacks.OffsetYStackUsed > 0);
|
||||
Box->Offset.y = UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1];
|
||||
if(UI->Stacks.AutoPopOffsetY)
|
||||
{
|
||||
UI_PopOffsetY();
|
||||
UI->Stacks.AutoPopOffsetY = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
struct ui_style_stacks
|
||||
{
|
||||
ui_box * ParentStack[64];
|
||||
s32 ParentStackUsed;
|
||||
b32 AutoPopParent;
|
||||
ui_size WidthStack[64];
|
||||
s32 WidthStackUsed;
|
||||
b32 AutoPopWidth;
|
||||
ui_size HeightStack[64];
|
||||
s32 HeightStackUsed;
|
||||
b32 AutoPopHeight;
|
||||
r32 FixedXStack[64];
|
||||
s32 FixedXStackUsed;
|
||||
b32 AutoPopFixedX;
|
||||
r32 FixedYStack[64];
|
||||
s32 FixedYStackUsed;
|
||||
b32 AutoPopFixedY;
|
||||
v4 TextColorStack[64];
|
||||
s32 TextColorStackUsed;
|
||||
b32 AutoPopTextColor;
|
||||
v4 BackgroundColorStack[64];
|
||||
s32 BackgroundColorStackUsed;
|
||||
b32 AutoPopBackgroundColor;
|
||||
v4 BorderColorStack[64];
|
||||
s32 BorderColorStackUsed;
|
||||
b32 AutoPopBorderColor;
|
||||
r32 BorderThicknessStack[64];
|
||||
s32 BorderThicknessStackUsed;
|
||||
b32 AutoPopBorderThickness;
|
||||
axis2 LayoutAxisStack[64];
|
||||
s32 LayoutAxisStackUsed;
|
||||
b32 AutoPopLayoutAxis;
|
||||
r32 CornerRadiusStack[64];
|
||||
s32 CornerRadiusStackUsed;
|
||||
b32 AutoPopCornerRadius;
|
||||
font_id FontStack[64];
|
||||
s32 FontStackUsed;
|
||||
b32 AutoPopFont;
|
||||
r32 FontSizeStack[64];
|
||||
s32 FontSizeStackUsed;
|
||||
b32 AutoPopFontSize;
|
||||
r32 OffsetXStack[64];
|
||||
s32 OffsetXStackUsed;
|
||||
b32 AutoPopOffsetX;
|
||||
r32 OffsetYStack[64];
|
||||
s32 OffsetYStackUsed;
|
||||
b32 AutoPopOffsetY;
|
||||
};
|
|
@ -175,7 +175,6 @@ global opengl_UniformMatrix4fv *glUniformMatrix4fv = 0;
|
|||
global opengl_DebugMessageCallback *glDebugMessageCallback = 0;
|
||||
|
||||
extern void *OpenGL_LoadFunction(char *);
|
||||
|
||||
static void OpenGL_LoadAllFunctions(void)
|
||||
{
|
||||
glBindTexture = (opengl_BindTexture *)OpenGL_LoadFunction("glBindTexture");
|
||||
|
@ -266,3 +265,4 @@ static void OpenGL_LoadAllFunctions(void)
|
|||
glUniformMatrix4fv = (opengl_UniformMatrix4fv *)OpenGL_LoadFunction("glUniformMatrix4fv");
|
||||
glDebugMessageCallback = (opengl_DebugMessageCallback *)OpenGL_LoadFunction("glDebugMessageCallback");
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
#define PLATFORM_RESERVE(name) void * name(u64 Size)
|
||||
#define PLATFORM_RELEASE(name) void name(void *Pointer)
|
||||
#define PLATFORM_COMMIT(name) void name(void *Pointer, u64 Size)
|
||||
#define PLATFORM_DECOMMIT(name) void name(void *Pointer, u64 Size)
|
||||
#define PLATFORM_OPEN_FILE(name) platform_file_handle name(string Path, platform_access_flags FileAccess)
|
||||
#define PLATFORM_CLOSE_FILE(name) void name(platform_file_handle Handle)
|
||||
#define PLATFORM_READ_FILE(name) void name(platform_file_handle Handle, void *Dest, u64 Offset, u64 Size)
|
||||
#define PLATFORM_WRITE_FILE(name) void name(platform_file_handle Handle, void *Source, u64 Offset, u64 Size)
|
||||
#define PLATFORM_GET_FILE_SIZE(name) u64 name(platform_file_handle Handle)
|
||||
#define PLATFORM_SET_CURSOR(name) void name(platform_cursor Cursor)
|
||||
#define PLATFORM_TOGGLE_FULLSCREEN(name) void name(void)
|
||||
#define PLATFORM_SHOW_MESSAGE(name) void name(string Message, platform_message_type Type)
|
||||
|
||||
typedef PLATFORM_RESERVE(platform_reserve);
|
||||
typedef PLATFORM_RELEASE(platform_release);
|
||||
typedef PLATFORM_COMMIT(platform_commit);
|
||||
typedef PLATFORM_DECOMMIT(platform_decommit);
|
||||
typedef PLATFORM_OPEN_FILE(platform_open_file);
|
||||
typedef PLATFORM_CLOSE_FILE(platform_close_file);
|
||||
typedef PLATFORM_READ_FILE(platform_read_file);
|
||||
typedef PLATFORM_WRITE_FILE(platform_write_file);
|
||||
typedef PLATFORM_GET_FILE_SIZE(platform_get_file_size);
|
||||
typedef PLATFORM_SET_CURSOR(platform_set_cursor);
|
||||
typedef PLATFORM_TOGGLE_FULLSCREEN(platform_toggle_fullscreen);
|
||||
typedef PLATFORM_SHOW_MESSAGE(platform_show_message);
|
||||
|
||||
struct platform_api
|
||||
{
|
||||
platform_reserve *Reserve;
|
||||
platform_release *Release;
|
||||
platform_commit *Commit;
|
||||
platform_decommit *Decommit;
|
||||
platform_open_file *OpenFile;
|
||||
platform_close_file *CloseFile;
|
||||
platform_read_file *ReadFile;
|
||||
platform_write_file *WriteFile;
|
||||
platform_get_file_size *GetFileSize;
|
||||
platform_set_cursor *SetCursor;
|
||||
platform_toggle_fullscreen *ToggleFullscreen;
|
||||
platform_show_message *ShowMessage;
|
||||
};
|
||||
|
||||
#define RegisterPlatformFunctions(PlatformName)\
|
||||
Platform.Reserve = PlatformName##_Reserve;\
|
||||
Platform.Release = PlatformName##_Release;\
|
||||
Platform.Commit = PlatformName##_Commit;\
|
||||
Platform.Decommit = PlatformName##_Decommit;\
|
||||
Platform.OpenFile = PlatformName##_OpenFile;\
|
||||
Platform.CloseFile = PlatformName##_CloseFile;\
|
||||
Platform.ReadFile = PlatformName##_ReadFile;\
|
||||
Platform.WriteFile = PlatformName##_WriteFile;\
|
||||
Platform.GetFileSize = PlatformName##_GetFileSize;\
|
||||
Platform.SetCursor = PlatformName##_SetCursor;\
|
||||
Platform.ToggleFullscreen = PlatformName##_ToggleFullscreen;\
|
||||
Platform.ShowMessage = PlatformName##_ShowMessage;\
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
enum scene_ast_node_type
|
||||
{
|
||||
S_AstNode_Invalid,
|
||||
S_AstNode_BlockStatement,
|
||||
S_AstNode_SceneDecl,
|
||||
S_AstNode_Count,
|
||||
};
|
||||
|
||||
struct scene_ast_node;
|
||||
|
||||
struct scene_ast_node_invalid {};
|
||||
struct scene_ast_node_block_statement {scene_ast_node *First; scene_ast_node *Last;};
|
||||
struct scene_ast_node_scene_declaration {};
|
||||
|
||||
struct scene_ast_node
|
||||
{
|
||||
scene_ast_node_type Type;
|
||||
union
|
||||
{
|
||||
scene_ast_node_invalid InvalidData;
|
||||
scene_ast_node_block_statement BlockStatementData;
|
||||
scene_ast_node_scene_declaration SceneDeclData;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
inline void UI_PushParent(ui_box * Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.ParentStackUsed + 1 < ArrayCount(UI->Stacks.ParentStack)); UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed++] = Element; }
|
||||
inline void UI_PushWidth(ui_size Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.WidthStackUsed + 1 < ArrayCount(UI->Stacks.WidthStack)); UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed++] = Element; }
|
||||
inline void UI_PushHeight(ui_size Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.HeightStackUsed + 1 < ArrayCount(UI->Stacks.HeightStack)); UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed++] = Element; }
|
||||
inline void UI_PushFixedX(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.FixedXStackUsed + 1 < ArrayCount(UI->Stacks.FixedXStack)); UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed++] = Element; }
|
||||
inline void UI_PushFixedY(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.FixedYStackUsed + 1 < ArrayCount(UI->Stacks.FixedYStack)); UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed++] = Element; }
|
||||
inline void UI_PushTextColor(v4 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.TextColorStackUsed + 1 < ArrayCount(UI->Stacks.TextColorStack)); UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed++] = Element; }
|
||||
inline void UI_PushBackgroundColor(v4 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.BackgroundColorStackUsed + 1 < ArrayCount(UI->Stacks.BackgroundColorStack)); UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed++] = Element; }
|
||||
inline void UI_PushBorderColor(v4 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.BorderColorStackUsed + 1 < ArrayCount(UI->Stacks.BorderColorStack)); UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed++] = Element; }
|
||||
inline void UI_PushBorderThickness(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.BorderThicknessStackUsed + 1 < ArrayCount(UI->Stacks.BorderThicknessStack)); UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed++] = Element; }
|
||||
inline void UI_PushLayoutAxis(axis2 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.LayoutAxisStackUsed + 1 < ArrayCount(UI->Stacks.LayoutAxisStack)); UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed++] = Element; }
|
||||
inline void UI_PushCornerRadius(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.CornerRadiusStackUsed + 1 < ArrayCount(UI->Stacks.CornerRadiusStack)); UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed++] = Element; }
|
||||
inline void UI_PushFont(font_id Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontStackUsed + 1 < ArrayCount(UI->Stacks.FontStack)); UI->Stacks.FontStack[UI->Stacks.FontStackUsed++] = Element; }
|
||||
inline void UI_PushFontSize(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontSizeStackUsed + 1 < ArrayCount(UI->Stacks.FontSizeStack)); UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed++] = Element; }
|
||||
inline void UI_PushOffsetX(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetXStackUsed + 1 < ArrayCount(UI->Stacks.OffsetXStack)); UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed++] = Element; }
|
||||
inline void UI_PushOffsetY(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetYStackUsed + 1 < ArrayCount(UI->Stacks.OffsetYStack)); UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed++] = Element; }
|
||||
inline void UI_PopParent(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.ParentStackUsed > 0); --UI->Stacks.ParentStackUsed; }
|
||||
inline void UI_PopWidth(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.WidthStackUsed > 0); --UI->Stacks.WidthStackUsed; }
|
||||
inline void UI_PopHeight(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.HeightStackUsed > 0); --UI->Stacks.HeightStackUsed; }
|
||||
inline void UI_PopFixedX(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.FixedXStackUsed > 0); --UI->Stacks.FixedXStackUsed; }
|
||||
inline void UI_PopFixedY(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.FixedYStackUsed > 0); --UI->Stacks.FixedYStackUsed; }
|
||||
inline void UI_PopTextColor(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.TextColorStackUsed > 0); --UI->Stacks.TextColorStackUsed; }
|
||||
inline void UI_PopBackgroundColor(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.BackgroundColorStackUsed > 0); --UI->Stacks.BackgroundColorStackUsed; }
|
||||
inline void UI_PopBorderColor(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.BorderColorStackUsed > 0); --UI->Stacks.BorderColorStackUsed; }
|
||||
inline void UI_PopBorderThickness(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.BorderThicknessStackUsed > 0); --UI->Stacks.BorderThicknessStackUsed; }
|
||||
inline void UI_PopLayoutAxis(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.LayoutAxisStackUsed > 0); --UI->Stacks.LayoutAxisStackUsed; }
|
||||
inline void UI_PopCornerRadius(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.CornerRadiusStackUsed > 0); --UI->Stacks.CornerRadiusStackUsed; }
|
||||
inline void UI_PopFont(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontStackUsed > 0); --UI->Stacks.FontStackUsed; }
|
||||
inline void UI_PopFontSize(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontSizeStackUsed > 0); --UI->Stacks.FontSizeStackUsed; }
|
||||
inline void UI_PopOffsetX(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetXStackUsed > 0); --UI->Stacks.OffsetXStackUsed; }
|
||||
inline void UI_PopOffsetY(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetYStackUsed > 0); --UI->Stacks.OffsetYStackUsed; }
|
||||
inline void UI_SetNextParent(ui_box * Element) { ui *UI = UI_GetState(); UI_PushParent(Element); UI->Stacks.AutoPopParent = true; }
|
||||
inline void UI_SetNextWidth(ui_size Element) { ui *UI = UI_GetState(); UI_PushWidth(Element); UI->Stacks.AutoPopWidth = true; }
|
||||
inline void UI_SetNextHeight(ui_size Element) { ui *UI = UI_GetState(); UI_PushHeight(Element); UI->Stacks.AutoPopHeight = true; }
|
||||
inline void UI_SetNextFixedX(r32 Element) { ui *UI = UI_GetState(); UI_PushFixedX(Element); UI->Stacks.AutoPopFixedX = true; }
|
||||
inline void UI_SetNextFixedY(r32 Element) { ui *UI = UI_GetState(); UI_PushFixedY(Element); UI->Stacks.AutoPopFixedY = true; }
|
||||
inline void UI_SetNextTextColor(v4 Element) { ui *UI = UI_GetState(); UI_PushTextColor(Element); UI->Stacks.AutoPopTextColor = true; }
|
||||
inline void UI_SetNextBackgroundColor(v4 Element) { ui *UI = UI_GetState(); UI_PushBackgroundColor(Element); UI->Stacks.AutoPopBackgroundColor = true; }
|
||||
inline void UI_SetNextBorderColor(v4 Element) { ui *UI = UI_GetState(); UI_PushBorderColor(Element); UI->Stacks.AutoPopBorderColor = true; }
|
||||
inline void UI_SetNextBorderThickness(r32 Element) { ui *UI = UI_GetState(); UI_PushBorderThickness(Element); UI->Stacks.AutoPopBorderThickness = true; }
|
||||
inline void UI_SetNextLayoutAxis(axis2 Element) { ui *UI = UI_GetState(); UI_PushLayoutAxis(Element); UI->Stacks.AutoPopLayoutAxis = true; }
|
||||
inline void UI_SetNextCornerRadius(r32 Element) { ui *UI = UI_GetState(); UI_PushCornerRadius(Element); UI->Stacks.AutoPopCornerRadius = true; }
|
||||
inline void UI_SetNextFont(font_id Element) { ui *UI = UI_GetState(); UI_PushFont(Element); UI->Stacks.AutoPopFont = true; }
|
||||
inline void UI_SetNextFontSize(r32 Element) { ui *UI = UI_GetState(); UI_PushFontSize(Element); UI->Stacks.AutoPopFontSize = true; }
|
||||
inline void UI_SetNextOffsetX(r32 Element) { ui *UI = UI_GetState(); UI_PushOffsetX(Element); UI->Stacks.AutoPopOffsetX = true; }
|
||||
inline void UI_SetNextOffsetY(r32 Element) { ui *UI = UI_GetState(); UI_PushOffsetY(Element); UI->Stacks.AutoPopOffsetY = true; }
|
||||
inline ui_box * UI_FirstParent(void) { ui *UI = UI_GetState(); return(UI->Stacks.ParentStack[0]); }
|
||||
inline ui_size UI_FirstWidth(void) { ui *UI = UI_GetState(); return(UI->Stacks.WidthStack[0]); }
|
||||
inline ui_size UI_FirstHeight(void) { ui *UI = UI_GetState(); return(UI->Stacks.HeightStack[0]); }
|
||||
inline r32 UI_FirstFixedX(void) { ui *UI = UI_GetState(); return(UI->Stacks.FixedXStack[0]); }
|
||||
inline r32 UI_FirstFixedY(void) { ui *UI = UI_GetState(); return(UI->Stacks.FixedYStack[0]); }
|
||||
inline v4 UI_FirstTextColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.TextColorStack[0]); }
|
||||
inline v4 UI_FirstBackgroundColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.BackgroundColorStack[0]); }
|
||||
inline v4 UI_FirstBorderColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.BorderColorStack[0]); }
|
||||
inline r32 UI_FirstBorderThickness(void) { ui *UI = UI_GetState(); return(UI->Stacks.BorderThicknessStack[0]); }
|
||||
inline axis2 UI_FirstLayoutAxis(void) { ui *UI = UI_GetState(); return(UI->Stacks.LayoutAxisStack[0]); }
|
||||
inline r32 UI_FirstCornerRadius(void) { ui *UI = UI_GetState(); return(UI->Stacks.CornerRadiusStack[0]); }
|
||||
inline font_id UI_FirstFont(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontStack[0]); }
|
||||
inline r32 UI_FirstFontSize(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontSizeStack[0]); }
|
||||
inline r32 UI_FirstOffsetX(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetXStack[0]); }
|
||||
inline r32 UI_FirstOffsetY(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetYStack[0]); }
|
||||
inline ui_box * UI_TopParent(void) { ui *UI = UI_GetState(); return(UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed - 1]); }
|
||||
inline ui_size UI_TopWidth(void) { ui *UI = UI_GetState(); return(UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed - 1]); }
|
||||
inline ui_size UI_TopHeight(void) { ui *UI = UI_GetState(); return(UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed - 1]); }
|
||||
inline r32 UI_TopFixedX(void) { ui *UI = UI_GetState(); return(UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed - 1]); }
|
||||
inline r32 UI_TopFixedY(void) { ui *UI = UI_GetState(); return(UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed - 1]); }
|
||||
inline v4 UI_TopTextColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed - 1]); }
|
||||
inline v4 UI_TopBackgroundColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed - 1]); }
|
||||
inline v4 UI_TopBorderColor(void) { ui *UI = UI_GetState(); return(UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed - 1]); }
|
||||
inline r32 UI_TopBorderThickness(void) { ui *UI = UI_GetState(); return(UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed - 1]); }
|
||||
inline axis2 UI_TopLayoutAxis(void) { ui *UI = UI_GetState(); return(UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed - 1]); }
|
||||
inline r32 UI_TopCornerRadius(void) { ui *UI = UI_GetState(); return(UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed - 1]); }
|
||||
inline font_id UI_TopFont(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontStack[UI->Stacks.FontStackUsed - 1]); }
|
||||
inline r32 UI_TopFontSize(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1]); }
|
||||
inline r32 UI_TopOffsetX(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1]); }
|
||||
inline r32 UI_TopOffsetY(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1]); }
|
||||
#define UI_Parent(Element) DeferLoop(UI_PushParent(Element), UI_PopParent())
|
||||
#define UI_Width(Element) DeferLoop(UI_PushWidth(Element), UI_PopWidth())
|
||||
#define UI_Height(Element) DeferLoop(UI_PushHeight(Element), UI_PopHeight())
|
||||
#define UI_FixedX(Element) DeferLoop(UI_PushFixedX(Element), UI_PopFixedX())
|
||||
#define UI_FixedY(Element) DeferLoop(UI_PushFixedY(Element), UI_PopFixedY())
|
||||
#define UI_TextColor(Element) DeferLoop(UI_PushTextColor(Element), UI_PopTextColor())
|
||||
#define UI_BackgroundColor(Element) DeferLoop(UI_PushBackgroundColor(Element), UI_PopBackgroundColor())
|
||||
#define UI_BorderColor(Element) DeferLoop(UI_PushBorderColor(Element), UI_PopBorderColor())
|
||||
#define UI_BorderThickness(Element) DeferLoop(UI_PushBorderThickness(Element), UI_PopBorderThickness())
|
||||
#define UI_LayoutAxis(Element) DeferLoop(UI_PushLayoutAxis(Element), UI_PopLayoutAxis())
|
||||
#define UI_CornerRadius(Element) DeferLoop(UI_PushCornerRadius(Element), UI_PopCornerRadius())
|
||||
#define UI_Font(Element) DeferLoop(UI_PushFont(Element), UI_PopFont())
|
||||
#define UI_FontSize(Element) DeferLoop(UI_PushFontSize(Element), UI_PopFontSize())
|
||||
#define UI_OffsetX(Element) DeferLoop(UI_PushOffsetX(Element), UI_PopOffsetX())
|
||||
#define UI_OffsetY(Element) DeferLoop(UI_PushOffsetY(Element), UI_PopOffsetY())
|
||||
|
||||
static void UI_ApplyStyles(ui_box *Box)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
Assert(UI->Stacks.ParentStackUsed > 0); Box->Parent = UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed - 1]; if(UI->Stacks.AutoPopParent) { UI_PopParent(); UI->Stacks.AutoPopParent = false; }
|
||||
Assert(UI->Stacks.WidthStackUsed > 0); Box->SemanticSize[Axis2_X] = UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed - 1]; if(UI->Stacks.AutoPopWidth) { UI_PopWidth(); UI->Stacks.AutoPopWidth = false; }
|
||||
Assert(UI->Stacks.HeightStackUsed > 0); Box->SemanticSize[Axis2_Y] = UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed - 1]; if(UI->Stacks.AutoPopHeight) { UI_PopHeight(); UI->Stacks.AutoPopHeight = false; }
|
||||
Assert(UI->Stacks.FixedXStackUsed > 0); Box->FixedP.E[Axis2_X] = UI->Stacks.FixedXStack[UI->Stacks.FixedXStackUsed - 1]; if(UI->Stacks.AutoPopFixedX) { UI_PopFixedX(); UI->Stacks.AutoPopFixedX = false; }
|
||||
Assert(UI->Stacks.FixedYStackUsed > 0); Box->FixedP.E[Axis2_Y] = UI->Stacks.FixedYStack[UI->Stacks.FixedYStackUsed - 1]; if(UI->Stacks.AutoPopFixedY) { UI_PopFixedY(); UI->Stacks.AutoPopFixedY = false; }
|
||||
Assert(UI->Stacks.TextColorStackUsed > 0); Box->TextColor = UI->Stacks.TextColorStack[UI->Stacks.TextColorStackUsed - 1]; if(UI->Stacks.AutoPopTextColor) { UI_PopTextColor(); UI->Stacks.AutoPopTextColor = false; }
|
||||
Assert(UI->Stacks.BackgroundColorStackUsed > 0); Box->BackgroundColor = UI->Stacks.BackgroundColorStack[UI->Stacks.BackgroundColorStackUsed - 1]; if(UI->Stacks.AutoPopBackgroundColor) { UI_PopBackgroundColor(); UI->Stacks.AutoPopBackgroundColor = false; }
|
||||
Assert(UI->Stacks.BorderColorStackUsed > 0); Box->BorderColor = UI->Stacks.BorderColorStack[UI->Stacks.BorderColorStackUsed - 1]; if(UI->Stacks.AutoPopBorderColor) { UI_PopBorderColor(); UI->Stacks.AutoPopBorderColor = false; }
|
||||
Assert(UI->Stacks.BorderThicknessStackUsed > 0); Box->BorderThickness = UI->Stacks.BorderThicknessStack[UI->Stacks.BorderThicknessStackUsed - 1]; if(UI->Stacks.AutoPopBorderThickness) { UI_PopBorderThickness(); UI->Stacks.AutoPopBorderThickness = false; }
|
||||
Assert(UI->Stacks.LayoutAxisStackUsed > 0); Box->LayoutAxis = UI->Stacks.LayoutAxisStack[UI->Stacks.LayoutAxisStackUsed - 1]; if(UI->Stacks.AutoPopLayoutAxis) { UI_PopLayoutAxis(); UI->Stacks.AutoPopLayoutAxis = false; }
|
||||
Assert(UI->Stacks.CornerRadiusStackUsed > 0); Box->CornerRadius = UI->Stacks.CornerRadiusStack[UI->Stacks.CornerRadiusStackUsed - 1]; if(UI->Stacks.AutoPopCornerRadius) { UI_PopCornerRadius(); UI->Stacks.AutoPopCornerRadius = false; }
|
||||
Assert(UI->Stacks.FontStackUsed > 0); Box->Font = UI->Stacks.FontStack[UI->Stacks.FontStackUsed - 1]; if(UI->Stacks.AutoPopFont) { UI_PopFont(); UI->Stacks.AutoPopFont = false; }
|
||||
Assert(UI->Stacks.FontSizeStackUsed > 0); Box->FontSize = UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1]; if(UI->Stacks.AutoPopFontSize) { UI_PopFontSize(); UI->Stacks.AutoPopFontSize = false; }
|
||||
Assert(UI->Stacks.OffsetXStackUsed > 0); Box->Offset.x = UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1]; if(UI->Stacks.AutoPopOffsetX) { UI_PopOffsetX(); UI->Stacks.AutoPopOffsetX = false; }
|
||||
Assert(UI->Stacks.OffsetYStackUsed > 0); Box->Offset.y = UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1]; if(UI->Stacks.AutoPopOffsetY) { UI_PopOffsetY(); UI->Stacks.AutoPopOffsetY = false; }
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
struct ui_style_stacks
|
||||
{
|
||||
ui_box * ParentStack[64]; s32 ParentStackUsed; b32 AutoPopParent;
|
||||
ui_size WidthStack[64]; s32 WidthStackUsed; b32 AutoPopWidth;
|
||||
ui_size HeightStack[64]; s32 HeightStackUsed; b32 AutoPopHeight;
|
||||
r32 FixedXStack[64]; s32 FixedXStackUsed; b32 AutoPopFixedX;
|
||||
r32 FixedYStack[64]; s32 FixedYStackUsed; b32 AutoPopFixedY;
|
||||
v4 TextColorStack[64]; s32 TextColorStackUsed; b32 AutoPopTextColor;
|
||||
v4 BackgroundColorStack[64]; s32 BackgroundColorStackUsed; b32 AutoPopBackgroundColor;
|
||||
v4 BorderColorStack[64]; s32 BorderColorStackUsed; b32 AutoPopBorderColor;
|
||||
r32 BorderThicknessStack[64]; s32 BorderThicknessStackUsed; b32 AutoPopBorderThickness;
|
||||
axis2 LayoutAxisStack[64]; s32 LayoutAxisStackUsed; b32 AutoPopLayoutAxis;
|
||||
r32 CornerRadiusStack[64]; s32 CornerRadiusStackUsed; b32 AutoPopCornerRadius;
|
||||
font_id FontStack[64]; s32 FontStackUsed; b32 AutoPopFont;
|
||||
r32 FontSizeStack[64]; s32 FontSizeStackUsed; b32 AutoPopFontSize;
|
||||
r32 OffsetXStack[64]; s32 OffsetXStackUsed; b32 AutoPopOffsetX;
|
||||
r32 OffsetYStack[64]; s32 OffsetYStackUsed; b32 AutoPopOffsetY;
|
||||
};
|
||||
|
|
@ -255,7 +255,7 @@ flat out s32 TextureIndex;
|
|||
|
||||
SourceP = In_SourceP;
|
||||
|
||||
v2 ScreenP = V2(DestP.x / Uniform_Resolution.x, DestP.y / Uniform_Resolution.y);
|
||||
v2 ScreenP = DestP / Uniform_Resolution;;
|
||||
ScreenP = ScreenP*2 - 1;
|
||||
ScreenP.y = -ScreenP.y;
|
||||
|
||||
|
@ -416,6 +416,11 @@ static opengl_context OpenGL_SetupContext(vn_render_commands *RenderCommands, um
|
|||
RenderCommands->MaxQuadIndexCount = MAX_QUAD_COUNT*6;
|
||||
RenderCommands->QuadIndexBase = (s32 *)OpenGL_AllocateMemory(RenderCommands->MaxQuadIndexCount*sizeof(s32));
|
||||
|
||||
#if 0
|
||||
RenderCommands->MaxInstancedQuadCount = MAX_QUAD_COUNT;
|
||||
RenderCommands->InstancedQuadBase = (instanced_quad *)OpenGL_AllocateMemory(RenderCommands->MaxInstancedQuadCount*sizeof(instanced_quad));
|
||||
#endif
|
||||
|
||||
Context.QuadProgram = OpenGL_CompileQuadProgram();
|
||||
|
||||
glGenBuffers(1, &Context.VertexBuffer);
|
||||
|
@ -453,6 +458,9 @@ static void OpenGL_BeginFrame(vn_render_commands *RenderCommands, v2 RenderDim)
|
|||
RenderCommands->PushBufferAt = RenderCommands->PushBufferBase;
|
||||
RenderCommands->QuadVertexCount = 0;
|
||||
RenderCommands->QuadIndexCount = 0;
|
||||
#if 0
|
||||
RenderCommands->InstancedQuadCount = 0;
|
||||
#endif
|
||||
|
||||
RenderCommands->RenderDim = RenderDim;
|
||||
}
|
||||
|
@ -518,6 +526,27 @@ static void OpenGL_EndFrame(opengl_context *Context, vn_render_commands *RenderC
|
|||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
} break;
|
||||
|
||||
#if 0
|
||||
case Render_Command_render_command_instanced_quads:
|
||||
{
|
||||
render_command_quads *Command = (render_command_quads *)PushBufferAt;
|
||||
PushBufferAt += sizeof(*Command);
|
||||
|
||||
for(s32 TextureIndex = 0;
|
||||
TextureIndex < Command->TexturesUsed;
|
||||
++TextureIndex)
|
||||
{
|
||||
opengl_texture Texture = OpenGL_GetTextureFromHandle(Command->Textures[TextureIndex]);
|
||||
glActiveTexture(GL_TEXTURE0 + TextureIndex);
|
||||
glBindTexture(GL_TEXTURE_2D, Texture.ID);
|
||||
}
|
||||
|
||||
glDrawElements(GL_TRIANGLES, Command->QuadCount*6, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
} break;
|
||||
#endif
|
||||
|
||||
case Render_Command_render_command_clip:
|
||||
{
|
||||
render_command_clip *Command = (render_command_clip *)PushBufferAt;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#define OPENGL_RENDER_H
|
||||
|
||||
#include "vn_opengl_defines.h"
|
||||
#include "generated/vn_opengl_functions.h"
|
||||
#include "generated/vn_opengl_functions.meta.h"
|
||||
|
||||
struct opengl_texture
|
||||
{
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "../metadesk/md.h"
|
||||
#include "../metadesk/md.c"
|
||||
#include "../meow_hash_x64_aesni.h"
|
||||
|
||||
#include "codegen_embed.h"
|
||||
#include "codegen_table.h"
|
||||
|
||||
#include "codegen.h"
|
||||
|
||||
#include "codegen_embed.c"
|
||||
#include "codegen_table.c"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Helpers
|
||||
|
||||
static CG_FilePair
|
||||
CG_FilePairFromNode(MD_Node *node)
|
||||
{
|
||||
CG_FilePair result = {0};
|
||||
MD_CodeLoc loc = MD_CodeLocFromNode(node);
|
||||
MD_String8 filename = loc.filename;
|
||||
MD_b32 found = 0;
|
||||
for(int i = 0; i < cg_file_pair_count; i += 1)
|
||||
{
|
||||
if(MD_S8Match(filename, cg_file_pairs[i].src_filename, 0))
|
||||
{
|
||||
result = cg_file_pairs[i];
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found == 0)
|
||||
{
|
||||
MD_String8 folder = MD_PathChopLastSlash(filename);
|
||||
MD_String8 layer_name = MD_PathChopLastPeriod(MD_PathSkipLastSlash(loc.filename));
|
||||
MD_String8 gen_folder = MD_S8Fmt(cg_arena, "%.*s/generated", MD_S8VArg(folder));
|
||||
MD_String8 h_filename = MD_S8Fmt(cg_arena, "%.*s/%.*s.meta.h", MD_S8VArg(gen_folder), MD_S8VArg(layer_name));
|
||||
MD_String8 c_filename = MD_S8Fmt(cg_arena, "%.*s/%.*s.meta.c", MD_S8VArg(gen_folder), MD_S8VArg(layer_name));
|
||||
result.src_filename = filename;
|
||||
result.h = fopen((char *)h_filename.str, "w");
|
||||
result.c = fopen((char *)c_filename.str, "w");
|
||||
cg_file_pairs[cg_file_pair_count] = result;
|
||||
cg_file_pair_count += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static FILE *
|
||||
CG_FileFromNodePair(MD_Node *node, CG_FilePair *pair)
|
||||
{
|
||||
FILE *result = pair->h;
|
||||
if(MD_NodeHasTag(node, MD_S8Lit("c"), MD_StringMatchFlag_CaseInsensitive))
|
||||
{
|
||||
result = pair->c;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
CG_CloseAllFiles(void)
|
||||
{
|
||||
for(int i = 0; i < cg_file_pair_count; i += 1)
|
||||
{
|
||||
fclose(cg_file_pairs[i].h);
|
||||
fclose(cg_file_pairs[i].c);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
CG_GenerateMultilineStringAsCLiteral(FILE *file, MD_String8 string)
|
||||
{
|
||||
fprintf(file, "\"\"\n\"");
|
||||
for(MD_u64 i = 0; i < string.size; i += 1)
|
||||
{
|
||||
if(string.str[i] == '\n')
|
||||
{
|
||||
fprintf(file, "\\n\"\n\"");
|
||||
}
|
||||
else if(string.str[i] == '\r')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "%c", string.str[i]);
|
||||
}
|
||||
}
|
||||
fprintf(file, "\"\n");
|
||||
}
|
||||
|
||||
static MD_String8
|
||||
CG_EscapedFromString(MD_Arena *arena, MD_String8 string)
|
||||
{
|
||||
MD_ArenaTemp scratch = MD_GetScratch(&arena, 1);
|
||||
MD_String8List strs = {0};
|
||||
MD_b32 escaped = 0;
|
||||
MD_u64 start = 0;
|
||||
for(MD_u64 idx = 0; idx <= string.size; idx += 1)
|
||||
{
|
||||
if(idx < string.size && escaped)
|
||||
{
|
||||
escaped = 0;
|
||||
start = idx+1;
|
||||
MD_u8 replace_char = 0;
|
||||
switch(string.str[idx])
|
||||
{
|
||||
default: break;
|
||||
case 'a': replace_char = 0x07; break;
|
||||
case 'b': replace_char = 0x08; break;
|
||||
case 'e': replace_char = 0x1b; break;
|
||||
case 'f': replace_char = 0x0c; break;
|
||||
case 'n': replace_char = 0x0a; break;
|
||||
case 'r': replace_char = 0x0d; break;
|
||||
case 't': replace_char = 0x09; break;
|
||||
case 'v': replace_char = 0x0b; break;
|
||||
case '\\': replace_char = 0x5c; break;
|
||||
case '\'': replace_char = 0x27; break;
|
||||
case '\"': replace_char = 0x22; break;
|
||||
case '\?': replace_char = 0x3f; break;
|
||||
}
|
||||
if(replace_char)
|
||||
{
|
||||
MD_String8 string = MD_S8Copy(scratch.arena, MD_S8(&replace_char, 1));
|
||||
MD_S8ListPush(scratch.arena, &strs, string);
|
||||
}
|
||||
}
|
||||
else if(idx == string.size || string.str[idx] == '\\')
|
||||
{
|
||||
escaped = (string.str[idx] == '\\');
|
||||
MD_String8 part = MD_S8Substring(string, start, idx);
|
||||
MD_S8ListPush(scratch.arena, &strs, part);
|
||||
start = idx;
|
||||
}
|
||||
}
|
||||
MD_String8 result = MD_S8ListJoin(arena, strs, 0);
|
||||
MD_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Entry Point
|
||||
|
||||
int main(int argument_count, char **arguments)
|
||||
{
|
||||
cg_arena = MD_ArenaAlloc();
|
||||
|
||||
//- rjf: parse command line
|
||||
MD_String8List options = MD_StringListFromArgCV(cg_arena, argument_count, arguments);
|
||||
MD_CmdLine cmdln = MD_MakeCmdLineFromOptions(cg_arena, options);
|
||||
|
||||
//- rjf: parse all files
|
||||
MD_Node *file_list = MD_MakeList(cg_arena);
|
||||
for(MD_String8Node *n = cmdln.inputs.first; n != 0; n = n->next)
|
||||
{
|
||||
MD_String8 code_dir = n->string;
|
||||
printf("searching %.*s for metacode...\n", MD_S8VArg(code_dir));
|
||||
MD_FileIter it = {0};
|
||||
MD_FileIterBegin(&it, code_dir);
|
||||
for(MD_FileInfo info = {0};;)
|
||||
{
|
||||
info = MD_FileIterNext(cg_arena, &it);
|
||||
if(info.filename.size == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(!(info.flags & MD_FileFlag_Directory) &&
|
||||
MD_S8Match(MD_PathSkipLastPeriod(info.filename), MD_S8Lit("md"), MD_StringMatchFlag_CaseInsensitive))
|
||||
{
|
||||
printf("parsing %.*s...\n", MD_S8VArg(info.filename));
|
||||
MD_String8 path = MD_S8Fmt(cg_arena, "%.*s/%.*s", MD_S8VArg(code_dir), MD_S8VArg(info.filename));
|
||||
MD_ParseResult parse = MD_ParseWholeFile(cg_arena, path);
|
||||
MD_PushNewReference(cg_arena, file_list, parse.node);
|
||||
}
|
||||
}
|
||||
MD_FileIterEnd(&it);
|
||||
}
|
||||
|
||||
//- rjf: send all parses to backends
|
||||
CG_EMBED_Generate(file_list);
|
||||
CG_TBL_Generate(file_list);
|
||||
CG_CloseAllFiles();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef METAPROGRAM_H
|
||||
#define METAPROGRAM_H
|
||||
|
||||
typedef struct CG_FilePair CG_FilePair;
|
||||
struct CG_FilePair
|
||||
{
|
||||
MD_String8 src_filename;
|
||||
FILE *h;
|
||||
FILE *c;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Helpers
|
||||
|
||||
static CG_FilePair CG_FilePairFromNode(MD_Node *node);
|
||||
static FILE *CG_FileFromNodePair(MD_Node *node, CG_FilePair *pair);
|
||||
static void CG_CloseAllFiles(void);
|
||||
static void CG_GenerateMultilineStringAsCLiteral(FILE *file, MD_String8 string);
|
||||
static MD_String8 CG_EscapedFromString(MD_Arena *arena, MD_String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
static MD_Arena *cg_arena = 0;
|
||||
static int cg_file_pair_count = 0;
|
||||
static CG_FilePair cg_file_pairs[4096] = {0};
|
||||
|
||||
#endif // METAPROGRAM_H
|
|
@ -0,0 +1,40 @@
|
|||
static void
|
||||
CG_EMBED_Generate(MD_Node *file_list)
|
||||
{
|
||||
for(MD_EachNode(file_ref, file_list->first_child))
|
||||
{
|
||||
MD_Node *file = MD_ResolveNodeFromReference(file_ref);
|
||||
for(MD_EachNode(node, file->first_child))
|
||||
{
|
||||
CG_FilePair f = CG_FilePairFromNode(node);
|
||||
if(MD_NodeHasTag(node, MD_S8Lit("embed_string"), MD_StringMatchFlag_CaseInsensitive))
|
||||
{
|
||||
FILE *file = CG_FileFromNodePair(node, &f);
|
||||
fprintf(file, "read_only global String8 %.*s =\nStr8LitComp(", MD_S8VArg(node->string));
|
||||
CG_GenerateMultilineStringAsCLiteral(file, node->first_child->string);
|
||||
fprintf(file, ");\n\n");
|
||||
}
|
||||
if(MD_NodeHasTag(node, MD_S8Lit("embed_file"), MD_StringMatchFlag_CaseInsensitive))
|
||||
{
|
||||
MD_String8 path = node->first_child->string;
|
||||
MD_String8 file_data = MD_LoadEntireFile(cg_arena, path);
|
||||
meow_u128 file_data_hash = MeowHash(MeowDefaultSeed, file_data.size, file_data.str);
|
||||
FILE *file = CG_FileFromNodePair(node, &f);
|
||||
fprintf(file, "read_only global U8 %.*s_data[] =\n{\n", MD_S8VArg(node->string));
|
||||
MD_u64 col = 0;
|
||||
for(MD_u64 idx = 0; idx < file_data.size; idx += 1, col += 1)
|
||||
{
|
||||
fprintf(file, "%i,", (int)file_data.str[idx]);
|
||||
if(col == 32)
|
||||
{
|
||||
fprintf(file,"\n");
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
fprintf(file, "};\n\n");
|
||||
fprintf(file, "read_only global String8 %.*s = {%.*s_data, sizeof(%.*s_data)};\n\n", MD_S8VArg(node->string), MD_S8VArg(node->string), MD_S8VArg(node->string));
|
||||
fprintf(file, "read_only global U64 %.*s_hash[2] = {0x%" PRIx64 ", 0x%" PRIx64 "};\n\n", MD_S8VArg(node->string), MeowU64From(file_data_hash, 0), MeowU64From(file_data_hash, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/* date = May 5th 2023 11:04 am */
|
||||
|
||||
#ifndef CODEGEN_EMBED_H
|
||||
#define CODEGEN_EMBED_H
|
||||
|
||||
#endif // CODEGEN_EMBED_H
|
|
@ -0,0 +1,581 @@
|
|||
static MD_Map cg_tbl_top_level_node_grid_map = {0};
|
||||
static MD_Map cg_tbl_top_level_table_header_map = {0};
|
||||
|
||||
static MD_Map cg_tbl_layer_map_gen = {0};
|
||||
static MD_Map cg_tbl_layer_map_gen_enum = {0};
|
||||
static MD_Map cg_tbl_layer_map_gen_data = {0};
|
||||
|
||||
static MD_String8 cg_tbl_tag__table = MD_S8LitComp("table");
|
||||
static MD_String8 cg_tbl_tag__table_gen = MD_S8LitComp("table_gen");
|
||||
static MD_String8 cg_tbl_tag__table_gen_enum = MD_S8LitComp("table_gen_enum");
|
||||
static MD_String8 cg_tbl_tag__table_gen_data = MD_S8LitComp("table_gen_data");
|
||||
|
||||
static CG_NodeArray
|
||||
CG_NodeArrayMake(MD_u64 count)
|
||||
{
|
||||
CG_NodeArray result = {0};
|
||||
result.count = count;
|
||||
result.v = MD_PushArrayZero(cg_arena, MD_Node *, result.count);
|
||||
for(MD_u64 idx = 0; idx < result.count; idx += 1)
|
||||
{
|
||||
result.v[idx] = MD_NilNode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CG_NodeGrid
|
||||
CG_GridFromNode(MD_Node *node)
|
||||
{
|
||||
CG_NodeGrid grid = {0};
|
||||
|
||||
//- rjf: determine dimensions
|
||||
MD_u64 row_count = 0;
|
||||
MD_u64 column_count = 0;
|
||||
{
|
||||
for(MD_EachNode(row, node->first_child))
|
||||
{
|
||||
row_count += 1;
|
||||
MD_u64 cell_count_this_row = MD_ChildCountFromNode(row);
|
||||
column_count = MD_Max(cell_count_this_row, column_count);
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: allocate cells / row parents
|
||||
{
|
||||
grid.cells = CG_NodeArrayMake(row_count * column_count);
|
||||
grid.row_parents = CG_NodeArrayMake(row_count);
|
||||
}
|
||||
|
||||
//- rjf: fill cells
|
||||
{
|
||||
MD_u64 row_idx = 0;
|
||||
for(MD_EachNode(row, node->first_child))
|
||||
{
|
||||
MD_u64 col_idx = 0;
|
||||
grid.row_parents.v[row_idx] = row;
|
||||
for(MD_EachNode(cell, row->first_child))
|
||||
{
|
||||
grid.cells.v[row_idx * column_count + col_idx] = cell;
|
||||
col_idx += 1;
|
||||
}
|
||||
row_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
static CG_TableHeader
|
||||
CG_TableHeaderFromTag(MD_Node *tag)
|
||||
{
|
||||
CG_TableHeader result = {0};
|
||||
result.column_count = MD_ChildCountFromNode(tag);
|
||||
result.column_descs = MD_PushArrayZero(cg_arena, CG_ColumnDesc, result.column_count);
|
||||
MD_u64 idx = 0;
|
||||
for(MD_EachNode(column_node, tag->first_child))
|
||||
{
|
||||
result.column_descs[idx].kind = CG_ColumnKind_Default;
|
||||
result.column_descs[idx].name = column_node->string;
|
||||
MD_Node *check_for_tag = MD_TagFromString(column_node, MD_S8Lit("check_for_tag"), 0);
|
||||
if(!MD_NodeIsNil(check_for_tag))
|
||||
{
|
||||
result.column_descs[idx].kind = CG_ColumnKind_CheckForTag;
|
||||
result.column_descs[idx].tag_string = check_for_tag->first_child->string;
|
||||
}
|
||||
MD_Node *default_value = MD_TagFromString(column_node, MD_S8Lit("default"), 0);
|
||||
if(!MD_NodeIsNil(default_value))
|
||||
{
|
||||
result.column_descs[idx].default_value = default_value->first_child->string;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static MD_u64
|
||||
CG_RowChildIndexFromColumnName(CG_TableHeader *header, MD_String8 column_name)
|
||||
{
|
||||
MD_u64 result = 0;
|
||||
for(MD_u64 idx = 0; idx < header->column_count; idx += 1)
|
||||
{
|
||||
if(MD_S8Match(header->column_descs[idx].name, column_name, 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(header->column_descs[idx].kind == CG_ColumnKind_Default)
|
||||
{
|
||||
result += 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static MD_i64
|
||||
CG_TableExprEvaluate_Numeric(CG_ExpandInfo *info, MD_Expr *expr)
|
||||
{
|
||||
MD_i64 result = 0;
|
||||
CG_TableOp op = expr->op ? expr->op->op_id : CG_TableOp_Null;
|
||||
switch(op)
|
||||
{
|
||||
case CG_TableOp_Equal:
|
||||
case CG_TableOp_IsNotEqual:
|
||||
{
|
||||
MD_ArenaTemp scratch = MD_GetScratch(0, 0);
|
||||
MD_String8List left_strs = {0};
|
||||
MD_String8List right_strs = {0};
|
||||
CG_TableExprEvaluate_String(info, expr->left, &left_strs);
|
||||
CG_TableExprEvaluate_String(info, expr->right, &right_strs);
|
||||
MD_String8 left_str = MD_S8ListJoin(scratch.arena, left_strs, 0);
|
||||
MD_String8 right_str = MD_S8ListJoin(scratch.arena, right_strs, 0);
|
||||
result = MD_S8Match(left_str, right_str, 0);
|
||||
if(op == CG_TableOp_IsNotEqual)
|
||||
{
|
||||
result = !result;
|
||||
}
|
||||
MD_ReleaseScratch(scratch);
|
||||
}break;
|
||||
|
||||
case CG_TableOp_BooleanAnd:
|
||||
case CG_TableOp_BooleanOr:
|
||||
{
|
||||
MD_i64 left = CG_TableExprEvaluate_Numeric(info, expr->left);
|
||||
MD_i64 right = CG_TableExprEvaluate_Numeric(info, expr->right);
|
||||
switch(op)
|
||||
{
|
||||
case CG_TableOp_BooleanAnd: result = left && right; break;
|
||||
case CG_TableOp_BooleanOr: result = left || right; break;
|
||||
}
|
||||
}break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
CG_TableExprEvaluate_String(CG_ExpandInfo *info, MD_Expr *expr, MD_String8List *out)
|
||||
{
|
||||
CG_TableOp op = expr->op ? expr->op->op_id : CG_TableOp_Null;
|
||||
switch(op)
|
||||
{
|
||||
default:
|
||||
case CG_TableOp_Null:
|
||||
{
|
||||
MD_S8ListPush(cg_arena, out, expr->md_node->string);
|
||||
}break;
|
||||
|
||||
case CG_TableOp_Dot:
|
||||
{
|
||||
MD_Expr *label_expr = expr->left;
|
||||
MD_Expr *column_query_expr = expr->right;
|
||||
MD_Node *label_node = label_expr->md_node;
|
||||
MD_Node *column_query_node = column_query_expr->md_node;
|
||||
MD_String8 label = label_node->string;
|
||||
MD_String8 column_query = column_query_node->string;
|
||||
MD_b32 column_query_is_by_expand_idx = MD_S8Match(column_query_node->string, MD_S8Lit("_it"), 0);
|
||||
MD_b32 column_query_is_by_name = !column_query_is_by_expand_idx && column_query_node->flags & MD_NodeFlag_Identifier;
|
||||
MD_b32 column_query_is_by_index = column_query_node->flags & MD_NodeFlag_Numeric;
|
||||
|
||||
// rjf: find which expansion this label refers to, grab its iterator
|
||||
CG_ExpandIter *iter = 0;
|
||||
for(CG_ExpandIter *it = info->first_expand_iter; it != 0; it = it->next)
|
||||
{
|
||||
if(MD_S8Match(it->label, label, 0))
|
||||
{
|
||||
iter = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: error on invalid label
|
||||
if(iter == 0)
|
||||
{
|
||||
MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(label_node), MD_MessageKind_Error, "Expansion label \"%S\" was not found as referring to a valid @expand tag.", label);
|
||||
}
|
||||
|
||||
// rjf: generate strings from iterator's table
|
||||
if(iter != 0)
|
||||
{
|
||||
CG_NodeGrid *grid = iter->grid;
|
||||
CG_TableHeader *header = iter->header;
|
||||
MD_Node *row = grid->row_parents.v[iter->idx];
|
||||
|
||||
// rjf: grab the cell string given the row & column_query
|
||||
MD_String8 cell_string = {0};
|
||||
{
|
||||
// NOTE(rjf): expansion index counter
|
||||
if(column_query_is_by_expand_idx)
|
||||
{
|
||||
MD_i64 index = iter->idx;
|
||||
cell_string = MD_S8Fmt(cg_arena, "%" PRIu64 "", index);
|
||||
}
|
||||
|
||||
// NOTE(rjf): by-name index (look into table header)
|
||||
else if(column_query_is_by_name && header != 0)
|
||||
{
|
||||
MD_u64 column_idx = 0;
|
||||
CG_ColumnDesc *column = 0;
|
||||
for(MD_u64 col_idx = 0; col_idx < header->column_count; col_idx += 1)
|
||||
{
|
||||
if(MD_S8Match(header->column_descs[col_idx].name, column_query, 0))
|
||||
{
|
||||
column = &header->column_descs[col_idx];
|
||||
column_idx = col_idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MD_u64 row_child_idx = CG_RowChildIndexFromColumnName(header, column_query);
|
||||
|
||||
// rjf: error on invalid column
|
||||
if(column == 0)
|
||||
{
|
||||
MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(column_query_node), MD_MessageKind_Error, "Column query \"%S\" did not map to a valid column for expansion label \"%S\".", column_query, label);
|
||||
}
|
||||
|
||||
if(column != 0)
|
||||
{
|
||||
switch(column->kind)
|
||||
{
|
||||
default:
|
||||
case CG_ColumnKind_Default:
|
||||
{
|
||||
MD_Node *cell_node = MD_ChildFromIndex(row, row_child_idx);
|
||||
cell_string = cell_node->string;
|
||||
if(MD_S8Match(cell_node->raw_string, MD_S8Lit("."), 0))
|
||||
{
|
||||
cell_string = column->default_value;
|
||||
}
|
||||
}break;
|
||||
|
||||
case CG_ColumnKind_CheckForTag:
|
||||
{
|
||||
MD_b32 has_tag = MD_NodeHasTag(row, column->tag_string, 0);
|
||||
cell_string = has_tag ? MD_S8Lit("1") : MD_S8Lit("0");
|
||||
}break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(rjf): by-index (grab nth child of row)
|
||||
else if(column_query_is_by_index)
|
||||
{
|
||||
MD_i64 index = MD_CStyleIntFromString(column_query);
|
||||
cell_string = MD_ChildFromIndex(row, index)->string;
|
||||
}
|
||||
}
|
||||
|
||||
MD_S8ListPush(cg_arena, out, cell_string);
|
||||
}
|
||||
|
||||
}break;
|
||||
|
||||
case CG_TableOp_Bump:
|
||||
{
|
||||
MD_u64 dst = MD_CStyleIntFromString(expr->unary_operand->md_node->string);
|
||||
MD_u64 src = out->total_size;
|
||||
MD_u64 spaces_to_print = dst - src;
|
||||
if(dst > src)
|
||||
{
|
||||
for(MD_u64 space_idx = 0; space_idx < spaces_to_print; space_idx += 1)
|
||||
{
|
||||
MD_S8ListPush(cg_arena, out, MD_S8Lit(" "));
|
||||
}
|
||||
}
|
||||
}break;
|
||||
|
||||
case CG_TableOp_CheckIfTrue:
|
||||
{
|
||||
MD_i64 check_val = CG_TableExprEvaluate_Numeric(info, expr->left);
|
||||
if(check_val)
|
||||
{
|
||||
CG_TableExprEvaluate_String(info, expr->right, out);
|
||||
}
|
||||
}break;
|
||||
|
||||
case CG_TableOp_Concat:
|
||||
{
|
||||
CG_TableExprEvaluate_String(info, expr->left, out);
|
||||
CG_TableExprEvaluate_String(info, expr->right, out);
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
CG_LoopExpansionDimension(CG_ExpandIter *it, CG_ExpandInfo *info, MD_String8List *out)
|
||||
{
|
||||
if(it->next)
|
||||
{
|
||||
for(MD_u64 idx = 0; idx < it->count; idx += 1)
|
||||
{
|
||||
it->idx = idx;
|
||||
CG_LoopExpansionDimension(it->next, info, out);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(MD_u64 idx = 0; idx < it->count; idx += 1)
|
||||
{
|
||||
it->idx = idx;
|
||||
MD_String8List expansion_strs = {0};
|
||||
MD_u64 start_idx = 0;
|
||||
for(MD_u64 char_idx = 0; char_idx <= info->strexpr.size; char_idx += 1)
|
||||
{
|
||||
MD_b32 is_expr_marker = info->strexpr.str[char_idx] == '$';
|
||||
|
||||
// rjf: push regular string contents
|
||||
if(char_idx == info->strexpr.size || is_expr_marker)
|
||||
{
|
||||
MD_String8 normal_string_chunk = MD_S8Substring(info->strexpr, start_idx, char_idx);
|
||||
MD_String8 escaped = CG_EscapedFromString(cg_arena, normal_string_chunk);
|
||||
MD_S8ListPush(cg_arena, &expansion_strs, escaped);
|
||||
}
|
||||
|
||||
// rjf: handle expansion
|
||||
if(is_expr_marker)
|
||||
{
|
||||
MD_String8 expr_string = MD_S8Skip(info->strexpr, char_idx+1);
|
||||
{
|
||||
MD_i64 paren_nest = 0;
|
||||
for(MD_u64 expr_str_char_idx = 0; expr_str_char_idx < expr_string.size; expr_str_char_idx += 1)
|
||||
{
|
||||
if(expr_string.str[expr_str_char_idx] == '(')
|
||||
{
|
||||
paren_nest += 1;
|
||||
}
|
||||
else if(expr_string.str[expr_str_char_idx] == ')')
|
||||
{
|
||||
paren_nest -= 1;
|
||||
if(paren_nest == 0)
|
||||
{
|
||||
expr_string.size = expr_str_char_idx+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MD_ParseResult parse = MD_ParseOneNode(cg_arena, expr_string, 0);
|
||||
MD_Node *node = parse.node;
|
||||
MD_ExprParseResult expr_parse = MD_ExprParse(cg_arena, &info->expr_op_table, node->first_child, MD_NilNode());
|
||||
MD_Expr *expr = expr_parse.expr;
|
||||
CG_TableExprEvaluate_String(info, expr, &expansion_strs);
|
||||
MD_String8 parsed_string = MD_S8Substring(info->strexpr, char_idx+1, char_idx+1+parse.string_advance);
|
||||
parsed_string = MD_S8ChopWhitespace(parsed_string);
|
||||
start_idx = char_idx+1+parsed_string.size;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: push expansion string to output list
|
||||
MD_String8 expansion_str = MD_S8ListJoin(cg_arena, expansion_strs, 0);
|
||||
MD_S8ListPush(cg_arena, out, expansion_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static MD_String8List
|
||||
CG_GenStringListFromNode(MD_ExprOprTable expr_op_table, MD_Node *gen)
|
||||
{
|
||||
MD_String8List result = {0};
|
||||
MD_ArenaTemp scratch = MD_GetScratch(0, 0);
|
||||
|
||||
for(MD_EachNode(strexpr, gen->first_child))
|
||||
{
|
||||
//- rjf: build expansion iterator list
|
||||
CG_ExpandIter *first_iter = 0;
|
||||
CG_ExpandIter *last_iter = 0;
|
||||
{
|
||||
for(MD_EachNode(tag, strexpr->first_tag))
|
||||
{
|
||||
if(MD_S8Match(tag->string, MD_S8Lit("expand"), 0))
|
||||
{
|
||||
MD_Node *table_name_node = MD_ChildFromIndex(tag, 0);
|
||||
MD_Node *label_node = MD_ChildFromIndex(tag, 1);
|
||||
MD_String8 table_name = table_name_node->string;
|
||||
MD_String8 label = label_node->string;
|
||||
|
||||
// rjf: grab the table associated with table_name
|
||||
CG_NodeGrid *grid = 0;
|
||||
{
|
||||
MD_MapSlot *slot = MD_MapLookup(&cg_tbl_top_level_node_grid_map, MD_MapKeyStr(table_name));
|
||||
if(slot != 0)
|
||||
{
|
||||
grid = slot->val;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: grab the table header associated with table_name
|
||||
CG_TableHeader *header = 0;
|
||||
{
|
||||
MD_MapSlot *slot = MD_MapLookup(&cg_tbl_top_level_table_header_map, MD_MapKeyStr(table_name));
|
||||
if(slot != 0)
|
||||
{
|
||||
header = slot->val;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: make iterator node if we got a grid
|
||||
if(grid != 0)
|
||||
{
|
||||
CG_ExpandIter *iter = MD_PushArrayZero(scratch.arena, CG_ExpandIter, 1);
|
||||
MD_QueuePush(first_iter, last_iter, iter);
|
||||
iter->grid = grid;
|
||||
iter->header = header;
|
||||
iter->label = label;
|
||||
iter->count = grid->row_parents.count;
|
||||
}
|
||||
|
||||
// rjf: print out an error if grid is 0
|
||||
if(grid == 0)
|
||||
{
|
||||
MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(tag), MD_MessageKind_Error, "Table \"%S\" was not found.", table_name);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: generate string list for this strexpr & push to result
|
||||
if(first_iter != 0)
|
||||
{
|
||||
CG_ExpandInfo info = {0};
|
||||
{
|
||||
info.strexpr = strexpr->string;
|
||||
info.first_expand_iter = first_iter;
|
||||
info.expr_op_table = expr_op_table;
|
||||
}
|
||||
CG_LoopExpansionDimension(first_iter, &info, &result);
|
||||
}
|
||||
//- rjf: generate non-expansion strings
|
||||
else
|
||||
{
|
||||
MD_String8 escaped = CG_EscapedFromString(cg_arena, strexpr->string);
|
||||
MD_S8ListPush(cg_arena, &result, escaped);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MD_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
CG_TBL_Generate(MD_Node *file_list)
|
||||
{
|
||||
//- rjf: initialize all maps
|
||||
cg_tbl_top_level_node_grid_map = MD_MapMake(cg_arena);
|
||||
cg_tbl_top_level_table_header_map = MD_MapMake(cg_arena);
|
||||
cg_tbl_layer_map_gen = MD_MapMake(cg_arena);
|
||||
cg_tbl_layer_map_gen_enum = MD_MapMake(cg_arena);
|
||||
cg_tbl_layer_map_gen_data = MD_MapMake(cg_arena);
|
||||
|
||||
//- rjf: build table expression operator table
|
||||
MD_ExprOprTable table_expr_op_table = {0};
|
||||
{
|
||||
MD_ExprOprList ops_list = {0};
|
||||
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 10, MD_S8Lit("."), CG_TableOp_Dot, 0);
|
||||
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Prefix, 9, MD_S8Lit("=>"), CG_TableOp_Bump, 0);
|
||||
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 6, MD_S8Lit("??"), CG_TableOp_CheckIfTrue, 0);
|
||||
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 7, MD_S8Lit(".."), CG_TableOp_Concat, 0);
|
||||
|
||||
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 8, MD_S8Lit("=="), CG_TableOp_Equal, 0);
|
||||
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 8, MD_S8Lit("!="), CG_TableOp_IsNotEqual, 0);
|
||||
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 5, MD_S8Lit("&&"), CG_TableOp_BooleanAnd, 0);
|
||||
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 4, MD_S8Lit("||"), CG_TableOp_BooleanOr, 0);
|
||||
table_expr_op_table = MD_ExprBakeOprTableFromList(cg_arena, &ops_list);
|
||||
}
|
||||
|
||||
//- rjf: gather phase
|
||||
for(MD_EachNode(file_ref, file_list->first_child))
|
||||
{
|
||||
MD_Node *file = MD_ResolveNodeFromReference(file_ref);
|
||||
MD_String8 layer_name = file->string;
|
||||
MD_MapKey layer_key = MD_MapKeyStr(layer_name);
|
||||
for(MD_EachNode(node, file->first_child))
|
||||
{
|
||||
MD_Node *table_tag = MD_TagFromString(node, cg_tbl_tag__table, 0);
|
||||
if(!MD_NodeIsNil(table_tag))
|
||||
{
|
||||
CG_NodeGrid *grid = MD_PushArrayZero(cg_arena, CG_NodeGrid, 1);
|
||||
*grid = CG_GridFromNode(node);
|
||||
MD_MapOverwrite(cg_arena, &cg_tbl_top_level_node_grid_map, MD_MapKeyStr(node->string), grid);
|
||||
CG_TableHeader *header = MD_PushArrayZero(cg_arena, CG_TableHeader, 1);
|
||||
*header = CG_TableHeaderFromTag(table_tag);
|
||||
MD_MapOverwrite(cg_arena, &cg_tbl_top_level_table_header_map, MD_MapKeyStr(node->string), header);
|
||||
}
|
||||
if(MD_NodeHasTag(node, cg_tbl_tag__table_gen, 0))
|
||||
{
|
||||
MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen, layer_key, node);
|
||||
}
|
||||
if(MD_NodeHasTag(node, cg_tbl_tag__table_gen_enum, 0))
|
||||
{
|
||||
MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen_enum, layer_key, node);
|
||||
}
|
||||
if(MD_NodeHasTag(node, cg_tbl_tag__table_gen_data, 0))
|
||||
{
|
||||
MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen_data, layer_key, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: generation phase
|
||||
for(MD_EachNode(file_ref, file_list->first_child))
|
||||
{
|
||||
MD_Node *file = MD_ResolveNodeFromReference(file_ref);
|
||||
MD_String8 layer_name = file->string;
|
||||
MD_MapKey layer_key = MD_MapKeyStr(layer_name);
|
||||
|
||||
//- rjf: generate all table enums
|
||||
for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen_enum, layer_key);
|
||||
slot != 0;
|
||||
slot = MD_MapScan(slot->next, layer_key))
|
||||
{
|
||||
MD_Node *gen = (MD_Node *)slot->val;
|
||||
CG_FilePair f = CG_FilePairFromNode(gen);
|
||||
FILE *file = CG_FileFromNodePair(gen, &f);
|
||||
fprintf(file, "enum %.*s\n{\n", MD_S8VArg(gen->string));
|
||||
MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen);
|
||||
MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") };
|
||||
MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join);
|
||||
fprintf(file, "%.*s", MD_S8VArg(gen_string));
|
||||
fprintf(file, "\n};\n\n");
|
||||
}
|
||||
|
||||
//- rjf: generate all data tables
|
||||
for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen_data, layer_key);
|
||||
slot != 0;
|
||||
slot = MD_MapScan(slot->next, layer_key))
|
||||
{
|
||||
MD_Node *gen = (MD_Node *)slot->val;
|
||||
MD_Node *tag = MD_TagFromString(gen, cg_tbl_tag__table_gen_data, 0);
|
||||
MD_Node *data_table_type_node = tag->first_child;
|
||||
MD_String8 data_table_type = data_table_type_node->string;
|
||||
MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen);
|
||||
|
||||
MD_String8 h_decl_specifier = MD_S8Lit("extern");
|
||||
|
||||
CG_FilePair f = CG_FilePairFromNode(gen);
|
||||
fprintf(f.h, "%.*s %.*s %.*s[%" PRIu64 "];\n\n", MD_S8VArg(h_decl_specifier), MD_S8VArg(data_table_type), MD_S8VArg(gen->string), gen_strings.node_count);
|
||||
|
||||
fprintf(f.c, "%.*s %.*s[%" PRIu64 "] =\n{\n", MD_S8VArg(data_table_type), MD_S8VArg(gen->string), gen_strings.node_count);
|
||||
MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") };
|
||||
MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join);
|
||||
fprintf(f.c, "%.*s", MD_S8VArg(gen_string));
|
||||
fprintf(f.c, "\n};\n");
|
||||
fprintf(f.c, "\n");
|
||||
}
|
||||
|
||||
//- rjf: generate all general generations
|
||||
for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen, layer_key);
|
||||
slot != 0;
|
||||
slot = MD_MapScan(slot->next, layer_key))
|
||||
{
|
||||
MD_Node *gen = (MD_Node *)slot->val;
|
||||
CG_FilePair f = CG_FilePairFromNode(gen);
|
||||
FILE *file = CG_FileFromNodePair(gen, &f);
|
||||
MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen);
|
||||
MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") };
|
||||
MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join);
|
||||
fprintf(file, "%.*s", MD_S8VArg(gen_string));
|
||||
fprintf(file, "\n\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
#ifndef METAPROGRAM_TABLE_H
|
||||
#define METAPROGRAM_TABLE_H
|
||||
|
||||
typedef enum CG_TableOp
|
||||
{
|
||||
CG_TableOp_Null,
|
||||
|
||||
CG_TableOp_BeginStringOps,
|
||||
CG_TableOp_Dot,
|
||||
CG_TableOp_Bump,
|
||||
CG_TableOp_CheckIfTrue,
|
||||
CG_TableOp_Concat,
|
||||
CG_TableOp_EndStringOps,
|
||||
|
||||
CG_TableOp_BeginNumericOps,
|
||||
CG_TableOp_Equal,
|
||||
CG_TableOp_IsNotEqual,
|
||||
CG_TableOp_BooleanAnd,
|
||||
CG_TableOp_BooleanOr,
|
||||
CG_TableOp_EndNumericOps,
|
||||
|
||||
CG_TableOp_COUNT
|
||||
}
|
||||
CG_TableOp;
|
||||
|
||||
typedef struct CG_NodeArray CG_NodeArray;
|
||||
struct CG_NodeArray
|
||||
{
|
||||
MD_u64 count;
|
||||
MD_Node **v;
|
||||
};
|
||||
|
||||
typedef struct CG_NodeGrid CG_NodeGrid;
|
||||
struct CG_NodeGrid
|
||||
{
|
||||
CG_NodeArray cells;
|
||||
CG_NodeArray row_parents;
|
||||
};
|
||||
|
||||
typedef enum CG_ColumnKind
|
||||
{
|
||||
CG_ColumnKind_Default,
|
||||
CG_ColumnKind_CheckForTag,
|
||||
CG_ColumnKind_COUNT
|
||||
}
|
||||
CG_ColumnKind;
|
||||
|
||||
typedef struct CG_ColumnDesc CG_ColumnDesc;
|
||||
struct CG_ColumnDesc
|
||||
{
|
||||
CG_ColumnKind kind;
|
||||
MD_String8 name;
|
||||
MD_String8 tag_string;
|
||||
MD_String8 default_value;
|
||||
};
|
||||
|
||||
typedef struct CG_TableHeader CG_TableHeader;
|
||||
struct CG_TableHeader
|
||||
{
|
||||
MD_u64 column_count;
|
||||
CG_ColumnDesc *column_descs;
|
||||
};
|
||||
|
||||
typedef struct CG_ExpandIter CG_ExpandIter;
|
||||
struct CG_ExpandIter
|
||||
{
|
||||
CG_ExpandIter *next;
|
||||
CG_NodeGrid *grid;
|
||||
CG_TableHeader *header;
|
||||
MD_String8 label;
|
||||
MD_u64 idx;
|
||||
MD_u64 count;
|
||||
};
|
||||
|
||||
typedef struct CG_ExpandInfo CG_ExpandInfo;
|
||||
struct CG_ExpandInfo
|
||||
{
|
||||
MD_String8 strexpr;
|
||||
CG_ExpandIter *first_expand_iter;
|
||||
MD_ExprOprTable expr_op_table;
|
||||
};
|
||||
|
||||
static CG_NodeArray CG_NodeArrayMake(MD_u64 count);
|
||||
static CG_NodeGrid CG_GridFromNode(MD_Node *node);
|
||||
static CG_TableHeader CG_TableHeaderFromTag(MD_Node *tag);
|
||||
static MD_u64 CG_RowChildIndexFromColumnName(CG_TableHeader *header, MD_String8 column_name);
|
||||
static MD_i64 CG_TableExprEvaluate_Numeric(CG_ExpandInfo *info, MD_Expr *expr);
|
||||
static void CG_TableExprEvaluate_String(CG_ExpandInfo *info, MD_Expr *expr, MD_String8List *out);
|
||||
static void CG_LoopExpansionDimension(CG_ExpandIter *it, CG_ExpandInfo *info, MD_String8List *out);
|
||||
static MD_String8List CG_GenStringListFromNode(MD_ExprOprTable expr_op_table, MD_Node *gen);
|
||||
static void CG_TBL_Generate(MD_Node *file_list);
|
||||
|
||||
#endif // METAPROGRAM_TABLE_H
|
|
@ -0,0 +1,744 @@
|
|||
/* ========================================================================
|
||||
|
||||
Meow - A Fast Non-cryptographic Hash
|
||||
(C) Copyright 2018-2019 by Molly Rocket, Inc. (https://mollyrocket.com)
|
||||
|
||||
See https://mollyrocket.com/meowhash for details.
|
||||
|
||||
========================================================================
|
||||
|
||||
zlib License
|
||||
|
||||
(C) Copyright 2018-2019 Molly Rocket, Inc.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
========================================================================
|
||||
|
||||
FAQ
|
||||
|
||||
Q: What is it?
|
||||
|
||||
A: Meow is a 128-bit Level 3 hash taking 128 bytes of seed. It operates
|
||||
at very high speeds on x64 processors, and potentially other processors
|
||||
that provide accelerated AES instructions.
|
||||
|
||||
Q: What is it GOOD for?
|
||||
|
||||
A: Quickly hashing any amount of data for comparison purposes such as
|
||||
block deduplication or change detection. It is fast on all buffer
|
||||
sizes, and can generally be used anywhere you need fast Level 3
|
||||
hashing without worrying about how big or small the inputs tend to be.
|
||||
|
||||
However, substantial speed improvements could be made over Meow
|
||||
if you either a) know you are always hashing an exact, small number of bytes,
|
||||
or b) can always supply a small number of bytes in a buffer padded to some
|
||||
fixed multiple of 16.
|
||||
|
||||
Q: What is it BAD for?
|
||||
|
||||
A: Anything requiring Level 4 or Level 5 security guarantees (see
|
||||
http://nohatcoder.dk/2019-05-19-1.html#level3). Also, note that
|
||||
Meow is a new hash and has not had the extensive community
|
||||
cryptanalysis necessary to ensure that it is not breakable down to
|
||||
a lower level of hash, so you must do your due diligence in
|
||||
deciding when and where to use Meow instead of a slower but
|
||||
more extensively studied existing hash. We have tried to design
|
||||
it to provide Level 3 security, but the possibility of the hash
|
||||
being broken in the future always exists.
|
||||
|
||||
Q: Why is it called the "Meow hash"?
|
||||
|
||||
A: It is named after a character in Meow the Infinite
|
||||
(https://meowtheinfinite.com)
|
||||
|
||||
Q: Who wrote it?
|
||||
|
||||
A: The final Meow Hash was created as a collaboration between
|
||||
JACOB CHRISTIAN MUNCH-ANDERSEN (https://twitter.com/nohatcoder) and
|
||||
CASEY MURATORI (https://caseymuratori.com). Casey wrote the original
|
||||
implementation for use in processing large-footprint assets for the
|
||||
game 1935 (https://molly1935.com). Jacob was the first to analyze
|
||||
that implementation and determine the adversarial bit strength, which
|
||||
was weaker than they would have liked.
|
||||
|
||||
Following that, the two collaborated to figure out how the hash
|
||||
could be strengthened without reducing Meow's 16 bytes/cycle
|
||||
maximum theoretical throughput. Jacob created the hash candidates
|
||||
and Casey did the performance validation. After a long and
|
||||
exhaustive effort, Jacob found the unaligned aes/add/xor formulation
|
||||
that forms the current Meow hash core.
|
||||
|
||||
A number of valuable additions to Meow Hash were also contributed
|
||||
by other great folks along the way:
|
||||
|
||||
JEFF ROBERTS (https://radgametools.com) provided a super slick
|
||||
way to handle the residual end-of-buffer bytes that dramatically
|
||||
improved Meow's small hash performance.
|
||||
|
||||
MARTINS MOZEIKO (https://matrins.ninja) ported Meow to ARM and
|
||||
ANSI-C, and added the proper preprocessor dressing for clean
|
||||
compilation on a variety of compiler configurations.
|
||||
|
||||
FABIAN GIESEN (https://fgiesen.wordpress.com) analyzed many
|
||||
performance oddities that came up during development, and
|
||||
helped get the benchmarking working properly across a number
|
||||
of platforms.
|
||||
|
||||
ARAS PRANCKEVICIUS (https://aras-p.info) provided the allocation
|
||||
shim for compilation on Mac OS X.
|
||||
|
||||
======================================================================== */
|
||||
|
||||
//
|
||||
// IMPORTANT(casey): We are currently evaluating this hash construction as
|
||||
// the final one for Meow Hash. If you find a way to produce collisions
|
||||
// that should not be possible with a Level 3 hash, find significant performance
|
||||
// problems, or see any bugs in this version, please be sure to report them
|
||||
// to the Meow Hash GitHub as soon as possible. We would like to know as
|
||||
// much as we can about the robustness and performance before committing to
|
||||
// it as the final construction.
|
||||
//
|
||||
|
||||
#if !defined(MEOW_HASH_X64_AESNI_H)
|
||||
|
||||
#define MEOW_HASH_VERSION 5
|
||||
#define MEOW_HASH_VERSION_NAME "0.5/calico"
|
||||
|
||||
#if !defined(meow_u8)
|
||||
|
||||
#if _MSC_VER
|
||||
#if !defined(__clang__)
|
||||
#define INSTRUCTION_REORDER_BARRIER _ReadWriteBarrier()
|
||||
#else
|
||||
#endif
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#define meow_u8 char unsigned
|
||||
#define meow_u64 long long unsigned
|
||||
#define meow_u128 __m128i
|
||||
|
||||
#if __x86_64__ || _M_AMD64
|
||||
#define meow_umm long long unsigned
|
||||
#define MeowU64From(A, I) (_mm_extract_epi64((A), (I)))
|
||||
#elif __i386__ || _M_IX86
|
||||
#define meow_umm int unsigned
|
||||
#define MeowU64From(A, I) (*(meow_u64 *)&(A))
|
||||
#else
|
||||
#error Cannot determine architecture to use!
|
||||
#endif
|
||||
|
||||
#define MeowU32From(A, I) (_mm_extract_epi32((A), (I)))
|
||||
#define MeowHashesAreEqual(A, B) (_mm_movemask_epi8(_mm_cmpeq_epi8((A), (B))) == 0xFFFF)
|
||||
|
||||
#if !defined INSTRUCTION_REORDER_BARRIER
|
||||
#define INSTRUCTION_REORDER_BARRIER
|
||||
#endif
|
||||
|
||||
#if !defined MEOW_PAGESIZE
|
||||
#define MEOW_PAGESIZE 4096
|
||||
#endif
|
||||
|
||||
#if !defined MEOW_PREFETCH
|
||||
#define MEOW_PREFETCH 4096
|
||||
#endif
|
||||
|
||||
#if !defined MEOW_PREFETCH_LIMIT
|
||||
#define MEOW_PREFETCH_LIMIT 0x3ff
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define prefetcht0(A) _mm_prefetch((char *)(A), _MM_HINT_T0)
|
||||
#define movdqu(A, B) A = _mm_loadu_si128((__m128i *)(B))
|
||||
#define movdqu_mem(A, B) _mm_storeu_si128((__m128i *)(A), B)
|
||||
#define movq(A, B) A = _mm_set_epi64x(0, B);
|
||||
#define aesdec(A, B) A = _mm_aesdec_si128(A, B)
|
||||
#define pshufb(A, B) A = _mm_shuffle_epi8(A, B)
|
||||
#define pxor(A, B) A = _mm_xor_si128(A, B)
|
||||
#define paddq(A, B) A = _mm_add_epi64(A, B)
|
||||
#define pand(A, B) A = _mm_and_si128(A, B)
|
||||
#define palignr(A, B, i) A = _mm_alignr_epi8(A, B, i)
|
||||
#define pxor_clear(A, B) A = _mm_setzero_si128(); // NOTE(casey): pxor_clear is a nonsense thing that is only here because compilers don't detect xor(a, a) is clearing a :(
|
||||
|
||||
#define MEOW_MIX_REG(r1, r2, r3, r4, r5, i1, i2, i3, i4) \
|
||||
aesdec(r1, r2); \
|
||||
INSTRUCTION_REORDER_BARRIER; \
|
||||
paddq(r3, i1); \
|
||||
pxor(r2, i2); \
|
||||
aesdec(r2, r4); \
|
||||
INSTRUCTION_REORDER_BARRIER; \
|
||||
paddq(r5, i3); \
|
||||
pxor(r4, i4);
|
||||
|
||||
#define MEOW_MIX(r1, r2, r3, r4, r5, ptr) \
|
||||
MEOW_MIX_REG(r1, r2, r3, r4, r5, _mm_loadu_si128( (__m128i *) ((ptr) + 15) ), _mm_loadu_si128( (__m128i *) ((ptr) + 0) ), _mm_loadu_si128( (__m128i *) ((ptr) + 1) ), _mm_loadu_si128( (__m128i *) ((ptr) + 16) ))
|
||||
|
||||
#define MEOW_SHUFFLE(r1, r2, r3, r4, r5, r6) \
|
||||
aesdec(r1, r4); \
|
||||
paddq(r2, r5); \
|
||||
pxor(r4, r6); \
|
||||
aesdec(r4, r2); \
|
||||
paddq(r5, r6); \
|
||||
pxor(r2, r3)
|
||||
|
||||
#if MEOW_DUMP
|
||||
struct meow_dump
|
||||
{
|
||||
meow_u128 xmm[8];
|
||||
void *Ptr;
|
||||
char const *Title;
|
||||
};
|
||||
extern "C" meow_dump *MeowDumpTo;
|
||||
meow_dump *MeowDumpTo;
|
||||
#define MEOW_DUMP_STATE(T, xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, ptr) \
|
||||
if(MeowDumpTo) \
|
||||
{ \
|
||||
MeowDumpTo->xmm[0] = xmm0; \
|
||||
MeowDumpTo->xmm[1] = xmm1; \
|
||||
MeowDumpTo->xmm[2] = xmm2; \
|
||||
MeowDumpTo->xmm[3] = xmm3; \
|
||||
MeowDumpTo->xmm[4] = xmm4; \
|
||||
MeowDumpTo->xmm[5] = xmm5; \
|
||||
MeowDumpTo->xmm[6] = xmm6; \
|
||||
MeowDumpTo->xmm[7] = xmm7; \
|
||||
MeowDumpTo->Ptr = ptr; \
|
||||
MeowDumpTo->Title = T; \
|
||||
++MeowDumpTo; \
|
||||
}
|
||||
#else
|
||||
#define MEOW_DUMP_STATE(...)
|
||||
#endif
|
||||
|
||||
static meow_u8 MeowShiftAdjust[32] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
|
||||
static meow_u8 MeowMaskLen[32] = {255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
|
||||
|
||||
// NOTE(casey): The default seed is now a "nothing-up-our-sleeves" number for good measure. You may verify that it is just an encoding of Pi.
|
||||
static meow_u8 MeowDefaultSeed[128] =
|
||||
{
|
||||
0x32, 0x43, 0xF6, 0xA8, 0x88, 0x5A, 0x30, 0x8D,
|
||||
0x31, 0x31, 0x98, 0xA2, 0xE0, 0x37, 0x07, 0x34,
|
||||
0x4A, 0x40, 0x93, 0x82, 0x22, 0x99, 0xF3, 0x1D,
|
||||
0x00, 0x82, 0xEF, 0xA9, 0x8E, 0xC4, 0xE6, 0xC8,
|
||||
0x94, 0x52, 0x82, 0x1E, 0x63, 0x8D, 0x01, 0x37,
|
||||
0x7B, 0xE5, 0x46, 0x6C, 0xF3, 0x4E, 0x90, 0xC6,
|
||||
0xCC, 0x0A, 0xC2, 0x9B, 0x7C, 0x97, 0xC5, 0x0D,
|
||||
0xD3, 0xF8, 0x4D, 0x5B, 0x5B, 0x54, 0x70, 0x91,
|
||||
0x79, 0x21, 0x6D, 0x5D, 0x98, 0x97, 0x9F, 0xB1,
|
||||
0xBD, 0x13, 0x10, 0xBA, 0x69, 0x8D, 0xFB, 0x5A,
|
||||
0xC2, 0xFF, 0xD7, 0x2D, 0xBD, 0x01, 0xAD, 0xFB,
|
||||
0x7B, 0x8E, 0x1A, 0xFE, 0xD6, 0xA2, 0x67, 0xE9,
|
||||
0x6B, 0xA7, 0xC9, 0x04, 0x5F, 0x12, 0xC7, 0xF9,
|
||||
0x92, 0x4A, 0x19, 0x94, 0x7B, 0x39, 0x16, 0xCF,
|
||||
0x70, 0x80, 0x1F, 0x2E, 0x28, 0x58, 0xEF, 0xC1,
|
||||
0x66, 0x36, 0x92, 0x0D, 0x87, 0x15, 0x74, 0xE6
|
||||
};
|
||||
|
||||
//
|
||||
// NOTE(casey): Single block version
|
||||
//
|
||||
|
||||
static meow_u128
|
||||
MeowHash(void *Seed128Init, meow_umm Len, void *SourceInit)
|
||||
{
|
||||
meow_u128 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; // NOTE(casey): xmm0-xmm7 are the hash accumulation lanes
|
||||
meow_u128 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15; // NOTE(casey): xmm8-xmm15 hold values to be appended (residual, length)
|
||||
|
||||
meow_u8 *rax = (meow_u8 *)SourceInit;
|
||||
meow_u8 *rcx = (meow_u8 *)Seed128Init;
|
||||
|
||||
//
|
||||
// NOTE(casey): Seed the eight hash registers
|
||||
//
|
||||
|
||||
movdqu(xmm0, rcx + 0x00);
|
||||
movdqu(xmm1, rcx + 0x10);
|
||||
movdqu(xmm2, rcx + 0x20);
|
||||
movdqu(xmm3, rcx + 0x30);
|
||||
|
||||
movdqu(xmm4, rcx + 0x40);
|
||||
movdqu(xmm5, rcx + 0x50);
|
||||
movdqu(xmm6, rcx + 0x60);
|
||||
movdqu(xmm7, rcx + 0x70);
|
||||
|
||||
MEOW_DUMP_STATE("Seed", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
//
|
||||
// NOTE(casey): Hash all full 256-byte blocks
|
||||
//
|
||||
|
||||
meow_umm BlockCount = (Len >> 8);
|
||||
if(BlockCount > MEOW_PREFETCH_LIMIT)
|
||||
{
|
||||
// NOTE(casey): For large input, modern Intel x64's can't hit full speed without prefetching, so we use this loop
|
||||
while(BlockCount--)
|
||||
{
|
||||
prefetcht0(rax + MEOW_PREFETCH + 0x00);
|
||||
prefetcht0(rax + MEOW_PREFETCH + 0x40);
|
||||
prefetcht0(rax + MEOW_PREFETCH + 0x80);
|
||||
prefetcht0(rax + MEOW_PREFETCH + 0xc0);
|
||||
|
||||
MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0x00);
|
||||
MEOW_MIX(xmm1,xmm5,xmm7,xmm2,xmm3, rax + 0x20);
|
||||
MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x40);
|
||||
MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x60);
|
||||
MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x80);
|
||||
MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0xa0);
|
||||
MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0xc0);
|
||||
MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xe0);
|
||||
|
||||
rax += 0x100;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE(casey): For small input, modern Intel x64's can't hit full speed _with_ prefetching (because of port pressure), so we use this loop.
|
||||
while(BlockCount--)
|
||||
{
|
||||
MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0x00);
|
||||
MEOW_MIX(xmm1,xmm5,xmm7,xmm2,xmm3, rax + 0x20);
|
||||
MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x40);
|
||||
MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x60);
|
||||
MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x80);
|
||||
MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0xa0);
|
||||
MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0xc0);
|
||||
MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xe0);
|
||||
|
||||
rax += 0x100;
|
||||
}
|
||||
}
|
||||
|
||||
MEOW_DUMP_STATE("PostBlocks", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
//
|
||||
// NOTE(casey): Load any less-than-32-byte residual
|
||||
//
|
||||
|
||||
pxor_clear(xmm9, xmm9);
|
||||
pxor_clear(xmm11, xmm11);
|
||||
|
||||
//
|
||||
// TODO(casey): I need to put more thought into how the end-of-buffer stuff is actually working out here,
|
||||
// because I _think_ it may be possible to remove the first branch (on Len8) and let the mask zero out the
|
||||
// result, but it would take a little thought to make sure it couldn't read off the end of the buffer due
|
||||
// to the & 0xf on the align computation.
|
||||
//
|
||||
|
||||
// NOTE(casey): First, we have to load the part that is _not_ 16-byte aligned
|
||||
meow_u8 *Last = (meow_u8 *)SourceInit + (Len & ~0xf);
|
||||
int unsigned Len8 = (Len & 0xf);
|
||||
if(Len8)
|
||||
{
|
||||
// NOTE(casey): Load the mask early
|
||||
movdqu(xmm8, &MeowMaskLen[0x10 - Len8]);
|
||||
|
||||
meow_u8 *LastOk = (meow_u8*)((((meow_umm)(((meow_u8 *)SourceInit)+Len - 1)) | (MEOW_PAGESIZE - 1)) - 16);
|
||||
int Align = (Last > LastOk) ? ((int)(meow_umm)Last) & 0xf : 0;
|
||||
movdqu(xmm10, &MeowShiftAdjust[Align]);
|
||||
movdqu(xmm9, Last - Align);
|
||||
pshufb(xmm9, xmm10);
|
||||
|
||||
// NOTE(jeffr): and off the extra bytes
|
||||
pand(xmm9, xmm8);
|
||||
}
|
||||
|
||||
// NOTE(casey): Next, we have to load the part that _is_ 16-byte aligned
|
||||
if(Len & 0x10)
|
||||
{
|
||||
xmm11 = xmm9;
|
||||
movdqu(xmm9, Last - 0x10);
|
||||
}
|
||||
|
||||
//
|
||||
// NOTE(casey): Construct the residual and length injests
|
||||
//
|
||||
|
||||
xmm8 = xmm9;
|
||||
xmm10 = xmm9;
|
||||
palignr(xmm8, xmm11, 15);
|
||||
palignr(xmm10, xmm11, 1);
|
||||
|
||||
// NOTE(casey): We have room for a 128-bit nonce and a 64-bit none here, but
|
||||
// the decision was made to leave them zero'd so as not to confuse people
|
||||
// about hwo to use them or what security implications they had.
|
||||
pxor_clear(xmm12, xmm12);
|
||||
pxor_clear(xmm13, xmm13);
|
||||
pxor_clear(xmm14, xmm14);
|
||||
movq(xmm15, Len);
|
||||
palignr(xmm12, xmm15, 15);
|
||||
palignr(xmm14, xmm15, 1);
|
||||
|
||||
MEOW_DUMP_STATE("Residuals", xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, 0);
|
||||
|
||||
// NOTE(casey): To maintain the mix-down pattern, we always Meow Mix the less-than-32-byte residual, even if it was empty
|
||||
MEOW_MIX_REG(xmm0, xmm4, xmm6, xmm1, xmm2, xmm8, xmm9, xmm10, xmm11);
|
||||
|
||||
// NOTE(casey): Append the length, to avoid problems with our 32-byte padding
|
||||
MEOW_MIX_REG(xmm1, xmm5, xmm7, xmm2, xmm3, xmm12, xmm13, xmm14, xmm15);
|
||||
|
||||
MEOW_DUMP_STATE("PostAppend", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
//
|
||||
// NOTE(casey): Hash all full 32-byte blocks
|
||||
//
|
||||
int unsigned LaneCount = (Len >> 5) & 0x7;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x00); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x20); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x40); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0x60); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0x80); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xa0); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0xc0); --LaneCount;
|
||||
|
||||
//
|
||||
// NOTE(casey): Mix the eight lanes down to one 128-bit hash
|
||||
//
|
||||
|
||||
MixDown:
|
||||
|
||||
MEOW_DUMP_STATE("PostLanes", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
MEOW_SHUFFLE(xmm0, xmm1, xmm2, xmm4, xmm5, xmm6);
|
||||
MEOW_SHUFFLE(xmm1, xmm2, xmm3, xmm5, xmm6, xmm7);
|
||||
MEOW_SHUFFLE(xmm2, xmm3, xmm4, xmm6, xmm7, xmm0);
|
||||
MEOW_SHUFFLE(xmm3, xmm4, xmm5, xmm7, xmm0, xmm1);
|
||||
MEOW_SHUFFLE(xmm4, xmm5, xmm6, xmm0, xmm1, xmm2);
|
||||
MEOW_SHUFFLE(xmm5, xmm6, xmm7, xmm1, xmm2, xmm3);
|
||||
MEOW_SHUFFLE(xmm6, xmm7, xmm0, xmm2, xmm3, xmm4);
|
||||
MEOW_SHUFFLE(xmm7, xmm0, xmm1, xmm3, xmm4, xmm5);
|
||||
MEOW_SHUFFLE(xmm0, xmm1, xmm2, xmm4, xmm5, xmm6);
|
||||
MEOW_SHUFFLE(xmm1, xmm2, xmm3, xmm5, xmm6, xmm7);
|
||||
MEOW_SHUFFLE(xmm2, xmm3, xmm4, xmm6, xmm7, xmm0);
|
||||
MEOW_SHUFFLE(xmm3, xmm4, xmm5, xmm7, xmm0, xmm1);
|
||||
|
||||
MEOW_DUMP_STATE("PostMix", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
paddq(xmm0, xmm2);
|
||||
paddq(xmm1, xmm3);
|
||||
paddq(xmm4, xmm6);
|
||||
paddq(xmm5, xmm7);
|
||||
pxor(xmm0, xmm1);
|
||||
pxor(xmm4, xmm5);
|
||||
paddq(xmm0, xmm4);
|
||||
|
||||
MEOW_DUMP_STATE("PostFold", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
return(xmm0);
|
||||
}
|
||||
|
||||
//
|
||||
// NOTE(casey): Streaming construction
|
||||
//
|
||||
|
||||
typedef struct meow_state
|
||||
{
|
||||
meow_u128 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7;
|
||||
meow_u64 TotalLengthInBytes;
|
||||
|
||||
int unsigned BufferLen;
|
||||
|
||||
meow_u8 Buffer[256];
|
||||
meow_u128 Pad[2]; // NOTE(casey): So we know we can over-read Buffer as necessary
|
||||
} meow_state;
|
||||
|
||||
static void
|
||||
MeowBegin(meow_state *State, void *Seed128)
|
||||
{
|
||||
meow_u8 *rcx = (meow_u8 *)Seed128;
|
||||
|
||||
movdqu(State->xmm0, rcx + 0x00);
|
||||
movdqu(State->xmm1, rcx + 0x10);
|
||||
movdqu(State->xmm2, rcx + 0x20);
|
||||
movdqu(State->xmm3, rcx + 0x30);
|
||||
movdqu(State->xmm4, rcx + 0x40);
|
||||
movdqu(State->xmm5, rcx + 0x50);
|
||||
movdqu(State->xmm6, rcx + 0x60);
|
||||
movdqu(State->xmm7, rcx + 0x70);
|
||||
|
||||
MEOW_DUMP_STATE("Seed", State->xmm0, State->xmm1, State->xmm2, State->xmm3, State->xmm4, State->xmm5, State->xmm6, State->xmm7, 0);
|
||||
|
||||
State->BufferLen = 0;
|
||||
State->TotalLengthInBytes = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
MeowAbsorbBlocks(meow_state *State, meow_umm BlockCount, meow_u8 *rax)
|
||||
{
|
||||
meow_u128 xmm0 = State->xmm0;
|
||||
meow_u128 xmm1 = State->xmm1;
|
||||
meow_u128 xmm2 = State->xmm2;
|
||||
meow_u128 xmm3 = State->xmm3;
|
||||
meow_u128 xmm4 = State->xmm4;
|
||||
meow_u128 xmm5 = State->xmm5;
|
||||
meow_u128 xmm6 = State->xmm6;
|
||||
meow_u128 xmm7 = State->xmm7;
|
||||
|
||||
if(BlockCount > MEOW_PREFETCH_LIMIT)
|
||||
{
|
||||
while(BlockCount--)
|
||||
{
|
||||
prefetcht0(rax + MEOW_PREFETCH + 0x00);
|
||||
prefetcht0(rax + MEOW_PREFETCH + 0x40);
|
||||
prefetcht0(rax + MEOW_PREFETCH + 0x80);
|
||||
prefetcht0(rax + MEOW_PREFETCH + 0xc0);
|
||||
|
||||
MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0x00);
|
||||
MEOW_MIX(xmm1,xmm5,xmm7,xmm2,xmm3, rax + 0x20);
|
||||
MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x40);
|
||||
MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x60);
|
||||
MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x80);
|
||||
MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0xa0);
|
||||
MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0xc0);
|
||||
MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xe0);
|
||||
|
||||
rax += 0x100;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(BlockCount--)
|
||||
{
|
||||
MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0x00);
|
||||
MEOW_MIX(xmm1,xmm5,xmm7,xmm2,xmm3, rax + 0x20);
|
||||
MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x40);
|
||||
MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x60);
|
||||
MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x80);
|
||||
MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0xa0);
|
||||
MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0xc0);
|
||||
MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xe0);
|
||||
|
||||
rax += 0x100;
|
||||
}
|
||||
}
|
||||
|
||||
State->xmm0 = xmm0;
|
||||
State->xmm1 = xmm1;
|
||||
State->xmm2 = xmm2;
|
||||
State->xmm3 = xmm3;
|
||||
State->xmm4 = xmm4;
|
||||
State->xmm5 = xmm5;
|
||||
State->xmm6 = xmm6;
|
||||
State->xmm7 = xmm7;
|
||||
}
|
||||
|
||||
static void
|
||||
MeowAbsorb(meow_state *State, meow_umm Len, void *SourceInit)
|
||||
{
|
||||
State->TotalLengthInBytes += Len;
|
||||
meow_u8 *Source = (meow_u8 *)SourceInit;
|
||||
|
||||
// NOTE(casey): Handle any buffered residual
|
||||
if(State->BufferLen)
|
||||
{
|
||||
int unsigned Fill = (sizeof(State->Buffer) - State->BufferLen);
|
||||
if(Fill > Len)
|
||||
{
|
||||
Fill = (int unsigned)Len;
|
||||
}
|
||||
|
||||
Len -= Fill;
|
||||
while(Fill--)
|
||||
{
|
||||
State->Buffer[State->BufferLen++] = *Source++;
|
||||
}
|
||||
|
||||
if(State->BufferLen == sizeof(State->Buffer))
|
||||
{
|
||||
MeowAbsorbBlocks(State, 1, State->Buffer);
|
||||
State->BufferLen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(casey): Handle any full blocks
|
||||
meow_u64 BlockCount = (Len >> 8);
|
||||
meow_u64 Advance = (BlockCount << 8);
|
||||
MeowAbsorbBlocks(State, BlockCount, Source);
|
||||
|
||||
Len -= Advance;
|
||||
Source += Advance;
|
||||
|
||||
// NOTE(casey): Store residual
|
||||
while(Len--)
|
||||
{
|
||||
State->Buffer[State->BufferLen++] = *Source++;
|
||||
}
|
||||
}
|
||||
|
||||
static meow_u128
|
||||
MeowEnd(meow_state *State, meow_u8 *Store128)
|
||||
{
|
||||
meow_umm Len = State->TotalLengthInBytes;
|
||||
|
||||
meow_u128 xmm0 = State->xmm0;
|
||||
meow_u128 xmm1 = State->xmm1;
|
||||
meow_u128 xmm2 = State->xmm2;
|
||||
meow_u128 xmm3 = State->xmm3;
|
||||
meow_u128 xmm4 = State->xmm4;
|
||||
meow_u128 xmm5 = State->xmm5;
|
||||
meow_u128 xmm6 = State->xmm6;
|
||||
meow_u128 xmm7 = State->xmm7;
|
||||
|
||||
meow_u128 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15;
|
||||
|
||||
meow_u8 *rax = State->Buffer;
|
||||
|
||||
pxor_clear(xmm9, xmm9);
|
||||
pxor_clear(xmm11, xmm11);
|
||||
|
||||
meow_u8 *Last = (meow_u8 *)rax + (Len & 0xf0);
|
||||
int unsigned Len8 = (Len & 0xf);
|
||||
if(Len8)
|
||||
{
|
||||
movdqu(xmm8, &MeowMaskLen[0x10 - Len8]);
|
||||
movdqu(xmm9, Last);
|
||||
pand(xmm9, xmm8);
|
||||
}
|
||||
|
||||
if(Len & 0x10)
|
||||
{
|
||||
xmm11 = xmm9;
|
||||
movdqu(xmm9, Last - 0x10);
|
||||
}
|
||||
|
||||
xmm8 = xmm9;
|
||||
xmm10 = xmm9;
|
||||
palignr(xmm8, xmm11, 15);
|
||||
palignr(xmm10, xmm11, 1);
|
||||
|
||||
pxor_clear(xmm12, xmm12);
|
||||
pxor_clear(xmm13, xmm13);
|
||||
pxor_clear(xmm14, xmm14);
|
||||
movq(xmm15, Len);
|
||||
palignr(xmm12, xmm15, 15);
|
||||
palignr(xmm14, xmm15, 1);
|
||||
|
||||
MEOW_DUMP_STATE("PostBlocks", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
MEOW_DUMP_STATE("Residuals", xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, 0);
|
||||
|
||||
// NOTE(casey): To maintain the mix-down pattern, we always Meow Mix the less-than-32-byte residual, even if it was empty
|
||||
MEOW_MIX_REG(xmm0, xmm4, xmm6, xmm1, xmm2, xmm8, xmm9, xmm10, xmm11);
|
||||
|
||||
// NOTE(casey): Append the length, to avoid problems with our 32-byte padding
|
||||
MEOW_MIX_REG(xmm1, xmm5, xmm7, xmm2, xmm3, xmm12, xmm13, xmm14, xmm15);
|
||||
|
||||
MEOW_DUMP_STATE("PostAppend", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
//
|
||||
// NOTE(casey): Hash all full 32-byte blocks
|
||||
//
|
||||
int unsigned LaneCount = (Len >> 5) & 0x7;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm2,xmm6,xmm0,xmm3,xmm4, rax + 0x00); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm3,xmm7,xmm1,xmm4,xmm5, rax + 0x20); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm4,xmm0,xmm2,xmm5,xmm6, rax + 0x40); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm5,xmm1,xmm3,xmm6,xmm7, rax + 0x60); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm6,xmm2,xmm4,xmm7,xmm0, rax + 0x80); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm7,xmm3,xmm5,xmm0,xmm1, rax + 0xa0); --LaneCount;
|
||||
if(LaneCount == 0) goto MixDown; MEOW_MIX(xmm0,xmm4,xmm6,xmm1,xmm2, rax + 0xc0); --LaneCount;
|
||||
|
||||
//
|
||||
// NOTE(casey): Mix the eight lanes down to one 128-bit hash
|
||||
//
|
||||
|
||||
MixDown:
|
||||
|
||||
MEOW_DUMP_STATE("PostLanes", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
MEOW_SHUFFLE(xmm0, xmm1, xmm2, xmm4, xmm5, xmm6);
|
||||
MEOW_SHUFFLE(xmm1, xmm2, xmm3, xmm5, xmm6, xmm7);
|
||||
MEOW_SHUFFLE(xmm2, xmm3, xmm4, xmm6, xmm7, xmm0);
|
||||
MEOW_SHUFFLE(xmm3, xmm4, xmm5, xmm7, xmm0, xmm1);
|
||||
MEOW_SHUFFLE(xmm4, xmm5, xmm6, xmm0, xmm1, xmm2);
|
||||
MEOW_SHUFFLE(xmm5, xmm6, xmm7, xmm1, xmm2, xmm3);
|
||||
MEOW_SHUFFLE(xmm6, xmm7, xmm0, xmm2, xmm3, xmm4);
|
||||
MEOW_SHUFFLE(xmm7, xmm0, xmm1, xmm3, xmm4, xmm5);
|
||||
MEOW_SHUFFLE(xmm0, xmm1, xmm2, xmm4, xmm5, xmm6);
|
||||
MEOW_SHUFFLE(xmm1, xmm2, xmm3, xmm5, xmm6, xmm7);
|
||||
MEOW_SHUFFLE(xmm2, xmm3, xmm4, xmm6, xmm7, xmm0);
|
||||
MEOW_SHUFFLE(xmm3, xmm4, xmm5, xmm7, xmm0, xmm1);
|
||||
|
||||
MEOW_DUMP_STATE("PostMix", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
if(Store128)
|
||||
{
|
||||
movdqu_mem(Store128 + 0x00, xmm0);
|
||||
movdqu_mem(Store128 + 0x10, xmm1);
|
||||
movdqu_mem(Store128 + 0x20, xmm2);
|
||||
movdqu_mem(Store128 + 0x30, xmm3);
|
||||
movdqu_mem(Store128 + 0x40, xmm4);
|
||||
movdqu_mem(Store128 + 0x50, xmm5);
|
||||
movdqu_mem(Store128 + 0x60, xmm6);
|
||||
movdqu_mem(Store128 + 0x70, xmm7);
|
||||
}
|
||||
|
||||
paddq(xmm0, xmm2);
|
||||
paddq(xmm1, xmm3);
|
||||
paddq(xmm4, xmm6);
|
||||
paddq(xmm5, xmm7);
|
||||
pxor(xmm0, xmm1);
|
||||
pxor(xmm4, xmm5);
|
||||
paddq(xmm0, xmm4);
|
||||
|
||||
MEOW_DUMP_STATE("PostFold", xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 0);
|
||||
|
||||
return(xmm0);
|
||||
}
|
||||
|
||||
#undef INSTRUCTION_REORDER_BARRIER
|
||||
#undef prefetcht0
|
||||
#undef movdqu
|
||||
#undef movdqu_mem
|
||||
#undef movq
|
||||
#undef aesdec
|
||||
#undef pshufb
|
||||
#undef pxor
|
||||
#undef paddq
|
||||
#undef pand
|
||||
#undef palignr
|
||||
#undef pxor_clear
|
||||
#undef MEOW_MIX
|
||||
#undef MEOW_MIX_REG
|
||||
#undef MEOW_SHUFFLE
|
||||
#undef MEOW_DUMP_STATE
|
||||
|
||||
//
|
||||
// NOTE(casey): If you need to create your own seed from non-random data, you can use MeowExpandSeed
|
||||
// to create a seed which you then store for repeated use. It is _expensive_ to generate the seed,
|
||||
// so you do not want to do this every time you hash. You _only_ want to do it when you actually
|
||||
// need to create a new seed.
|
||||
//
|
||||
|
||||
static void
|
||||
MeowExpandSeed(meow_umm InputLen, void *Input, meow_u8 *SeedResult)
|
||||
{
|
||||
meow_state State;
|
||||
meow_u64 LengthTab = (meow_u64)InputLen; // NOTE(casey): We need to always injest 8-byte lengths exactly, even on 32-bit builds, to ensure identical results
|
||||
meow_umm InjestCount = (256 / InputLen) + 2;
|
||||
|
||||
MeowBegin(&State, MeowDefaultSeed);
|
||||
MeowAbsorb(&State, sizeof(LengthTab), &LengthTab);
|
||||
while(InjestCount--)
|
||||
{
|
||||
MeowAbsorb(&State, InputLen, Input);
|
||||
}
|
||||
MeowEnd(&State, SeedResult);
|
||||
}
|
||||
|
||||
#define MEOW_HASH_X64_AESNI_H
|
||||
#endif
|
86
code/vn.cpp
|
@ -1,21 +1,23 @@
|
|||
#include "vn_platform.h"
|
||||
#include "vn_platform.cpp"
|
||||
|
||||
#if VN_INTERNAL
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "third_party/stb_image.h"
|
||||
|
||||
#if VN_INTERNAL
|
||||
struct debug_settings
|
||||
{
|
||||
b32 RenderUIDebugRects;
|
||||
b32 RenderFPSCounter;
|
||||
b32 ListHotAndActive;
|
||||
b32 ShowWelcomeMessage;
|
||||
};
|
||||
|
||||
per_thread debug_settings *DEBUG_DebugSettings = 0;
|
||||
|
||||
#endif
|
||||
|
||||
#include "vn_config.h"
|
||||
#include "vn_tokenizer.h"
|
||||
#include "vn_config.h"
|
||||
#include "vn_font.h"
|
||||
#include "vn_text_op.h"
|
||||
#include "vn_ui.h"
|
||||
|
@ -23,7 +25,9 @@ per_thread debug_settings *DEBUG_DebugSettings = 0;
|
|||
#include "vn_workspace.h"
|
||||
#include "vn_theme_dark.h"
|
||||
#include "vn_animation_curve.h"
|
||||
#include "vn_scene.h"
|
||||
|
||||
#include "vn_tokenizer.cpp"
|
||||
#include "vn_config.cpp"
|
||||
#include "vn_render.cpp"
|
||||
#include "vn_font.cpp"
|
||||
|
@ -31,10 +35,11 @@ per_thread debug_settings *DEBUG_DebugSettings = 0;
|
|||
#include "vn_ui_utils.cpp"
|
||||
#include "vn_animation_curve.cpp"
|
||||
#include "vn_workspace.cpp"
|
||||
#include "vn_scene.cpp"
|
||||
|
||||
struct vn_state
|
||||
{
|
||||
memory_arena Arena;
|
||||
memory_arena *Arena;
|
||||
glyph_atlas *GlyphAtlas;
|
||||
|
||||
config *Config;
|
||||
|
@ -43,11 +48,47 @@ struct vn_state
|
|||
workspace Workspace;
|
||||
animation_curve_state AnimationCurveState;
|
||||
|
||||
render_handle BackgroundTexture;
|
||||
|
||||
#if VN_INTERNAL
|
||||
debug_settings DebugSettings;
|
||||
#endif
|
||||
};
|
||||
|
||||
static render_handle CreateTextureFromPath(vn_render_commands *Commands, string Path)
|
||||
{
|
||||
render_handle Result = {};
|
||||
|
||||
temporary_memory Scratch = GetScratch();
|
||||
platform_file_handle File = Platform.OpenFile(Path, PlatformAccess_Read);
|
||||
if(File.IsValid)
|
||||
{
|
||||
u64 DataCount = Platform.GetFileSize(File);
|
||||
u8 *Data = PushArray(Scratch.Arena, u8, DataCount);
|
||||
Platform.ReadFile(File, Data, 0, DataCount);
|
||||
|
||||
s32 Width, Height, BPP;
|
||||
u8 *TextureData = stbi_load_from_memory(Data, DataCount, &Width, &Height, &BPP, 0);
|
||||
|
||||
render_texture_format TextureFormat = Render_TextureFormat_Invalid;
|
||||
switch(BPP)
|
||||
{
|
||||
InvalidDefaultCase;
|
||||
case 1: { TextureFormat = Render_TextureFormat_R8; } break;
|
||||
case 3: { TextureFormat = Render_TextureFormat_RGB8; } break;
|
||||
case 4: { TextureFormat = Render_TextureFormat_RGBA8; } break;
|
||||
}
|
||||
|
||||
Result = Commands->AllocateTexture(V2S32(Width, Height), TextureFormat, TextureData);
|
||||
|
||||
stbi_image_free(TextureData);
|
||||
|
||||
Platform.CloseFile(File);
|
||||
}
|
||||
ReleaseScratch(Scratch);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
|
||||
{
|
||||
SetThreadContext(ThreadContext);
|
||||
|
@ -55,35 +96,47 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
|
|||
|
||||
vn_state *State = Memory->State;
|
||||
|
||||
//- sixten: initialize application state
|
||||
if(!Memory->State)
|
||||
{
|
||||
State = Memory->State = BootstrapPushStruct(vn_state, Arena);
|
||||
memory_arena *Arena = ArenaAllocate(Gigabytes(1));
|
||||
State = Memory->State = PushStruct(Arena, vn_state);
|
||||
State->Arena = Arena;
|
||||
|
||||
State->GlyphAtlas = CreateGlyphAtlas(RenderCommands);
|
||||
State->Config = BootstrapPushStruct(config, Arena);
|
||||
State->Config = CreateConfig();
|
||||
|
||||
// sixten: Setup config binds and load current config.
|
||||
//- sixten: load assets
|
||||
State->BackgroundTexture = CreateTextureFromPath(RenderCommands, StrLit("data/backgrounds/Futon_Room.png"));
|
||||
|
||||
//- sixten: setup config binds and load current config
|
||||
{
|
||||
Config_BindS32(State->Config, StrLit("Platform/RefreshRate"), &Input->RefreshRate, 60);
|
||||
#if VN_INTERNAL
|
||||
Config_BindB32(State->Config, StrLit("Dev/RenderUIDebugRects"), &State->DebugSettings.RenderUIDebugRects, 0);
|
||||
Config_BindB32(State->Config, StrLit("Dev/RenderFPSCounter"), &State->DebugSettings.RenderFPSCounter, 0);
|
||||
Config_BindB32(State->Config, StrLit("Dev/ListHotAndActive"), &State->DebugSettings.ListHotAndActive, 0);
|
||||
DEBUG_DebugSettings = &State->DebugSettings;
|
||||
Config_BindB32(State->Config, StrLit("Dev/RenderUIDebugRects"), &DEBUG_DebugSettings->RenderUIDebugRects, 0);
|
||||
Config_BindB32(State->Config, StrLit("Dev/RenderFPSCounter"), &DEBUG_DebugSettings->RenderFPSCounter, 0);
|
||||
Config_BindB32(State->Config, StrLit("Dev/ListHotAndActive"), &DEBUG_DebugSettings->ListHotAndActive, 0);
|
||||
Config_BindB32(State->Config, StrLit("Dev/ShowWelcomeMessage"), &DEBUG_DebugSettings->ShowWelcomeMessage, 1);
|
||||
#endif
|
||||
|
||||
Config_ReadFile(State->Config, StrLit("config.vn"));
|
||||
}
|
||||
|
||||
UI_Init(&State->UI);
|
||||
Workspace_Init(&State->Workspace);
|
||||
AnimationCurve_Init(&State->AnimationCurveState);
|
||||
}
|
||||
|
||||
#if VN_INTERNAL
|
||||
DEBUG_DebugSettings = &State->DebugSettings;
|
||||
#endif
|
||||
|
||||
//- sixten: begin new frame
|
||||
AnimationCurve_NewFrame(&State->AnimationCurveState, Input->dtForFrame);
|
||||
UI_NewFrame(&State->UI, Input->EventList, Input->MouseP, State->GlyphAtlas);
|
||||
|
||||
//- sixten: build the ui
|
||||
UI_BeginBuild(RenderCommands->RenderDim);
|
||||
{
|
||||
Workspace_Update(&State->Workspace, RenderCommands, Input, State->GlyphAtlas);
|
||||
|
@ -91,6 +144,7 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
|
|||
UI_EndBuild();
|
||||
|
||||
|
||||
//- sixten: consume all remaining evetns
|
||||
for(platform_event *Event = Input->EventList->First;
|
||||
Event != 0;
|
||||
Event = Event->Next)
|
||||
|
@ -104,16 +158,24 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
|
|||
Platform_ConsumeEvent(Input->EventList, Event);
|
||||
}
|
||||
|
||||
//- sixten: render the frame
|
||||
{
|
||||
render_group Group = BeginRenderGroup(RenderCommands);
|
||||
PushClear(&Group, V3(0.1, 0.1, 0.1));
|
||||
|
||||
PushTexturedQuad(&Group,
|
||||
Range2R32(V2R32(0, 0), RenderCommands->RenderDim),
|
||||
Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(State->BackgroundTexture))),
|
||||
Color_White, Color_White, Color_White, Color_White, 0, 0, 0, State->BackgroundTexture);
|
||||
|
||||
UI_RenderFrame(&Group, State->GlyphAtlas);
|
||||
|
||||
if(DEBUG_DebugSettings->ListHotAndActive)
|
||||
{
|
||||
PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 20), 15, Color_Grey,
|
||||
PushFormat(&State->UI.FrameArena, "Hot: %S:%llu", UI_GetBoxNameByKey(UI_GetHot()), UI_GetHot()));
|
||||
PushFormat(State->UI.FrameArena, "Hot: %S:%llu", UI_GetBoxNameByKey(UI_GetHot()), UI_GetHot()));
|
||||
PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 40), 15, Color_Grey,
|
||||
PushFormat(&State->UI.FrameArena, "Active: %S:%llu", UI_GetBoxNameByKey(UI_GetActive()), UI_GetActive()));
|
||||
PushFormat(State->UI.FrameArena, "Active: %S:%llu", UI_GetBoxNameByKey(UI_GetActive()), UI_GetActive()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,11 @@ inline void AnimationCurve_SetState(animation_curve_state *State)
|
|||
Global_AnimationCurveState = State;
|
||||
}
|
||||
|
||||
static void AnimationCurve_Init(animation_curve_state *State)
|
||||
{
|
||||
State->Arena = ArenaAllocate(Gigabytes(1));
|
||||
}
|
||||
|
||||
inline animation_curve_key AnimationCurve_GenerateKeyFromString(string String)
|
||||
{
|
||||
animation_curve_key Key;
|
||||
|
@ -43,7 +48,7 @@ static animation_curve_entry *AnimationCurve_GetEntryByKey(animation_curve_key K
|
|||
{
|
||||
if(DLLIsEmpty(State->FirstFreeEntry))
|
||||
{
|
||||
Result = PushStruct(&State->Arena, animation_curve_entry);
|
||||
Result = PushStruct(State->Arena, animation_curve_entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -33,7 +33,7 @@ struct animation_curve_bucket
|
|||
|
||||
struct animation_curve_state
|
||||
{
|
||||
memory_arena Arena;
|
||||
memory_arena *Arena;
|
||||
|
||||
u32 CurrentFrame;
|
||||
r32 dtForFrame;
|
||||
|
@ -48,6 +48,7 @@ struct animation_curve_state
|
|||
|
||||
inline void AnimationCurve_SetState(animation_curve_state *State);
|
||||
inline animation_curve_key AnimationCurve_GenerateKeyFromString(string String);
|
||||
static void AnimationCurve_Init(animation_curve_state *State);
|
||||
static animation_curve_entry *AnimationCurve_GetEntryByKey(animation_curve_key Key, r32 Initial = 0);
|
||||
inline r32 AnimationCurve_GetValue(string Name, r32 Initial);
|
||||
inline void AnimationCurve_SetValue(string Name, r32 Value);
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
static config *CreateConfig(void)
|
||||
{
|
||||
memory_arena *Arena = ArenaAllocate(Gigabytes(1));
|
||||
config *Config = PushStruct(Arena, config);
|
||||
Config->Arena = Arena;
|
||||
return(Config);
|
||||
}
|
||||
|
||||
static config_entry *Config_FindEntryByName(config *Config, string Name)
|
||||
{
|
||||
config_entry *Result = 0;
|
||||
|
@ -24,8 +32,8 @@ static void Config_BindEntry(config *Config, string Name, config_entry_type Type
|
|||
config_entry *Entry = Config_FindEntryByName(Config, Name);
|
||||
if(!Entry)
|
||||
{
|
||||
Entry = PushStruct(&Config->Arena, config_entry);
|
||||
Entry->Name = PushString(&Config->Arena, Name);
|
||||
Entry = PushStruct(Config->Arena, config_entry);
|
||||
Entry->Name = PushString(Config->Arena, Name);
|
||||
Entry->Type = Type;
|
||||
|
||||
u32 BucketSlot = HashString(Name) % ArrayCount(Config->EntryBuckets);
|
||||
|
@ -66,9 +74,10 @@ inline void Config_BindB32(config *Config, string Name, b32 *Target, b32 Default
|
|||
Config_BindEntry(Config, Name, Config_Entry_B32, Target);
|
||||
}
|
||||
|
||||
static void Config_ParseError(char *Message, memory_arena *Arena)
|
||||
static void Config_ParseError(string Message, string FileText, s64 Offset, memory_arena *Arena)
|
||||
{
|
||||
string String = PushFormat(Arena, "An error occured during config parsing:\n\"%s\"", Message);
|
||||
text_point Point = TextPointFromOffset(FileText, Offset);
|
||||
string String = PushFormat(Arena, "Config: At %i:%i - %S", Point.Line, Point.Column, Message);
|
||||
Platform.ShowMessage(String, Platform_Message_Warning);
|
||||
}
|
||||
|
||||
|
@ -76,43 +85,92 @@ static void Config_ReadFile(config *Config, string Path)
|
|||
{
|
||||
temporary_memory Scratch = GetScratch();
|
||||
|
||||
tokenizer Tokenizer = Tokenizer_BeginTokenization(Platform_ReadEntireFile(Scratch.Arena, Path));
|
||||
if(Tokenizer.Input.Data)
|
||||
{
|
||||
token Token;
|
||||
for(;;)
|
||||
{
|
||||
Token = Tokenizer_GetNextToken(&Tokenizer);
|
||||
//- sixten: read & tokenize input file
|
||||
string Text = Platform_ReadEntireFile(Scratch.Arena, Path);
|
||||
tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, Path, Text, TokenGroup_Whitespace|TokenGroup_Comment);
|
||||
token_array Tokens = TokenizeResult.Tokens;
|
||||
|
||||
if(Token.Type == Token_Identifier)
|
||||
{
|
||||
string Dir = Token.String;
|
||||
// sixten: parse context
|
||||
config_parse_list FullPath = {};
|
||||
config_parse_mode ParseMode = ConfigParseMode_Main;
|
||||
|
||||
if(Tokenizer_RequireToken(&Tokenizer, Token_CurlyOpen))
|
||||
//- sixten: parse tokens
|
||||
token *TokensStart = Tokens.Tokens;
|
||||
token *TokensEnd = Tokens.Tokens + Tokens.Count;
|
||||
token *Token = TokensStart;
|
||||
for(;Token < TokensEnd;)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
Token = Tokenizer_GetNextToken(&Tokenizer);
|
||||
string TokenString = Substring(Text, Token->Range);
|
||||
|
||||
if(Token.Type == Token_Identifier)
|
||||
//- sixten: get next name
|
||||
if(ParseMode == ConfigParseMode_Main && Token->Flags & TokenFlag_Identifier)
|
||||
{
|
||||
string Name = Token.String;
|
||||
Config_ParseListPush(Scratch.Arena, &FullPath, TokenString);
|
||||
ParseMode = ConfigParseMode_ScanForCurlyOpenOrEquals;
|
||||
Token += 1;
|
||||
goto TokenConsumed;
|
||||
}
|
||||
|
||||
if(Tokenizer_RequireToken(&Tokenizer, Token_Equals))
|
||||
//- sixten: scan for curly close
|
||||
if(ParseMode == ConfigParseMode_Main && Token->Flags & TokenFlag_Reserved && AreEqual(TokenString, StrLit("}")))
|
||||
{
|
||||
Token = Tokenizer_GetNextToken(&Tokenizer);
|
||||
Config_ParseListPop(&FullPath);
|
||||
Token += 1;
|
||||
goto TokenConsumed;
|
||||
}
|
||||
|
||||
if(Token.Type == Token_IntegerValue)
|
||||
//- sixten: scan for curly open
|
||||
if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Flags & TokenFlag_Reserved && AreEqual(TokenString, StrLit("{")))
|
||||
{
|
||||
s64 Value = ConvertStringToS64(Token.String);
|
||||
ParseMode = ConfigParseMode_Main;
|
||||
Token += 1;
|
||||
goto TokenConsumed;
|
||||
}
|
||||
|
||||
string FullName = PushFormat(Scratch.Arena, "%S/%S", Dir, Name);
|
||||
//- sixten: scan for equals
|
||||
if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Flags & TokenFlag_Symbol && AreEqual(TokenString, StrLit("=")))
|
||||
{
|
||||
ParseMode = ConfigParseMode_ScanForValue;
|
||||
Token += 1;
|
||||
goto TokenConsumed;
|
||||
}
|
||||
|
||||
//- sixten: scan for semicolon
|
||||
if(ParseMode == ConfigParseMode_ScanForSemicolon && Token->Flags & TokenFlag_Reserved && AreEqual(TokenString, StrLit(";")))
|
||||
{
|
||||
ParseMode = ConfigParseMode_Main;
|
||||
Token += 1;
|
||||
goto TokenConsumed;
|
||||
}
|
||||
|
||||
//- sixten: scan for boolean value
|
||||
if(ParseMode == ConfigParseMode_ScanForValue && Token->Flags & TokenFlag_Identifier && (AreEqual(TokenString, StrLit("true")) || AreEqual(TokenString, StrLit("false"))))
|
||||
{
|
||||
string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath);
|
||||
config_entry *Entry = Config_FindEntryByName(Config, FullName);
|
||||
if(Entry)
|
||||
{
|
||||
b32 Value = AreEqual(TokenString, StrLit("true"));
|
||||
Assert(Entry->Type == Config_Entry_B32);
|
||||
*(b32 *)Entry->Target = Value;
|
||||
}
|
||||
Config_ParseListPop(&FullPath);
|
||||
ParseMode = ConfigParseMode_ScanForSemicolon;
|
||||
Token += 1;
|
||||
goto TokenConsumed;
|
||||
}
|
||||
|
||||
//- sixten: scan for integer value
|
||||
if(ParseMode == ConfigParseMode_ScanForValue && Token->Flags & TokenFlag_Numeric)
|
||||
{
|
||||
string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath);
|
||||
config_entry *Entry = Config_FindEntryByName(Config, FullName);
|
||||
if(Entry)
|
||||
{
|
||||
s64 Value = ConvertStringToS64(TokenString);
|
||||
if(Entry->Type == Config_Entry_S32)
|
||||
{
|
||||
*(s32 *)Entry->Target = (s32)Value;
|
||||
*(s32 *)Entry->Target = Value;
|
||||
}
|
||||
else if(Entry->Type == Config_Entry_S64)
|
||||
{
|
||||
|
@ -120,93 +178,75 @@ static void Config_ReadFile(config *Config, string Path)
|
|||
}
|
||||
else
|
||||
{
|
||||
Config_ParseError("Entry has wrong data type.", Scratch.Arena);
|
||||
goto End;
|
||||
InvalidCodepath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Config_ParseError("Cannot find entry.", Scratch.Arena);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
else if(Token.Type == Token_Identifier)
|
||||
{
|
||||
string FullName = PushFormat(Scratch.Arena, "%S/%S", Dir, Name);
|
||||
config_entry *Entry = Config_FindEntryByName(Config, FullName);
|
||||
if(Entry)
|
||||
{
|
||||
if(AreEqual(Token.String, StrLit("true")))
|
||||
{
|
||||
*(b32 *)Entry->Target = true;
|
||||
}
|
||||
else if(AreEqual(Token.String, StrLit("false")))
|
||||
{
|
||||
*(b32 *)Entry->Target = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Config_ParseError("Entry has wrong data type.", Scratch.Arena);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Config_ParseError("Cannot find entry.", Scratch.Arena);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Config_ParseError("Expected a value.", Scratch.Arena);
|
||||
goto End;
|
||||
Config_ParseListPop(&FullPath);
|
||||
ParseMode = ConfigParseMode_ScanForSemicolon;
|
||||
Token += 1;
|
||||
goto TokenConsumed;
|
||||
}
|
||||
|
||||
if(!Tokenizer_RequireToken(&Tokenizer, Token_Semicolon))
|
||||
//- sixten: if the token has not been consumed, something's gone wrong
|
||||
{
|
||||
Config_ParseError("Expected a ';'.", Scratch.Arena);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
else
|
||||
string ErrorMessage = StrLit("Unknown parse error");
|
||||
//- sixten: determine error message
|
||||
switch(ParseMode)
|
||||
{
|
||||
Config_ParseError("Expected '='.", Scratch.Arena);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
else if(Token.Type == Token_CurlyClose)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Config_ParseError("Expected '}' or identifier.", Scratch.Arena);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Config_ParseError("Expected '{'.", Scratch.Arena);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
else if(Token.Type == Token_EndOfFile)
|
||||
{
|
||||
goto End;
|
||||
}
|
||||
else
|
||||
{
|
||||
Config_ParseError("Unexpected token.", Scratch.Arena);
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
case ConfigParseMode_Main: { ErrorMessage = StrLit("Expected identifier or '}'"); } break;
|
||||
case ConfigParseMode_ScanForCurlyOpenOrEquals: { ErrorMessage = StrLit("Expected '{' or '='") ; } break;
|
||||
case ConfigParseMode_ScanForValue: { ErrorMessage = StrLit("Expected value"); } break;
|
||||
case ConfigParseMode_ScanForSemicolon: { ErrorMessage = StrLit("Expected ';'"); } break;
|
||||
}
|
||||
|
||||
Config_ParseError(ErrorMessage, Text, Token->Range.Min, Scratch.Arena);
|
||||
Token += 1;
|
||||
}
|
||||
|
||||
TokenConsumed:;
|
||||
}
|
||||
|
||||
End:
|
||||
ReleaseScratch(Scratch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Config Parse Type Functions
|
||||
static void Config_ParseListPush(memory_arena *Arena, config_parse_list *List, string Name)
|
||||
{
|
||||
config_parse_node *Node = PushStruct(Arena, config_parse_node);
|
||||
Node->Name = Name;
|
||||
List->TotalCountPlusOne += Name.Count + 1;
|
||||
DLLInsertLast(List->First, List->Last, Node);
|
||||
}
|
||||
|
||||
static void Config_ParseListPop(config_parse_list *List)
|
||||
{
|
||||
config_parse_node *Node = List->Last;
|
||||
if(Node)
|
||||
{
|
||||
List->TotalCountPlusOne -= Node->Name.Count + 1;
|
||||
DLLRemove(List->First, List->Last, Node);
|
||||
}
|
||||
}
|
||||
|
||||
static string Config_ParseListJoin(memory_arena *Arena, config_parse_list *List)
|
||||
{
|
||||
s64 TotalCount = List->TotalCountPlusOne - 1;
|
||||
string Result = MakeString(PushArray(Arena, u8, List->TotalCountPlusOne), TotalCount);
|
||||
s64 Index = 0;
|
||||
for(config_parse_node *Node = List->First; Node != 0; Node = Node->Next)
|
||||
{
|
||||
Copy(Result.Data + Index, Node->Name.Data, Node->Name.Count);
|
||||
Index += Node->Name.Count;
|
||||
if(Node->Next)
|
||||
{
|
||||
Result.Data[Index] = '/';
|
||||
Index += 1;
|
||||
}
|
||||
}
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static void Config_WriteFile(config *Config, string Path)
|
||||
{
|
||||
string_list Out = {};
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#ifndef VN_CONFIG_H
|
||||
#define VN_CONFIG_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Config Types
|
||||
enum config_entry_type
|
||||
{
|
||||
Config_Entry_S32,
|
||||
|
@ -30,7 +32,7 @@ struct config_entry_bucket
|
|||
|
||||
struct config
|
||||
{
|
||||
memory_arena Arena;
|
||||
memory_arena *Arena;
|
||||
config_entry_bucket EntryBuckets[32];
|
||||
|
||||
// sixten(NOTE): Keeps track of the order in which the entries were declared. (Used during saving)
|
||||
|
@ -38,6 +40,35 @@ struct config
|
|||
config_entry *LastInternal;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Config Parse Types
|
||||
enum config_parse_mode
|
||||
{
|
||||
ConfigParseMode_Main,
|
||||
ConfigParseMode_ScanForCurlyOpenOrEquals,
|
||||
ConfigParseMode_ScanForValue,
|
||||
ConfigParseMode_ScanForSemicolon,
|
||||
};
|
||||
|
||||
struct config_parse_node
|
||||
{
|
||||
config_parse_node *Next;
|
||||
config_parse_node *Prev;
|
||||
string Name;
|
||||
};
|
||||
|
||||
struct config_parse_list
|
||||
{
|
||||
config_parse_node *First;
|
||||
config_parse_node *Last;
|
||||
s64 TotalCountPlusOne;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Config Type Functions
|
||||
|
||||
static config *CreateConfig(void);
|
||||
|
||||
static config_entry *Config_FindEntryByName(config *Config, string Name);
|
||||
|
||||
static void Config_BindEntry(config *Config, string Name, config_entry_type Type, void *Target);
|
||||
|
@ -48,4 +79,10 @@ inline void Config_BindB32(config *Config, string Name, b32 *Target, b32 Default
|
|||
static void Config_ReadFile(config *Config, string Path);
|
||||
static void Config_WriteFile(config *Config);
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Config Parse Type Functions
|
||||
static void Config_ParseListPush(memory_arena *Arena, config_parse_list *List, string Name);
|
||||
static void Config_ParseListPop(config_parse_list *List);
|
||||
static string Config_ParseListJoin(memory_arena *Arena, config_parse_list *List);
|
||||
|
||||
#endif //VN_CONFIG_H
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#define GLYPH_SUBPIXEL_SEGMENTS 3
|
||||
|
||||
global read_only s32 Font_Oversample = 2;
|
||||
|
||||
inline s32 GetSubpixelSegmentAtP(r32 Value)
|
||||
{
|
||||
s32 Result = (s32)(Value - Floor(Value))*GLYPH_SUBPIXEL_SEGMENTS;
|
||||
|
@ -102,22 +104,24 @@ static glyph_atlas *CreateGlyphAtlas(vn_render_commands *RenderCommands,
|
|||
s32 BitmapSize = DEFAULT_GLYPH_ATLAS_DIM,
|
||||
s32 GlyphSize = MAX_GLYPH_SIZE)
|
||||
{
|
||||
glyph_atlas *Atlas = BootstrapPushStruct(glyph_atlas, Arena);
|
||||
memory_arena *Arena = ArenaAllocate(Megabytes(128));
|
||||
glyph_atlas *Atlas = PushStruct(Arena, glyph_atlas);
|
||||
Atlas->Arena = Arena;
|
||||
|
||||
Atlas->BitmapSize = BitmapSize;
|
||||
Atlas->GlyphSize = GlyphSize;
|
||||
|
||||
Atlas->MaxGlyphCount = (DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE)*(DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE);
|
||||
Atlas->Glyphs = PushArray(&Atlas->Arena, glyph, Atlas->MaxGlyphCount);
|
||||
Atlas->Glyphs = PushArray(Atlas->Arena, glyph, Atlas->MaxGlyphCount);
|
||||
|
||||
Atlas->RenderCommands = RenderCommands;
|
||||
Atlas->Texture = RenderCommands->AllocateTexture(V2S32(BitmapSize, BitmapSize), Render_TextureFormat_R8, 0);
|
||||
|
||||
Atlas->Fonts[Font_Regular].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/Roboto-Regular.ttf"));
|
||||
Atlas->Fonts[Font_Bold].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/Roboto-Bold.ttf"));
|
||||
Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/Liberation-Mono.ttf"));
|
||||
Atlas->Fonts[Font_Hand].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/PatrickHand-Regular.ttf"));
|
||||
Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/icons.ttf"));
|
||||
Atlas->Fonts[Font_Regular].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/Roboto-Regular.ttf"));
|
||||
Atlas->Fonts[Font_Bold].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/Roboto-Bold.ttf"));
|
||||
Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/DejaVuSansMono.ttf"));
|
||||
Atlas->Fonts[Font_Hand].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/PatrickHand-Regular.ttf"));
|
||||
Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/icons.ttf"));
|
||||
|
||||
for(s32 FontIndex = 0;
|
||||
FontIndex < Font_Count;
|
||||
|
@ -131,40 +135,38 @@ static glyph_atlas *CreateGlyphAtlas(vn_render_commands *RenderCommands,
|
|||
stbtt_GetFontVMetrics(&Font->Info, &Font->Ascent, &Font->Descent, &Font->LineGap);
|
||||
}
|
||||
|
||||
Atlas->BitmapBuffer = PushArray(&Atlas->Arena, u8, GlyphSize*GlyphSize);
|
||||
Atlas->BitmapBuffer = PushArray(Atlas->Arena, u8, GlyphSize*GlyphSize);
|
||||
|
||||
return(Atlas);
|
||||
}
|
||||
|
||||
static void PushText(render_group *Group, glyph_atlas *Atlas, font_id Font,
|
||||
static r32 PushText(render_group *Group, glyph_atlas *Atlas, font_id Font,
|
||||
v2 P, r32 Size, v4 Color,
|
||||
string Text)
|
||||
{
|
||||
r32 Oversample = 2;
|
||||
|
||||
r32 OffsetX = 0;
|
||||
for(utf8_iterator Iter = IterateUTF8String(Text);
|
||||
IsValid(&Iter);
|
||||
Advance(&Iter))
|
||||
{
|
||||
u32 Codepoint = Iter.Codepoint;
|
||||
|
||||
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Oversample, GetSubpixelSegmentAtP(P.x*Oversample));
|
||||
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(P.x*Font_Oversample));
|
||||
Assert(Glyph);
|
||||
|
||||
v2 GlyphP = P;
|
||||
GlyphP.x += Glyph->Offset.x/Oversample;
|
||||
GlyphP.y += Glyph->Offset.y/Oversample;
|
||||
v2 GlyphP = P + Glyph->Offset*(1.0 / Font_Oversample) + V2(OffsetX, 1);
|
||||
|
||||
v2 RenderDim = ConvertV2ToR32(Glyph->P1 - Glyph->P0);
|
||||
v2 Dim = RenderDim;
|
||||
Dim.x /= Oversample;
|
||||
Dim.y /= Oversample;
|
||||
v2 Dim = RenderDim*(1.0 / Font_Oversample);
|
||||
|
||||
PushTexturedQuad(Group, GlyphP, Dim, ConvertV2ToR32(Glyph->P0), RenderDim,
|
||||
PushTexturedQuad(Group,
|
||||
Range2R32(GlyphP, GlyphP+Dim),
|
||||
Range2R32(ConvertV2ToR32(Glyph->P0), ConvertV2ToR32(Glyph->P1)),
|
||||
Color, Color, Color, Color, 0, 0, 0, Atlas->Texture);
|
||||
|
||||
P.x += Glyph->Advance/Oversample;
|
||||
OffsetX += Glyph->Advance/Font_Oversample;
|
||||
}
|
||||
return(OffsetX);
|
||||
}
|
||||
|
||||
static void PushTextF(render_group *Group, glyph_atlas *Atlas, font_id Font,
|
||||
|
@ -185,8 +187,6 @@ static void PushTextF(render_group *Group, glyph_atlas *Atlas, font_id Font,
|
|||
|
||||
inline r32 CalculateRasterizedTextWidth(glyph_atlas *Atlas, font_id Font, r32 Size, string Text)
|
||||
{
|
||||
r32 Oversample = 2;
|
||||
|
||||
r32 X = 0;
|
||||
|
||||
for(utf8_iterator Iter = IterateUTF8String(Text);
|
||||
|
@ -195,10 +195,10 @@ inline r32 CalculateRasterizedTextWidth(glyph_atlas *Atlas, font_id Font, r32 Si
|
|||
{
|
||||
u32 Codepoint = Iter.Codepoint;
|
||||
|
||||
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Oversample, GetSubpixelSegmentAtP(X*Oversample));
|
||||
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(X*Font_Oversample));
|
||||
Assert(Glyph);
|
||||
|
||||
X += Glyph->Advance/Oversample;
|
||||
X += Glyph->Advance/Font_Oversample;
|
||||
}
|
||||
|
||||
return(X);
|
||||
|
|
|
@ -89,7 +89,7 @@ struct loaded_font
|
|||
|
||||
struct glyph_atlas
|
||||
{
|
||||
memory_arena Arena;
|
||||
memory_arena *Arena;
|
||||
|
||||
s32 MaxGlyphCount;
|
||||
s32 GlyphsUsed;
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
@table(Name, Type, Arguments)
|
||||
OpenGLFunctions:
|
||||
{
|
||||
{BindTexture `void` `GLenum target, GLuint texture`}
|
||||
{BlendFunc `void` `GLenum sfactor, GLenum dfactor`}
|
||||
{Clear `void` `GLbitfield mask`}
|
||||
{ClearAccum `void` `GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha`}
|
||||
{ClearColor `void` `GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha`}
|
||||
{ClearDepth `void` `GLclampd depth`}
|
||||
{ClearIndex `void` `GLfloat c`}
|
||||
{ClearStencil `void` `GLint s`}
|
||||
{ClipPlane `void` `GLenum plane, const GLdouble *equation`}
|
||||
{CullFace `void` `GLenum mode`}
|
||||
{DeleteTextures `void` `GLsizei n, const GLuint *textures`}
|
||||
{Disable `void` `GLenum cap`}
|
||||
{DrawArrays `void` `GLenum mode, GLint first, GLsizei count`}
|
||||
{DrawBuffer `void` `GLenum mode`}
|
||||
{DrawElements `void` `GLenum mode, GLsizei count, GLenum type, const GLvoid *indices`}
|
||||
{DrawArraysInstanced `void` `GLenum mode, GLint first, GLsizei count, GLsizei instancecount`}
|
||||
{Enable `void` `GLenum cap`}
|
||||
{GenTextures `void` `GLsizei n, GLuint *textures`}
|
||||
{GetClipPlane `void` `GLenum plane, GLdouble *equation`}
|
||||
{GetDoublev `void` `GLenum pname, GLdouble *params`}
|
||||
{GetError `GLenum` `void`}
|
||||
{GetFloatv `void` `GLenum pname, GLfloat *params`}
|
||||
{GetIntegerv `void` `GLenum pname, GLint *params`}
|
||||
{GetPointerv `void` `GLenum pname, GLvoid* *params`}
|
||||
{GetString `const GLubyte *` `GLenum name`}
|
||||
{GetTexEnvfv `void` `GLenum target, GLenum pname, GLfloat *params`}
|
||||
{GetTexEnviv `void` `GLenum target, GLenum pname, GLint *params`}
|
||||
{GetTexGendv `void` `GLenum coord, GLenum pname, GLdouble *params`}
|
||||
{GetTexGenfv `void` `GLenum coord, GLenum pname, GLfloat *params`}
|
||||
{GetTexGeniv `void` `GLenum coord, GLenum pname, GLint *params`}
|
||||
{GetTexImage `void` `GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels`}
|
||||
{GetTexLevelParameterfv `void` `GLenum target, GLint level, GLenum pname, GLfloat *params`}
|
||||
{GetTexLevelParameteriv `void` `GLenum target, GLint level, GLenum pname, GLint *params`}
|
||||
{GetTexParameterfv `void` `GLenum target, GLenum pname, GLfloat *params`}
|
||||
{GetTexParameteriv `void` `GLenum target, GLenum pname, GLint *params`}
|
||||
{Hint `void` `GLenum target, GLenum mode`}
|
||||
{IsTexture `GLboolean` `GLuint texture`}
|
||||
{LineWidth `void` `GLfloat width`}
|
||||
{ListBase `void` `GLuint base`}
|
||||
{LoadName `void` `GLuint name`}
|
||||
{LogicOp `void` `GLenum opcode`}
|
||||
{PointSize `void` `GLfloat size`}
|
||||
{PolygonMode `void` `GLenum face, GLenum mode`}
|
||||
{Scissor `void` `GLint x, GLint y, GLsizei width, GLsizei height`}
|
||||
{TexImage1D `void` `GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels`}
|
||||
{TexImage2D `void` `GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels`}
|
||||
{TexParameterf `void` `GLenum target, GLenum pname, GLfloat param`}
|
||||
{TexParameterfv `void` `GLenum target, GLenum pname, const GLfloat *params`}
|
||||
{TexParameteri `void` `GLenum target, GLenum pname, GLint param`}
|
||||
{TexParameteriv `void` `GLenum target, GLenum pname, const GLint *params`}
|
||||
{TexSubImage1D `void` `GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels`}
|
||||
{TexSubImage2D `void` `GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels`}
|
||||
{CompressedTexImage2D `void` `GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *pixels`}
|
||||
{ActiveTexture `void` `GLenum texture`}
|
||||
{Viewport `void` `GLint x, GLint y, GLsizei width, GLsizei height`}
|
||||
{GenBuffers `void` `GLsizei n, GLuint *buffers`}
|
||||
{BindBuffer `void` `GLenum target, GLuint buffer`}
|
||||
{BufferData `void` `GLenum target, GLsizeiptr size, const void *data, GLenum usage`}
|
||||
{BufferSubData `void` `GLenum target, GLintptr offset, GLsizeiptr size, const void *data`}
|
||||
{GenVertexArrays `void` `GLsizei n, GLuint *arrays`}
|
||||
{BindVertexArray `void` `GLenum array`}
|
||||
{GetAttribLocation `GLint` `GLuint program, const GLchar *name`}
|
||||
{EnableVertexAttribArray `void` `GLuint index`}
|
||||
{DisableVertexAttribArray `void` `GLuint index`}
|
||||
{VertexAttribPointer `void` `GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer`}
|
||||
{VertexAttribIPointer `void` `GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer`}
|
||||
{VertexAttribLPointer `void` `GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer`}
|
||||
{VertexAttribDivisor `void` `GLuint index, GLuint divisor`}
|
||||
{CreateShader `GLuint` `GLenum type`}
|
||||
{ShaderSource `void` `GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length`}
|
||||
{CompileShader `void` `GLuint shader`}
|
||||
{DeleteShader `void` `GLuint shader`}
|
||||
{GetShaderiv `void` `GLuint shader, GLenum pname, GLint *params`}
|
||||
{GetShaderInfoLog `void` `GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog`}
|
||||
{CreateProgram `GLuint` `void`}
|
||||
{UseProgram `void` `GLuint program`}
|
||||
{AttachShader `void` `GLuint program, GLuint shader`}
|
||||
{DeleteProgram `void` `GLuint program`}
|
||||
{LinkProgram `void` `GLuint program`}
|
||||
{GetProgramiv `void` `GLuint program, GLenum pname, GLint *params`}
|
||||
{GetProgramInfoLog `void` `GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog`}
|
||||
{GetUniformLocation `GLint` `GLuint program, const GLchar *name`}
|
||||
{Uniform1i `void` `GLint location, GLint v0`}
|
||||
{Uniform2f `void` `GLint location, GLfloat v0, GLfloat v1`}
|
||||
{Uniform3f `void` `GLint location, GLfloat v0, GLfloat v1, GLfloat v2`}
|
||||
{UniformMatrix4fv `void` `GLint location, GLsizei count, GLboolean transpose, const GLfloat *value`}
|
||||
{DebugMessageCallback `void` `void (*)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar *, const void *), const void *userParam`}
|
||||
}
|
||||
|
||||
// sixten: Generate the typedefs.
|
||||
@table_gen
|
||||
{
|
||||
@expand(OpenGLFunctions s) `typedef $(s.Type) opengl_$(s.Name)($(s.Arguments));`;
|
||||
}
|
||||
|
||||
// sixten: Generate the global pointers.
|
||||
@table_gen
|
||||
{
|
||||
@expand(OpenGLFunctions s) `global opengl_$(s.Name) *gl$(s.Name) = 0;`;
|
||||
}
|
||||
|
||||
// sixten: Generate OpenGL_LoadAllFunctions
|
||||
@table_gen
|
||||
{
|
||||
`extern void *OpenGL_LoadFunction(char *);`;
|
||||
`static void OpenGL_LoadAllFunctions(void)`;
|
||||
`{`;
|
||||
@expand(OpenGLFunctions s) `gl$(s.Name) = (opengl_$(s.Name) *)OpenGL_LoadFunction("gl$(s.Name)");`;
|
||||
`}`;
|
||||
}
|
|
@ -11,21 +11,6 @@
|
|||
|
||||
// sixten: Services the platform provides to the application
|
||||
|
||||
struct platform_memory_block
|
||||
{
|
||||
u8 *Base;
|
||||
u64 Size;
|
||||
|
||||
u64 Used;
|
||||
platform_memory_block *ArenaPrev;
|
||||
};
|
||||
|
||||
#define PLATFORM_ALLOCATE_MEMORY(name) platform_memory_block *name(u64 Size)
|
||||
typedef PLATFORM_ALLOCATE_MEMORY(platform_allocate_memory);
|
||||
|
||||
#define PLATFORM_DEALLOCATE_MEMORY(name) b32 name(platform_memory_block *Block)
|
||||
typedef PLATFORM_DEALLOCATE_MEMORY(platform_deallocate_memory);
|
||||
|
||||
enum
|
||||
{
|
||||
PlatformAccess_Read = 0x1,
|
||||
|
@ -39,21 +24,6 @@ struct platform_file_handle
|
|||
b32 IsValid;
|
||||
};
|
||||
|
||||
#define PLATFORM_OPEN_FILE(name) platform_file_handle name(string Path, platform_access_flags FileAccess)
|
||||
typedef PLATFORM_OPEN_FILE(platform_open_file);
|
||||
|
||||
#define PLATFORM_CLOSE_FILE(name) void name(platform_file_handle Handle)
|
||||
typedef PLATFORM_CLOSE_FILE(platform_close_file);
|
||||
|
||||
#define PLATFORM_READ_FILE(name) void name(platform_file_handle Handle, void *Dest, u64 Offset, u64 Size)
|
||||
typedef PLATFORM_READ_FILE(platform_read_file);
|
||||
|
||||
#define PLATFORM_WRITE_FILE(name) void name(platform_file_handle Handle, void *Source, u64 Offset, u64 Size)
|
||||
typedef PLATFORM_WRITE_FILE(platform_write_file);
|
||||
|
||||
#define PLATFORM_GET_FILE_SIZE(name) u64 name(platform_file_handle Handle)
|
||||
typedef PLATFORM_GET_FILE_SIZE(platform_get_file_size);
|
||||
|
||||
enum platform_cursor
|
||||
{
|
||||
PlatformCursor_Arrow,
|
||||
|
@ -72,12 +42,6 @@ enum platform_cursor
|
|||
PlatformCursor_Count
|
||||
};
|
||||
|
||||
#define PLATFORM_SET_CURSOR(name) void name(platform_cursor Cursor)
|
||||
typedef PLATFORM_SET_CURSOR(platform_set_cursor);
|
||||
|
||||
#define PLATFORM_TOGGLE_FULLSCREEN(name) void name(void)
|
||||
typedef PLATFORM_TOGGLE_FULLSCREEN(platform_toggle_fullscreen);
|
||||
|
||||
enum platform_message_type
|
||||
{
|
||||
Platform_Message_Info,
|
||||
|
@ -86,25 +50,6 @@ enum platform_message_type
|
|||
Platform_Message_Fatal,
|
||||
};
|
||||
|
||||
#define PLATFORM_SHOW_MESSAGE(name) void name(string Message, platform_message_type Type)
|
||||
typedef PLATFORM_SHOW_MESSAGE(platform_show_message);
|
||||
|
||||
struct platform_api
|
||||
{
|
||||
platform_allocate_memory *AllocateMemory;
|
||||
platform_deallocate_memory *DeallocateMemory;
|
||||
|
||||
platform_open_file *OpenFile;
|
||||
platform_close_file *CloseFile;
|
||||
platform_read_file *ReadFile;
|
||||
platform_write_file *WriteFile;
|
||||
platform_get_file_size *GetFileSize;
|
||||
|
||||
platform_set_cursor *SetCursor;
|
||||
platform_toggle_fullscreen *ToggleFullscreen;
|
||||
platform_show_message *ShowMessage;
|
||||
};
|
||||
|
||||
enum platform_event_type
|
||||
{
|
||||
PlatformEvent_Press,
|
||||
|
@ -170,6 +115,8 @@ struct platform_event_list
|
|||
platform_event *Last;
|
||||
};
|
||||
|
||||
#include "generated/vn_platform.meta.h"
|
||||
|
||||
static platform_api Platform;
|
||||
|
||||
// sixten: Services that the render backend provides to the application
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
@table(Name, NameLower, NameCaps, Type, Arguments) platform_functions:
|
||||
{
|
||||
{ Reserve reserve RESERVE `void *` `u64 Size` }
|
||||
{ Release release RELEASE `void` `void *Pointer` }
|
||||
{ Commit commit COMMIT `void` `void *Pointer, u64 Size` }
|
||||
{ Decommit decommit DECOMMIT `void` `void *Pointer, u64 Size` }
|
||||
{ OpenFile open_file OPEN_FILE `platform_file_handle` `string Path, platform_access_flags FileAccess` }
|
||||
{ CloseFile close_file CLOSE_FILE `void` `platform_file_handle Handle` }
|
||||
{ ReadFile read_file READ_FILE `void` `platform_file_handle Handle, void *Dest, u64 Offset, u64 Size` }
|
||||
{ WriteFile write_file WRITE_FILE `void` `platform_file_handle Handle, void *Source, u64 Offset, u64 Size` }
|
||||
{ GetFileSize get_file_size GET_FILE_SIZE `u64` `platform_file_handle Handle` }
|
||||
{ SetCursor set_cursor SET_CURSOR `void` `platform_cursor Cursor` }
|
||||
{ ToggleFullscreen toggle_fullscreen TOGGLE_FULLSCREEN `void` `void` }
|
||||
{ ShowMessage show_message SHOW_MESSAGE `void` `string Message, platform_message_type Type` }
|
||||
}
|
||||
|
||||
@table_gen
|
||||
{
|
||||
@expand(platform_functions s) `#define PLATFORM_$(s.NameCaps)(name) $(s.Type) name($(s.Arguments))`
|
||||
}
|
||||
|
||||
@table_gen
|
||||
{
|
||||
@expand(platform_functions s) `typedef PLATFORM_$(s.NameCaps)(platform_$(s.NameLower));`
|
||||
}
|
||||
|
||||
@table_gen
|
||||
{
|
||||
`struct platform_api`
|
||||
`{`
|
||||
@expand(platform_functions s) `platform_$(s.NameLower) *$(s.Name); `
|
||||
`};`
|
||||
}
|
||||
|
||||
@table_gen
|
||||
{
|
||||
`#define RegisterPlatformFunctions(PlatformName)\\`
|
||||
@expand(platform_functions s) `Platform.$(s.Name) = PlatformName##_$(s.Name);\\`
|
||||
}
|
|
@ -9,7 +9,7 @@ inline b32 AreEqual(render_handle A, render_handle B)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline v2_s32 GetTextureDim(render_handle Handle)
|
||||
inline v2_s32 DimFromTexture(render_handle Handle)
|
||||
{
|
||||
v2_s32 Result = V2S32(Handle.U32[2], Handle.U32[3]);
|
||||
return(Result);
|
||||
|
@ -102,9 +102,49 @@ inline u32 PackV4ToU32(v4 V)
|
|||
return(Result);
|
||||
}
|
||||
|
||||
inline void PushTexturedQuad(render_group *Group,
|
||||
v2 P, v2 Dim,
|
||||
v2 SourceP, v2 SourceDim,
|
||||
#if 0
|
||||
static void PushTexturedQuadAsInstances(render_group *Group,
|
||||
range2_r32 Dest,
|
||||
range2_r32 Source,
|
||||
v4 Color00, v4 Color10, v4 Color01, v4 Color11,
|
||||
r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness,
|
||||
render_handle Texture)
|
||||
{
|
||||
vn_render_commands *Commands = Group->Commands;
|
||||
|
||||
render_command_instanced_quads *Command = (render_command_instanced_quads *)(Group->CurrentCommand + 1);
|
||||
s32 TextureIndex;
|
||||
if(!(Group->CurrentCommand && Group->CurrentCommand->Type == Render_Command_render_command_instanced_quads &&
|
||||
(TextureIndex = GetTextureIndexForCommand((render_command_quads *)Command, Texture)) != -1))
|
||||
{
|
||||
Command = PushCommand(Group, render_command_instanced_quads);
|
||||
TextureIndex = GetTextureIndexForCommand((render_command_quads *)Command, Texture);
|
||||
Command->QuadBufferIndex = Commands->QuadIndexCount;
|
||||
}
|
||||
|
||||
range2_r32 Clip = Group->ClipStack[Group->ClipStackUsed];
|
||||
range2_r32 FinalDest = Intersection(Dest, Clip);
|
||||
|
||||
if(InRange(Clip, Dest.Min) || InRange(Clip, Dest.Max))
|
||||
{
|
||||
instanced_quad *Quad = Commands->InstancedQuadBase + Commands->InstancedQuadCount++;
|
||||
Quad->Dest = Dest;
|
||||
Quad->Source = Source;
|
||||
Quad->TextureIndex = TextureIndex;
|
||||
Quad->Colors[0] = PackV4ToU32(Color00);
|
||||
Quad->Colors[1] = PackV4ToU32(Color10);
|
||||
Quad->Colors[2] = PackV4ToU32(Color01);
|
||||
Quad->Colors[3] = PackV4ToU32(Color11);
|
||||
Quad->CornerRadius = CornerRadius;
|
||||
Quad->EdgeSoftness = EdgeSoftness;
|
||||
Quad->BorderThickness = BorderThickness;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void PushTexturedQuad(render_group *Group,
|
||||
range2_r32 Dest,
|
||||
range2_r32 Source,
|
||||
v4 Color00, v4 Color10, v4 Color01, v4 Color11,
|
||||
r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness,
|
||||
render_handle Texture)
|
||||
|
@ -122,33 +162,30 @@ inline void PushTexturedQuad(render_group *Group,
|
|||
}
|
||||
|
||||
range2_r32 Clip = Group->ClipStack[Group->ClipStackUsed];
|
||||
range2_r32 Dest = Range2R32(P, P + Dim);
|
||||
|
||||
v2 DestMin = Max(Dest.Min, Clip.Min);
|
||||
v2 DestMax = Min(Dest.Max, Clip.Max);
|
||||
|
||||
if(InRange(Clip, P) || InRange(Clip, P + Dim))
|
||||
// sixten(TODO): Proper aabb overlap testing here!
|
||||
if(InRange(Clip, Dest.Min) || InRange(Clip, Dest.Max))
|
||||
{
|
||||
v2 HalfSize = Dim*0.5;
|
||||
v2 HalfSize = DimOfRange(Dest)*0.5;
|
||||
|
||||
v2 P00 = V2(DestMin.x, DestMin.y);
|
||||
v2 P01 = V2(DestMin.x, DestMax.y);
|
||||
v2 P10 = V2(DestMax.x, DestMin.y);
|
||||
v2 P11 = V2(DestMax.x, DestMax.y);
|
||||
v2 Center = P + HalfSize;
|
||||
v2 Center = (DestMin + DestMax)*0.5;
|
||||
|
||||
u32 Width = Texture.U32[2];
|
||||
u32 Height = Texture.U32[3];
|
||||
v2 TextureSize = V2(Texture.U32[2], Texture.U32[3]);
|
||||
|
||||
SourceP.x /= Width;
|
||||
SourceP.y /= Height;
|
||||
SourceDim.x /= Width;
|
||||
SourceDim.y /= Height;
|
||||
Source.Min /= TextureSize;
|
||||
Source.Max /= TextureSize;
|
||||
|
||||
v2 Source00 = SourceP;
|
||||
v2 Source01 = SourceP + V2(0, SourceDim.y);
|
||||
v2 Source10 = SourceP + V2(SourceDim.x, 0);
|
||||
v2 Source11 = SourceP + SourceDim;
|
||||
v2 Source00 = Source.Min;
|
||||
v2 Source01 = V2(Source.Min.x, Source.Max.y);
|
||||
v2 Source10 = V2(Source.Max.x, Source.Min.y);
|
||||
v2 Source11 = Source.Max;;
|
||||
|
||||
s32 BaseVertex = Commands->QuadVertexCount;
|
||||
|
||||
|
@ -213,19 +250,19 @@ inline void PushTexturedQuad(render_group *Group,
|
|||
#endif
|
||||
}
|
||||
|
||||
inline void PushQuad(render_group *Group, v2 P, v2 Dim,
|
||||
inline void PushQuad(render_group *Group, range2_r32 Dest,
|
||||
v4 Color00, v4 Color01, v4 Color10, v4 Color11,
|
||||
r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness)
|
||||
{
|
||||
PushTexturedQuad(Group, P, Dim, V2(0, 0), V2(0, 0), Color00, Color01, Color10, Color11,
|
||||
PushTexturedQuad(Group, Dest, Range2R32(V2(0, 0), V2(0, 0)), Color00, Color01, Color10, Color11,
|
||||
CornerRadius, EdgeSoftness, BorderThickness, Group->Commands->WhiteTexture);
|
||||
}
|
||||
|
||||
inline void PushQuad(render_group *Group, v2 P, v2 Dim,
|
||||
inline void PushQuad(render_group *Group, range2_r32 Dest,
|
||||
v4 Color,
|
||||
r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness)
|
||||
{
|
||||
PushTexturedQuad(Group, P, Dim, V2(0, 0), V2(0, 0), Color, Color, Color, Color,
|
||||
PushTexturedQuad(Group, Dest, Range2R32(V2(0, 0), V2(0, 0)), Color, Color, Color, Color,
|
||||
CornerRadius, EdgeSoftness, BorderThickness, Group->Commands->WhiteTexture);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#define VN_RENDER_H
|
||||
|
||||
#define MAX_BOUND_TEXTURES 16
|
||||
#define MAX_QUAD_COUNT 16*1024
|
||||
#define MAX_QUAD_COUNT 128*1024
|
||||
|
||||
#define ColorFromHex(Value) V4((((Value) >> 24) & 0xFF) / 255.0, (((Value) >> 16) & 0xFF) / 255.0, (((Value) >> 8) & 0xFF) / 255.0, (((Value) >> 0) & 0xFF) / 255.0)
|
||||
|
||||
|
@ -21,8 +21,11 @@ read_only v4 Color_Yellow = V4(1, 1, 0, 1);
|
|||
read_only v4 Color_Magenta = V4(1, 0, 1, 1);
|
||||
read_only v4 Color_Cyan = V4(0, 1, 1, 1);
|
||||
|
||||
#define SetAlpha(Color, Value) V4(Color.x, Color.y, Color.z, Value)
|
||||
|
||||
enum render_texture_format
|
||||
{
|
||||
Render_TextureFormat_Invalid,
|
||||
Render_TextureFormat_R8,
|
||||
Render_TextureFormat_RGB8,
|
||||
Render_TextureFormat_RGBA8,
|
||||
|
@ -41,6 +44,7 @@ enum render_command_type
|
|||
{
|
||||
Render_Command_render_command_clear,
|
||||
Render_Command_render_command_quads,
|
||||
//Render_Command_render_command_instanced_quads,
|
||||
Render_Command_render_command_clip,
|
||||
};
|
||||
|
||||
|
@ -78,6 +82,19 @@ struct render_group
|
|||
s32 ClipStackUsed;
|
||||
};
|
||||
|
||||
#if 0
|
||||
struct instanced_quad
|
||||
{
|
||||
range2_r32 Dest;
|
||||
range2_r32 Source;
|
||||
u32 TextureIndex;
|
||||
u32 Colors[4];
|
||||
r32 CornerRadius;
|
||||
r32 EdgeSoftness;
|
||||
r32 BorderThickness;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct quad_vertex
|
||||
{
|
||||
v2 P;
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
////////////////////////////////
|
||||
//~ sixten: Scene Message Functions
|
||||
static void S_PushMessage(memory_arena *Arena, scene_message_list *Messages, scene_node *Node, scene_message_type Type, string String)
|
||||
{
|
||||
scene_message *Message = PushStruct(Arena, scene_message);
|
||||
Message->Type = Type;
|
||||
Message->Node = Node;
|
||||
Message->String = String;
|
||||
QueuePush(Messages->First, Messages->Last, Message);
|
||||
Messages->Count += 1;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Scene Node Functions
|
||||
static scene_node *S_SceneFromText(memory_arena *Arena, string Filename, string Text)
|
||||
{
|
||||
tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Filename, Text, TokenGroup_Comment|TokenGroup_Whitespace);
|
||||
scene_parse_result ParseResult = S_ParseFromTokens(Arena, Filename, Text, TokenizeResult.Tokens);
|
||||
scene_node *Result = ParseResult.Root;
|
||||
return(Result);
|
||||
}
|
||||
////////////////////////////////
|
||||
//~ sixten: Tokens -> Syntax Tree
|
||||
static scene_parse_result S_ParseFromTokens(memory_arena *Arena, string Filename, string Text, token_array Tokens)
|
||||
{
|
||||
temporary_memory Scratch = GetScratch(&Arena, 1);
|
||||
|
||||
//scene_node *Root = {};
|
||||
|
||||
//- sixten: setup parse context
|
||||
scene_parse_context Context = {};
|
||||
{
|
||||
Context.TokensStart = Tokens.Tokens;
|
||||
Context.TokensEnd = Context.TokensStart + Tokens.Count;
|
||||
Context.Token = Context.TokensStart;
|
||||
}
|
||||
|
||||
//- sixten: parse top-level
|
||||
for(;Context.Token < Context.TokensEnd;)
|
||||
{
|
||||
string TokenString = PushString(Scratch.Arena, T_StringFromToken(Text, *Context.Token));
|
||||
|
||||
if(T_TokenMatches(*Context.Token, TokenFlag_Identifier, Text, StrLit("proc")))
|
||||
{
|
||||
Context.Token += 1;
|
||||
|
||||
goto TokenConsumed;
|
||||
}
|
||||
//- sixten: token was not consumed, something has gone wrong
|
||||
{
|
||||
Context.Token += 1;
|
||||
}
|
||||
TokenConsumed:;
|
||||
}
|
||||
|
||||
ReleaseScratch(Scratch);
|
||||
|
||||
scene_parse_result Result =
|
||||
{
|
||||
0
|
||||
};
|
||||
return(Result);
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/* date = July 9th 2023 11:08 am */
|
||||
|
||||
#ifndef VN_SCENE_H
|
||||
#define VN_SCENE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Scene Node Types
|
||||
enum scene_node_kind
|
||||
{
|
||||
S_NodeKind_Invalid,
|
||||
S_NodeKind_File,
|
||||
S_NodeKind_Proc,
|
||||
S_NodeKind_Text,
|
||||
S_NodeKind_Branch,
|
||||
S_NodeKind_Count,
|
||||
};
|
||||
|
||||
struct scene_node
|
||||
{
|
||||
scene_node *Next;
|
||||
scene_node *Prev;
|
||||
scene_node *First;
|
||||
scene_node *Last;
|
||||
scene_node *Parent;
|
||||
|
||||
scene_node *FirstTag;
|
||||
scene_node *LastTag;
|
||||
|
||||
scene_node_kind Kind;
|
||||
string String;
|
||||
|
||||
u64 SourceOffset;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Scene Messages
|
||||
enum scene_message_type
|
||||
{
|
||||
S_MessageType_Invalid,
|
||||
S_MessageType_Note,
|
||||
S_MessageType_Warning,
|
||||
S_MessageType_Error,
|
||||
};
|
||||
|
||||
struct scene_message
|
||||
{
|
||||
scene_message_type Type;
|
||||
scene_message *Next;
|
||||
scene_node *Node;
|
||||
string String;
|
||||
};
|
||||
|
||||
struct scene_message_list
|
||||
{
|
||||
scene_message *First;
|
||||
scene_message *Last;
|
||||
s64 Count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Text -> Tokens
|
||||
struct scene_tokenize_result
|
||||
{
|
||||
token_array Tokens;
|
||||
scene_message_list Messages;
|
||||
};
|
||||
|
||||
struct scene_parse_context
|
||||
{
|
||||
token *TokensStart;
|
||||
token *TokensEnd;
|
||||
token *Token;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Tokens -> Syntax Tree
|
||||
struct scene_parse_result
|
||||
{
|
||||
scene_node *Root;
|
||||
scene_message_list Messages;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Scene Message Functions
|
||||
static void S_PushMessage(memory_arena *Arena, scene_message_list *Messages, scene_node *Node, scene_message_type Type, string String);;
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Scene Node Functions
|
||||
static scene_node *S_PushNode(memory_arena *Arena, scene_node_kind Kind, string String);
|
||||
static scene_node *S_SceneFromText(memory_arena *Arena, string Text);
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Tokens -> Syntax Tree
|
||||
static scene_node *S_ParseProc(scene_parse_context *Context, string ProcName);
|
||||
static scene_parse_result S_ParseFromTokens(memory_arena *Arena, string Filename, string Text, token_array Tokens);
|
||||
|
||||
#endif //VN_SCENE_H
|
|
@ -0,0 +1,34 @@
|
|||
@table(Name, NameLower, Contents) scene_ast_node_types:
|
||||
{
|
||||
{ Invalid invalid `` }
|
||||
{ BlockStatement block_statement `scene_ast_node *First; scene_ast_node *Last;` }
|
||||
{ SceneDecl scene_declaration `` }
|
||||
}
|
||||
|
||||
@table_gen_enum scene_ast_node_type:
|
||||
{
|
||||
@expand(scene_ast_node_types s) `S_AstNode_$(s.Name),`;
|
||||
`S_AstNode_Count,`:
|
||||
}
|
||||
|
||||
@table_gen
|
||||
{
|
||||
`struct scene_ast_node;`
|
||||
}
|
||||
|
||||
@table_gen
|
||||
{
|
||||
@expand(scene_ast_node_types s) `struct scene_ast_node_$(s.NameLower) {$(s.Contents)};`
|
||||
}
|
||||
|
||||
@table_gen
|
||||
{
|
||||
`struct scene_ast_node`;
|
||||
`{`;
|
||||
`scene_ast_node_type Type;`;
|
||||
`union`;
|
||||
`{`;
|
||||
@expand(scene_ast_node_types s) `scene_ast_node_$(s.NameLower) $(s.Name)Data;`;
|
||||
`};`;
|
||||
`};`;
|
||||
}
|
|
@ -26,6 +26,10 @@ enum
|
|||
TextActionFlag_Delete = (1<<2),
|
||||
TextActionFlag_ZeroDeltaWithSelection = (1<<3),
|
||||
TextActionFlag_DeltaPicksSelectionSide = (1<<4),
|
||||
TextActionFlag_OperateOnLine = (1<<5),
|
||||
TextActionFlag_StopOnNewline = (1<<6),
|
||||
|
||||
TextActionFlag_EmptyLineScan = TextActionFlag_WordScan,
|
||||
};
|
||||
|
||||
struct text_action
|
||||
|
@ -98,7 +102,82 @@ static text_action SingleLineTextActionFromEvent(platform_event *Event)
|
|||
return(Action);
|
||||
}
|
||||
|
||||
inline s64 CodepointScan(string String, s64 Index, s64 Delta)
|
||||
static text_action MultiLineTextActionFromEvent(platform_event *Event)
|
||||
{
|
||||
text_action Action = {};
|
||||
|
||||
if(Event->Type == PlatformEvent_Text)
|
||||
{
|
||||
Action.Codepoint = Event->Codepoint;
|
||||
}
|
||||
else if(Event->Type == PlatformEvent_Press)
|
||||
{
|
||||
if(Event->Modifiers & PlatformModifier_Ctrl)
|
||||
{
|
||||
Action.Flags |= TextActionFlag_WordScan;
|
||||
}
|
||||
if(Event->Modifiers & PlatformModifier_Shift)
|
||||
{
|
||||
Action.Flags |= TextActionFlag_KeepMark;
|
||||
}
|
||||
|
||||
switch(Event->Key)
|
||||
{
|
||||
case Key_Right:
|
||||
{
|
||||
Action.Delta = +1;
|
||||
Action.Flags |= TextActionFlag_DeltaPicksSelectionSide;
|
||||
} break;
|
||||
|
||||
case Key_Left:
|
||||
{
|
||||
Action.Delta = -1;
|
||||
Action.Flags |= TextActionFlag_DeltaPicksSelectionSide;
|
||||
} break;
|
||||
|
||||
case Key_Down:
|
||||
{
|
||||
Action.Delta = +1;
|
||||
Action.Flags |= TextActionFlag_OperateOnLine;
|
||||
} break;
|
||||
|
||||
case Key_Up:
|
||||
{
|
||||
Action.Delta = -1;
|
||||
Action.Flags |= TextActionFlag_OperateOnLine;
|
||||
} break;
|
||||
|
||||
case Key_Home:
|
||||
{
|
||||
Action.Delta = S64_Min;
|
||||
Action.Flags |= TextActionFlag_StopOnNewline;
|
||||
} break;
|
||||
|
||||
case Key_End:
|
||||
{
|
||||
Action.Delta = S64_Max;
|
||||
Action.Flags |= TextActionFlag_StopOnNewline;
|
||||
} break;
|
||||
|
||||
case Key_Backspace:
|
||||
{
|
||||
Action.Delta = -1;
|
||||
Action.Flags |= TextActionFlag_Delete|TextActionFlag_ZeroDeltaWithSelection;
|
||||
} break;
|
||||
|
||||
case Key_Delete:
|
||||
{
|
||||
Action.Delta = +1;
|
||||
Action.Flags |= TextActionFlag_Delete|TextActionFlag_ZeroDeltaWithSelection;
|
||||
} break;
|
||||
|
||||
default: {} break;
|
||||
}
|
||||
}
|
||||
return(Action);
|
||||
}
|
||||
|
||||
inline s64 CodepointScan(string String, s64 Index, s64 Delta, b32 StopOnNewline)
|
||||
{
|
||||
s64 Result = 0;
|
||||
if(Delta > 0)
|
||||
|
@ -108,6 +187,11 @@ inline s64 CodepointScan(string String, s64 Index, s64 Delta)
|
|||
u8 Base = String.Data[Index];
|
||||
s64 ToMove = 0;
|
||||
|
||||
if(StopOnNewline && Base == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if((Base & 0x80) == 0x00)
|
||||
{
|
||||
ToMove = 1;
|
||||
|
@ -138,6 +222,13 @@ inline s64 CodepointScan(string String, s64 Index, s64 Delta)
|
|||
while(Index >= 0 && (Delta != 0))
|
||||
{
|
||||
u8 Base = String.Data[Index];
|
||||
|
||||
if(StopOnNewline && Base == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if(((Base & 0x80) == 0) || !((Base & 0xC0) == 0x80))
|
||||
{
|
||||
++Delta;
|
||||
|
@ -218,7 +309,8 @@ static s64 WordScan(string String, s64 Index, s64 Delta)
|
|||
}
|
||||
|
||||
static text_op TextOpFromAction(memory_arena *Arena, string String,
|
||||
text_edit_state *State, text_action *Action)
|
||||
text_edit_state *State, text_action *Action,
|
||||
range1_s64_array *Lines = 0, s64 LastColumnIndex = 0)
|
||||
{
|
||||
text_op Op = {};
|
||||
|
||||
|
@ -228,15 +320,57 @@ static text_op TextOpFromAction(memory_arena *Arena, string String,
|
|||
Op.ReplaceString = StrLit("");
|
||||
|
||||
s64 Delta = 0;
|
||||
if(Action->Flags & TextActionFlag_OperateOnLine)
|
||||
{
|
||||
if(Action->Flags & TextActionFlag_EmptyLineScan)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
//- sixten: determine what line we are on.
|
||||
s64 LineIndex = 0;
|
||||
s64 ColumnIndex = 0;
|
||||
u8 *TextBegin = String.Data;
|
||||
u8 *TextEnd = TextBegin + State->Cursor;
|
||||
u8 *Char = TextBegin;
|
||||
for(;Char < TextEnd; Char += 1)
|
||||
{
|
||||
ColumnIndex += 1;
|
||||
if(*Char == '\n')
|
||||
{
|
||||
LineIndex += 1;
|
||||
ColumnIndex = 0;
|
||||
}
|
||||
}
|
||||
u64 ColumnOffset = Max(LastColumnIndex - ColumnIndex, 0LLU);
|
||||
|
||||
//- sixten: check that the line we are trying to access is inbouse, else just go to the start or end of the text.
|
||||
if(InRange(Range1S64(0, Lines->Count), LineIndex + Action->Delta))
|
||||
{
|
||||
Delta = Lines->Ranges[LineIndex+Action->Delta].Min - Lines->Ranges[LineIndex].Min + ColumnOffset;
|
||||
if(!InRange(Lines->Ranges[LineIndex+Action->Delta], State->Cursor + Delta))
|
||||
{
|
||||
Delta = Lines->Ranges[LineIndex+Action->Delta].Max - State->Cursor - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s64 TempDelta = (Action->Delta > 0)?S64_Max:S64_Min;
|
||||
Delta = CodepointScan(String, State->Cursor, TempDelta, Action->Flags & TextActionFlag_StopOnNewline);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Action->Flags & TextActionFlag_WordScan)
|
||||
{
|
||||
Delta = WordScan(String, State->Cursor, Action->Delta);
|
||||
}
|
||||
else
|
||||
{
|
||||
Delta = CodepointScan(String, State->Cursor, Action->Delta);
|
||||
Delta = CodepointScan(String, State->Cursor, Action->Delta, Action->Flags & TextActionFlag_StopOnNewline);
|
||||
}
|
||||
}
|
||||
|
||||
if(State->Cursor != State->Mark &&
|
||||
Action->Flags & TextActionFlag_ZeroDeltaWithSelection)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,288 @@
|
|||
|
||||
////////////////////////////////
|
||||
//~ sixten: Token Type Functions
|
||||
static string T_StringFromToken(string Text, token Token)
|
||||
{
|
||||
string Result = Substring(Text, Token.Range);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static b32 T_TokenMatches(token Token, token_flags Flags, string Text, string String)
|
||||
{
|
||||
b32 Result = (Token.Flags & Flags && AreEqual(T_StringFromToken(Text, Token), String));
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static void T_TokenChunkListPush(memory_arena *Arena, token_chunk_list *List, token Token, s64 MaxTokenCountPerNode)
|
||||
{
|
||||
token_chunk_node *Node = List->Last;
|
||||
if(!Node || Node->Count >= Node->MaxCount)
|
||||
{
|
||||
Node = PushStruct(Arena, token_chunk_node);
|
||||
Node->Count = 0;
|
||||
Node->MaxCount = MaxTokenCountPerNode;
|
||||
Node->Tokens = PushArrayNoClear(Arena, token, Node->MaxCount);
|
||||
QueuePush(List->First, List->Last, Node);
|
||||
}
|
||||
Node->Tokens[Node->Count] = Token;
|
||||
Node->Count += 1;
|
||||
List->Count += 1;
|
||||
}
|
||||
|
||||
static token_array T_TokenArrayFromList(memory_arena *Arena, token_chunk_list *List)
|
||||
{
|
||||
token_array Result = {};
|
||||
Result.Tokens = PushArrayNoClear(Arena, token, List->Count);
|
||||
Result.Count = List->Count;
|
||||
s64 Index = 0;
|
||||
for(token_chunk_node *Node = List->First; Node != 0; Node = Node->Next)
|
||||
{
|
||||
Copy(Result.Tokens + Index, Node->Tokens, sizeof(token)*Node->Count);
|
||||
Index += Node->Count;
|
||||
}
|
||||
return(Result);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Tokenizer Message Functions
|
||||
|
||||
static void T_MessageListPush(memory_arena *Arena, tokenizer_message_list *List, tokenizer_message_kind Kind, s64 Offset, string String)
|
||||
{
|
||||
tokenizer_message *Message = PushStructNoClear(Arena, tokenizer_message);
|
||||
Message->Kind = Kind;
|
||||
Message->Offset = Offset;
|
||||
Message->String = String;
|
||||
QueuePush(List->First, List->Last, Message);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Text -> Token Functions
|
||||
|
||||
static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, string Text, token_flags ExcludeFilter)
|
||||
{
|
||||
temporary_memory Scratch = GetScratch(&Arena, 1);
|
||||
token_chunk_list Tokens = {};
|
||||
tokenizer_message_list Messages = {};
|
||||
u8 *TextStart = Text.Data;
|
||||
u8 *TextEnd = TextStart + Text.Count;
|
||||
u8 *Byte = TextStart;
|
||||
|
||||
//- sixten: scan string & produce tokens
|
||||
for(;Byte < TextEnd;)
|
||||
{
|
||||
token_flags TokenFlags = 0;
|
||||
u8 *TokenStart = 0;
|
||||
u8 *TokenEnd = 0;
|
||||
|
||||
//- sixten: whitespace
|
||||
if(TokenFlags == 0 && (*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r'))
|
||||
{
|
||||
TokenFlags = TokenFlag_Whitespace;
|
||||
TokenStart = Byte;
|
||||
TokenEnd = Byte;
|
||||
Byte += 1;
|
||||
for(;Byte <= TextEnd; Byte += 1)
|
||||
{
|
||||
TokenEnd += 1;
|
||||
if(Byte == TextEnd || !(*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: newlines
|
||||
if(TokenFlags == 0 && *Byte == '\n')
|
||||
{
|
||||
TokenFlags = TokenFlag_Newline;
|
||||
TokenStart = Byte;
|
||||
TokenEnd = Byte + 1;
|
||||
Byte += 1;
|
||||
}
|
||||
|
||||
//- sixten: single-line comments
|
||||
if(TokenFlags == 0 && (Byte[0] == '/' && Byte[1] == '/'))
|
||||
{
|
||||
TokenFlags = TokenFlag_Comment;
|
||||
TokenStart = Byte;
|
||||
TokenEnd = Byte + 2;
|
||||
Byte += 2;
|
||||
for(;Byte <= TextEnd; Byte += 1)
|
||||
{
|
||||
if(Byte == TextEnd || *Byte == '\n' || *Byte == '\r')
|
||||
{
|
||||
break;
|
||||
}
|
||||
TokenEnd += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: multi-line comments
|
||||
if(TokenFlags == 0 && (Byte+1 < TextEnd && Byte[0] == '/' && Byte[1] == '*'))
|
||||
{
|
||||
TokenFlags = TokenFlag_Comment;
|
||||
TokenStart = Byte;
|
||||
TokenEnd = Byte + 2;
|
||||
Byte += 2;
|
||||
for(;Byte <= TextEnd; Byte += 1)
|
||||
{
|
||||
// sixten(NOTE): This could potentially be wrong. The TokenEnd += 1 statement could currently make the token include the EOF.
|
||||
if(Byte == TextEnd)
|
||||
{
|
||||
TokenFlags |= TokenFlag_BrokenComment;
|
||||
break;
|
||||
}
|
||||
TokenEnd += 1;
|
||||
if(Byte+1 < TextEnd && Byte[0] == '*' && Byte[1] == '/')
|
||||
{
|
||||
TokenEnd += 1;
|
||||
Byte += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: identifiers
|
||||
if(TokenFlags == 0 && (('A' <= *Byte && *Byte <= 'Z') ||
|
||||
('a' <= *Byte && *Byte <= 'z') ||
|
||||
(UTF8Lengths[*Byte>>3] > 1) ||
|
||||
*Byte == '_'))
|
||||
{
|
||||
TokenFlags = TokenFlag_Identifier;
|
||||
TokenStart = Byte;
|
||||
TokenEnd = Byte;
|
||||
Byte += 1;
|
||||
for(;Byte <= TextEnd; Byte += 1)
|
||||
{
|
||||
TokenEnd += 1;
|
||||
if(Byte == TextEnd || !(('A' <= *Byte && *Byte <= 'Z') ||
|
||||
('a' <= *Byte && *Byte <= 'z') ||
|
||||
('0' <= *Byte && *Byte <= '9') ||
|
||||
(UTF8Lengths[*Byte>>3] > 1) ||
|
||||
*Byte == '_'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: numerics
|
||||
if(TokenFlags == 0 && (('0' <= *Byte && *Byte <= '9') ||
|
||||
*Byte == '.' ||
|
||||
((*Byte == '+' || *Byte == '-') && Byte + 1 < TextEnd && '0' <= Byte[1] && Byte[1] <= '9')))
|
||||
{
|
||||
TokenFlags = TokenFlag_Numeric;
|
||||
TokenStart = Byte;
|
||||
TokenEnd = Byte;
|
||||
Byte += 1;
|
||||
for(;Byte <= TextEnd; Byte += 1)
|
||||
{
|
||||
TokenEnd += 1;
|
||||
if(Byte == TextEnd ||
|
||||
!(('A' <= *Byte && *Byte <= 'Z') ||
|
||||
('a' <= *Byte && *Byte <= 'z') ||
|
||||
('0' <= *Byte && *Byte <= '9') ||
|
||||
*Byte == '_' || *Byte == '.'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: string literals
|
||||
if(TokenFlags == 0 && *Byte == '"')
|
||||
{
|
||||
TokenFlags = TokenFlag_StringLiteral;
|
||||
TokenStart = Byte;
|
||||
TokenEnd = Byte;
|
||||
Byte += 1;
|
||||
for(;Byte <= TextEnd; Byte += 1)
|
||||
{
|
||||
TokenEnd += 1;
|
||||
if(Byte == TextEnd || *Byte == '\n')
|
||||
{
|
||||
TokenFlags |= TokenFlag_BrokenStringLiteral;
|
||||
break;
|
||||
}
|
||||
if(*Byte == '"')
|
||||
{
|
||||
Byte += 1;
|
||||
TokenEnd += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: multi-char symbols
|
||||
if(TokenFlags == 0 && (*Byte == '!' || *Byte == '%' || *Byte == '&' || *Byte == '|' ||
|
||||
*Byte == '/' || *Byte == '=' || *Byte == '?' || *Byte == '^' ||
|
||||
*Byte == '*' || *Byte == '+' || *Byte == '-' || *Byte == '$' ||
|
||||
*Byte == '<' || *Byte == '>' || *Byte == '~' || *Byte == '\''))
|
||||
{
|
||||
TokenFlags = TokenFlag_Symbol;
|
||||
TokenStart = Byte;
|
||||
TokenEnd = Byte;
|
||||
Byte += 1;
|
||||
for(;Byte <= TextEnd; Byte += 1)
|
||||
{
|
||||
TokenEnd += 1;
|
||||
if(Byte == TextEnd ||
|
||||
!(*Byte == '!' || *Byte == '%' || *Byte == '&' || *Byte == '|' ||
|
||||
*Byte == '/' || *Byte == '=' || *Byte == '?' || *Byte == '^' ||
|
||||
*Byte == '*' || *Byte == '+' || *Byte == '-' || *Byte == '$' ||
|
||||
*Byte == '<' || *Byte == '>' || *Byte == '~' || *Byte == '\''))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: single-char symbols
|
||||
if(TokenFlags == 0 && (*Byte == '{' || *Byte == '}' || *Byte == '(' || *Byte == ')' ||
|
||||
*Byte == '[' || *Byte == ']' || *Byte == ',' || *Byte == ';' ||
|
||||
*Byte == ':' || *Byte == '@' || *Byte == '#'))
|
||||
{
|
||||
TokenFlags = TokenFlag_Reserved;
|
||||
TokenStart = Byte;
|
||||
TokenEnd = Byte + 1;
|
||||
Byte += 1;
|
||||
}
|
||||
|
||||
//- sixten: bad character
|
||||
if(TokenFlags == 0)
|
||||
{
|
||||
TokenFlags = TokenFlag_BadCharacter;
|
||||
TokenStart = Byte;
|
||||
TokenStart = Byte + 1;
|
||||
Byte += 1;
|
||||
}
|
||||
|
||||
//- sixten: push token
|
||||
if(TokenFlags != 0 && !(TokenFlags & ExcludeFilter) && TokenStart != 0 && TokenEnd > TokenStart)
|
||||
{
|
||||
token Token = {TokenFlags, {TokenStart - TextStart, TokenEnd - TextStart}};
|
||||
T_TokenChunkListPush(Scratch.Arena, &Tokens, Token, 4096);
|
||||
}
|
||||
|
||||
if(TokenFlags & TokenFlag_BrokenComment)
|
||||
{
|
||||
string Message = StrLit("broken comment");
|
||||
T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message);
|
||||
}
|
||||
|
||||
if(TokenFlags & TokenFlag_BrokenStringLiteral)
|
||||
{
|
||||
string Message = StrLit("broken string literal");
|
||||
T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message);
|
||||
}
|
||||
}
|
||||
|
||||
tokenize_result Result =
|
||||
{
|
||||
T_TokenArrayFromList(Arena, &Tokens),
|
||||
Messages
|
||||
};
|
||||
ReleaseScratch(Scratch);
|
||||
return(Result);
|
||||
}
|
||||
|
|
@ -1,122 +1,118 @@
|
|||
/* date = June 18th 2023 5:12 pm */
|
||||
/* date = July 9th 2023 8:04 pm */
|
||||
|
||||
#ifndef VN_TOKENIZER_H
|
||||
#define VN_TOKENIZER_H
|
||||
|
||||
enum token_type
|
||||
////////////////////////////////
|
||||
//~ sixten: Token Types
|
||||
typedef u32 token_flags;
|
||||
enum
|
||||
{
|
||||
Token_EndOfFile,
|
||||
Token_Identifier,
|
||||
Token_IntegerValue,
|
||||
//Token_RealValue,
|
||||
Token_CurlyOpen,
|
||||
Token_CurlyClose,
|
||||
Token_Semicolon,
|
||||
Token_Equals,
|
||||
TokenFlag_Identifier = (1<<0),
|
||||
TokenFlag_Numeric = (1<<1),
|
||||
TokenFlag_StringLiteral = (1<<2),
|
||||
TokenFlag_Symbol = (1<<3),
|
||||
TokenFlag_Reserved = (1<<4),
|
||||
TokenFlag_Comment = (1<<5),
|
||||
TokenFlag_Whitespace = (1<<6),
|
||||
TokenFlag_Newline = (1<<7),
|
||||
|
||||
TokenFlag_BrokenComment = (1<<8),
|
||||
TokenFlag_BrokenStringLiteral = (1<<9),
|
||||
TokenFlag_BadCharacter = (1<<10),
|
||||
};
|
||||
|
||||
struct tokenizer
|
||||
typedef u32 token_group;
|
||||
enum
|
||||
{
|
||||
string Input;
|
||||
s64 At;
|
||||
s64 Index;
|
||||
TokenGroup_Comment = TokenFlag_Comment,
|
||||
TokenGroup_Whitespace = (TokenFlag_Whitespace |
|
||||
TokenFlag_Newline),
|
||||
TokenGroup_Irregular = (TokenGroup_Comment |
|
||||
TokenGroup_Whitespace),
|
||||
TokenGroup_Regular = ~TokenGroup_Irregular,
|
||||
TokenGroup_Label = (TokenFlag_Identifier |
|
||||
TokenFlag_Numeric |
|
||||
TokenFlag_StringLiteral |
|
||||
TokenFlag_Symbol),
|
||||
TokenGroup_Error = (TokenFlag_BrokenComment |
|
||||
TokenFlag_BrokenStringLiteral |
|
||||
TokenFlag_BadCharacter),
|
||||
};
|
||||
|
||||
struct token
|
||||
{
|
||||
token_type Type;
|
||||
token_flags Flags;
|
||||
range1_s64 Range;
|
||||
};
|
||||
|
||||
struct token_chunk_node
|
||||
{
|
||||
token *Tokens;
|
||||
s64 MaxCount;
|
||||
s64 Count;
|
||||
token_chunk_node *Next;
|
||||
};
|
||||
|
||||
struct token_chunk_list
|
||||
{
|
||||
token_chunk_node *First;
|
||||
token_chunk_node *Last;
|
||||
s64 Count;
|
||||
};
|
||||
|
||||
struct token_array
|
||||
{
|
||||
token *Tokens;
|
||||
s64 Count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Tokenizer Message Types
|
||||
enum tokenizer_message_kind
|
||||
{
|
||||
T_MessageKind_Invalid,
|
||||
T_MessageKind_Note,
|
||||
T_MessageKind_Warning,
|
||||
T_MessageKind_Error,
|
||||
};
|
||||
|
||||
struct tokenizer_message
|
||||
{
|
||||
tokenizer_message *Next;
|
||||
tokenizer_message_kind Kind;
|
||||
s64 Offset;
|
||||
string String;
|
||||
};
|
||||
|
||||
inline tokenizer Tokenizer_BeginTokenization(string Input)
|
||||
struct tokenizer_message_list
|
||||
{
|
||||
tokenizer Result = {};
|
||||
Result.Input = Input;
|
||||
tokenizer_message *First;
|
||||
tokenizer_message *Last;
|
||||
s64 Count;
|
||||
};
|
||||
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static token Tokenizer_GetNextToken(tokenizer *Tokenizer)
|
||||
////////////////////////////////
|
||||
//~ sixten: Text -> Token Types
|
||||
struct tokenize_result
|
||||
{
|
||||
token Token = {};
|
||||
token_array Tokens;
|
||||
tokenizer_message_list Messages;
|
||||
};
|
||||
|
||||
string Input = Tokenizer->Input;
|
||||
u8 *Base = Input.Data;
|
||||
////////////////////////////////
|
||||
//~ sixten: Token Type Functions
|
||||
static string T_StringFromToken(string Text, token Token);
|
||||
static b32 T_TokenMatches(token Token, token_flags Flags, string Text, string String);
|
||||
static void T_TokenChunkListPush(memory_arena *Arena, token_chunk_list *List, token Token, s64 MaxTokenCountPerNode);
|
||||
static token_array T_TokenArrayFromChunkList(memory_arena *Arena, token_chunk_list *List);
|
||||
|
||||
// sixten: Consume whitespace
|
||||
while(IsWhitespace(Base[Tokenizer->Index]))
|
||||
{
|
||||
++Tokenizer->Index;
|
||||
}
|
||||
////////////////////////////////
|
||||
//~ sixten: Tokenizer Message Functions
|
||||
static void T_MessageListPush(memory_arena *Arena, tokenizer_message_list *List, tokenizer_message_kind Kind, s64 Offset, string String);
|
||||
|
||||
// sixten(NOTE): Assume single char token.
|
||||
Token.String.Data = Base + Tokenizer->Index;
|
||||
Token.String.Count = 1;
|
||||
|
||||
if(Tokenizer->Index < Input.Count)
|
||||
{
|
||||
switch(Base[Tokenizer->Index])
|
||||
{
|
||||
case '{': { Token.Type = Token_CurlyOpen; } break;
|
||||
case '}': { Token.Type = Token_CurlyClose; } break;
|
||||
case ';': { Token.Type = Token_Semicolon; } break;
|
||||
case '=': { Token.Type = Token_Equals; } break;
|
||||
|
||||
default:
|
||||
{
|
||||
if(IsDigit(Base[Tokenizer->Index]) || Base[Tokenizer->Index] == '-')
|
||||
{
|
||||
// sixten: Parse integer number
|
||||
Token.Type = Token_IntegerValue;
|
||||
|
||||
Token.String.Data = Base + Tokenizer->Index;
|
||||
Token.String.Count = 0;
|
||||
|
||||
while(IsDigit(Token.String.Data[Token.String.Count]) || Token.String.Data[Token.String.Count] == '-')
|
||||
{
|
||||
++Token.String.Count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// sixten: Parse tokenizer
|
||||
Token.Type = Token_Identifier;
|
||||
|
||||
Token.String.Data = Base + Tokenizer->Index;
|
||||
Token.String.Count = 0;
|
||||
|
||||
while(IsDigit(Token.String.Data[Token.String.Count]) ||
|
||||
IsLetter(Token.String.Data[Token.String.Count]))
|
||||
{
|
||||
++Token.String.Count;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Token.Type = Token_EndOfFile;
|
||||
}
|
||||
|
||||
Tokenizer->Index += Token.String.Count;
|
||||
|
||||
return(Token);
|
||||
}
|
||||
|
||||
inline token Tokenizer_PeekNextToken(tokenizer Tokenizer)
|
||||
{
|
||||
// sixten(NOTE): Yup, we just make a copy of the tokenizer and read the next token.
|
||||
token Result = Tokenizer_GetNextToken(&Tokenizer);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
inline b32 Tokenizer_RequireToken(tokenizer *Tokenizer, token_type Type)
|
||||
{
|
||||
token Token = Tokenizer_GetNextToken(Tokenizer);
|
||||
|
||||
b32 Result = (Token.Type == Type);
|
||||
return(Result);
|
||||
}
|
||||
////////////////////////////////
|
||||
//~ sixten: Text -> Token Functions
|
||||
static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename, string Text, token_flags ExcludeFilter = 0);
|
||||
|
||||
#endif //VN_TOKENIZER_H
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "generated/vn_generated_ui.cpp"
|
||||
#include "generated/vn_ui.meta.c"
|
||||
|
||||
inline ui_size UI_Pixels(r32 Value, r32 Strictness)
|
||||
{
|
||||
|
@ -54,6 +54,25 @@ inline ui_key UI_GetActive(void)
|
|||
return(UI->Active);
|
||||
}
|
||||
|
||||
inline platform_event_list *UI_EventList(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->EventList);
|
||||
}
|
||||
|
||||
inline v2 UI_MouseP(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->MouseP);
|
||||
}
|
||||
|
||||
inline glyph_atlas *UI_GlyphAtlas(void)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
return(UI->GlyphAtlas);
|
||||
}
|
||||
|
||||
|
||||
inline void UI_SetDragStartP(v2 P)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
|
@ -207,7 +226,7 @@ inline ui_box *UI_GetBoxByKey(ui *UI, ui_key Key)
|
|||
if(DLLIsEmpty(UI->FirstFreeBox))
|
||||
{
|
||||
// sixten: If not, simply allocate one
|
||||
Result = PushStruct(&UI->Arena, ui_box);
|
||||
Result = PushStruct(UI->Arena, ui_box);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -259,7 +278,7 @@ inline ui_box *UI_MakeBox(ui_box_flags Flags, string String)
|
|||
Box->LastFrameTouched = UI->CurrentFrame;
|
||||
|
||||
Box->Flags = Flags;
|
||||
Box->String = PushString(&UI->FrameArena, String);
|
||||
Box->String = PushString(UI->FrameArena, String);
|
||||
|
||||
s64 HashIndex = LastIndexOf(Box->String, '#');
|
||||
if(HashIndex != -1)
|
||||
|
@ -286,7 +305,7 @@ inline ui_box *UI_MakeBoxF(ui_box_flags Flags, char *Format, ...)
|
|||
|
||||
va_list Arguments;
|
||||
va_start(Arguments, Format);
|
||||
string String = PushFormatVariadic(&UI->FrameArena, Format, Arguments);
|
||||
string String = PushFormatVariadic(UI->FrameArena, Format, Arguments);
|
||||
va_end(Arguments);
|
||||
|
||||
ui_box *Box = UI_MakeBox(Flags, String);
|
||||
|
@ -335,6 +354,11 @@ static b32 UI_ChildrenContainsP(ui_box *Parent, v2 P, range2_r32 Clip)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(Child == Parent->Last)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(Result);
|
||||
|
@ -358,7 +382,21 @@ static ui_signal UI_SignalFromBox(ui_box *Box)
|
|||
}
|
||||
}
|
||||
|
||||
Signal.Hovering = InRange(ClippedRect, UI->MouseP) && !UI_ChildrenContainsP(Box, UI->MouseP, ClippedRect);
|
||||
Signal.Hovering = InRange(ClippedRect, UI->MouseP);
|
||||
|
||||
// sixten: Make sure no children boxes overlap.
|
||||
Signal.Hovering &= !UI_ChildrenContainsP(Box, UI->MouseP, ClippedRect);
|
||||
|
||||
// sixten: Make sure no previous boxes overlap.
|
||||
if(Box->Prev)
|
||||
{
|
||||
ui_box PrevBoxParent = {};
|
||||
PrevBoxParent.Rect = Box->Rect;
|
||||
PrevBoxParent.First = Box->Parent->First;
|
||||
PrevBoxParent.Last = Box->Prev;
|
||||
|
||||
Signal.Hovering &= !UI_ChildrenContainsP(&PrevBoxParent, UI->MouseP, ClippedRect);
|
||||
}
|
||||
|
||||
// sixten: Make sure the tooltip is not overlapping.
|
||||
{
|
||||
|
@ -557,23 +595,23 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas
|
|||
|
||||
if(Box->Flags & UI_BoxFlag_DrawBackground)
|
||||
{
|
||||
PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Box->BackgroundColor, Box->CornerRadius, 0, 0);
|
||||
PushQuad(Group, Box->Rect, Box->BackgroundColor, Box->CornerRadius, 0, 0);
|
||||
}
|
||||
|
||||
if(Box->Flags & UI_BoxFlag_HotAnimation)
|
||||
{
|
||||
v4 Top = V4(1, 1, 1, 0.06F*Box->HotTransition);
|
||||
v4 Top = V4(1, 1, 1, 0.08F*Box->HotTransition);
|
||||
v4 Bottom = V4(1, 1, 1, 0.0);
|
||||
|
||||
PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0);
|
||||
PushQuad(Group, Box->Rect, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0);
|
||||
}
|
||||
|
||||
if(Box->Flags & UI_BoxFlag_ActiveAnimation)
|
||||
{
|
||||
v4 Top = V4(0, 0, 0, 0.5F*Box->ActiveTransition);
|
||||
v4 Top = V4(0, 0, 0, 0.7F*Box->ActiveTransition);
|
||||
v4 Bottom = V4(0, 0, 0, 0.1F*Box->ActiveTransition);
|
||||
|
||||
PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0);
|
||||
PushQuad(Group, Box->Rect, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0);
|
||||
}
|
||||
|
||||
if(Box->Flags & UI_BoxFlag_DrawText)
|
||||
|
@ -599,7 +637,7 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas
|
|||
r32 G = (((Box->Key.Value >> 21) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1));
|
||||
r32 B = (((Box->Key.Value >> 42) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1));
|
||||
v4 Red = V4R32(R, G, B, 1);
|
||||
PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Red, Red, Red, Red, 0, 1.8, 1.8);
|
||||
PushQuad(Group, Box->Rect, Red, Red, Red, Red, 0, 1.8, 1.8);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -619,9 +657,12 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas
|
|||
v2 P = Child->Rect.Min - V2(ShadowRadius, ShadowRadius);
|
||||
v2 Dim = Child->ComputedDim + V2(ShadowRadius, ShadowRadius)*2;
|
||||
|
||||
range2_r32 Rect = Range2R32(Box->Rect.Min - V2R32(ShadowRadius, ShadowRadius),
|
||||
Box->Rect.Min + V2R32(ShadowRadius, ShadowRadius));
|
||||
|
||||
v4 ShadowColor = V4(0, 0, 0, 0.7);
|
||||
|
||||
PushQuad(Group, P, Dim, ShadowColor, 0, ShadowRadius, 0);
|
||||
PushQuad(Group, Rect, ShadowColor, 0, ShadowRadius, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,7 +680,7 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas
|
|||
|
||||
if(Box->Flags & UI_BoxFlag_DrawBorder)
|
||||
{
|
||||
PushQuad(Group, Box->Rect.Min, Box->ComputedDim, Box->BorderColor, Box->CornerRadius, 0.8, Box->BorderThickness);
|
||||
PushQuad(Group, Box->Rect, Box->BorderColor, Box->CornerRadius, 0.8, Box->BorderThickness);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -673,7 +714,13 @@ static r32 UI_CalculateBoxSize(ui_box *Box, axis2 Axis)
|
|||
{
|
||||
if(Parent && Parent->SemanticSize[Axis].Type != UI_SizeType_ChildrenSum)
|
||||
{
|
||||
Result = Parent->ComputedDim.E[Axis]*Box->SemanticSize[Axis].Value;
|
||||
r32 Size = Parent->ComputedDim.E[Axis];
|
||||
// sixten: if the size is zero, try to find it.
|
||||
if(Size == 0.0)
|
||||
{
|
||||
Size = DimOfRange(Parent->Rect).E[Axis];
|
||||
}
|
||||
Result = Size*Box->SemanticSize[Axis].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -743,6 +790,12 @@ static void UI_LayoutBox(ui_box *Box)
|
|||
}
|
||||
}
|
||||
|
||||
static void UI_Init(ui *UI)
|
||||
{
|
||||
UI->Arena = ArenaAllocate(Gigabytes(1));
|
||||
UI->FrameArena = ArenaAllocate(Gigabytes(1));
|
||||
}
|
||||
|
||||
static void UI_BeginBuild(v2 ScreenDim)
|
||||
{
|
||||
ui *UI = UI_GetState();
|
||||
|
@ -837,12 +890,7 @@ static void UI_NewFrame(ui *UI, platform_event_list *EventList, v2 MouseP, glyph
|
|||
{
|
||||
UI_SetState(UI);
|
||||
|
||||
if(UI->FrameMemory.Arena)
|
||||
{
|
||||
EndTemporaryMemory(UI->FrameMemory);
|
||||
}
|
||||
|
||||
UI->FrameMemory = BeginTemporaryMemory(&UI->FrameArena);
|
||||
ArenaClear(UI->FrameArena);
|
||||
|
||||
UI->EventList = EventList;
|
||||
UI->MouseP = MouseP;
|
||||
|
|
19
code/vn_ui.h
|
@ -52,10 +52,12 @@ enum
|
|||
UI_BoxFlag_OverflowY = (1 << 9),
|
||||
UI_BoxFlag_FloatingX = (1 << 10),
|
||||
UI_BoxFlag_FloatingY = (1 << 11),
|
||||
UI_BoxFlag_Scrollable = (1 << 12),
|
||||
};
|
||||
typedef u32 ui_box_flags;
|
||||
|
||||
typedef void ui_draw_callback(render_group *Group, glyph_atlas *Atlas, struct ui_box *Box, void *Data);
|
||||
#define UI_CUSTOM_DRAW_CALLBACK(name) void name(render_group *Group, glyph_atlas *Atlas, struct ui_box *Box, void *Data)
|
||||
typedef UI_CUSTOM_DRAW_CALLBACK(ui_custom_draw_callback);
|
||||
|
||||
struct ui_box
|
||||
{
|
||||
|
@ -86,7 +88,7 @@ struct ui_box
|
|||
r32 FontSize;
|
||||
v2 Offset;
|
||||
|
||||
ui_draw_callback *DrawCallback;
|
||||
ui_custom_draw_callback *DrawCallback;
|
||||
void *DrawCallbackData;
|
||||
|
||||
v2 ComputedRelativeP;
|
||||
|
@ -109,6 +111,7 @@ struct ui_signal
|
|||
ui_box *Box;
|
||||
v2 MouseP;
|
||||
v2 DragDelta;
|
||||
v2 Scroll;
|
||||
b8 Clicked;
|
||||
b8 Pressed;
|
||||
b8 PressedRight;
|
||||
|
@ -117,13 +120,12 @@ struct ui_signal
|
|||
b8 Dragging;
|
||||
};
|
||||
|
||||
#include "generated/vn_generated_ui.h"
|
||||
#include "generated/vn_ui.meta.h"
|
||||
|
||||
struct ui
|
||||
{
|
||||
memory_arena Arena;
|
||||
memory_arena FrameArena;
|
||||
temporary_memory FrameMemory;
|
||||
memory_arena *Arena;
|
||||
memory_arena *FrameArena;
|
||||
ui_box *FirstFreeBox;
|
||||
ui_box *LastFreeBox;
|
||||
|
||||
|
@ -158,6 +160,10 @@ inline ui *UI_GetState(void);
|
|||
inline ui_key UI_GetHot(void);
|
||||
inline ui_key UI_GetActive(void);
|
||||
|
||||
inline platform_event_list *UI_EventList(void);
|
||||
inline v2 UI_MouseP(void);
|
||||
inline glyph_atlas *UI_GlyphAtlas(void);
|
||||
|
||||
//- sixten: Drag helpers
|
||||
inline void UI_SetDragStartP(v2 P);
|
||||
inline void UI_UpdateDragStartP(void);
|
||||
|
@ -186,6 +192,7 @@ static ui_box *UI_MakeBoxF(ui_box_flags Flags, char *Format, ...);
|
|||
static ui_signal UI_SignalFromBox(ui_box *Box);
|
||||
|
||||
//- sixten: Building and rendering
|
||||
static void UI_Init(ui *UI);
|
||||
static void UI_BeginBuild(ui *UI, v2 ScreenDim);
|
||||
static void UI_EndBuild(void);
|
||||
static void UI_RenderFrame(render_group *RenderGroup);
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
@table(Name, Type, BoxName)
|
||||
UIStyleStacks:
|
||||
{
|
||||
{ Parent `ui_box *` `Parent` }
|
||||
{ Width `ui_size` `SemanticSize[Axis2_X]` }
|
||||
{ Height `ui_size` `SemanticSize[Axis2_Y]` }
|
||||
{ FixedX `r32` `FixedP.E[Axis2_X]` }
|
||||
{ FixedY `r32` `FixedP.E[Axis2_Y]` }
|
||||
{ TextColor `v4` `TextColor` }
|
||||
{ BackgroundColor `v4` `BackgroundColor` }
|
||||
{ BorderColor `v4` `BorderColor` }
|
||||
{ BorderThickness `r32` `BorderThickness` }
|
||||
{ LayoutAxis `axis2` `LayoutAxis` }
|
||||
{ CornerRadius `r32` `CornerRadius` }
|
||||
{ Font `font_id` `Font` }
|
||||
{ FontSize `r32` `FontSize` }
|
||||
{ OffsetX `r32` `Offset.x` }
|
||||
{ OffsetY `r32` `Offset.y` }
|
||||
}
|
||||
|
||||
@table_gen
|
||||
{
|
||||
`struct ui_style_stacks`;
|
||||
`{`;
|
||||
@expand(UIStyleStacks s) `$(s.Type) $(s.Name)Stack[64]; s32 $(s.Name)StackUsed; b32 AutoPop$(s.Name);`;
|
||||
`};`;
|
||||
}
|
||||
|
||||
@c @table_gen
|
||||
{
|
||||
@expand(UIStyleStacks s) `inline void UI_Push$(s.Name)($(s.Type) Element) $(=>50) { ui *UI = UI_GetState(); Assert(UI->Stacks.$(s.Name)StackUsed $(=>125) + 1 < ArrayCount(UI->Stacks.$(s.Name)Stack)); $(=>180) UI->Stacks.$(s.Name)Stack[UI->Stacks.$(s.Name)StackUsed++] $(=>260) = Element; }`;
|
||||
@expand(UIStyleStacks s) `inline void UI_Pop$(s.Name)(void) $(=>50) { ui *UI = UI_GetState(); Assert(UI->Stacks.$(s.Name)StackUsed $(=>125) > 0); $(=>180) --UI->Stacks.$(s.Name)StackUsed; $(=>271) }`;
|
||||
@expand(UIStyleStacks s) `inline void UI_SetNext$(s.Name)($(s.Type) Element) { ui *UI = UI_GetState(); UI_Push$(s.Name)(Element); UI->Stacks.AutoPop$(s.Name) = true; }`;
|
||||
@expand(UIStyleStacks s) `inline $(s.Type) UI_First$(s.Name)(void) { ui *UI = UI_GetState(); return(UI->Stacks.$(s.Name)Stack[0]); }`;
|
||||
@expand(UIStyleStacks s) `inline $(s.Type) UI_Top$(s.Name)(void) { ui *UI = UI_GetState(); return(UI->Stacks.$(s.Name)Stack[UI->Stacks.$(s.Name)StackUsed - 1]); }`;
|
||||
@expand(UIStyleStacks s) `#define UI_$(s.Name)(Element) DeferLoop(UI_Push$(s.Name)(Element), UI_Pop$(s.Name)())`
|
||||
}
|
||||
|
||||
@c @table_gen
|
||||
{
|
||||
`static void UI_ApplyStyles(ui_box *Box)`;
|
||||
`{`;
|
||||
`ui *UI = UI_GetState();`;
|
||||
@expand(UIStyleStacks s) `Assert(UI->Stacks.$(s.Name)StackUsed > 0); $(=>50) Box->$(s.BoxName) $(=>75) = UI->Stacks.$(s.Name)Stack[UI->Stacks.$(s.Name)StackUsed - 1]; $(=>160) if(UI->Stacks.AutoPop$(s.Name)) $(=>200) { UI_Pop$(s.Name)(); $(=>230) UI->Stacks.AutoPop$(s.Name) = false; $(=>260) }`
|
||||
`}`;
|
||||
}
|
|
@ -223,13 +223,6 @@ static ui_signal UI_Scrollbar(axis2 Axis, string Name, r32 Size, r32 Offset)
|
|||
return(Signal);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Goal for next week: Be able to make basic dialog trees.
|
||||
Goal for tomorrow: Scrollable regions and basic text editing (with line wrapping).
|
||||
|
||||
*/
|
||||
|
||||
static void UI_ScrollBegin(r32 *X, r32 *Y, string Name)
|
||||
{
|
||||
u32 Flags = 0;
|
||||
|
@ -270,6 +263,7 @@ static void UI_ScrollEnd(r32 *X, r32 *Y)
|
|||
b32 AllowOnY = (Y != 0);
|
||||
|
||||
ui_box *ScrollableBox = UI_TopParent();
|
||||
ui_signal ScrollableBoxSignal = UI_SignalFromBox(ScrollableBox);
|
||||
{
|
||||
{
|
||||
UI_PopParent();
|
||||
|
|
|
@ -1,12 +1,28 @@
|
|||
#include "vn_workspace_view.cpp"
|
||||
#include "vn_workspace_editor.cpp"
|
||||
#include "vn_workspace_text_editor.cpp"
|
||||
#include "vn_workspace_commands.cpp"
|
||||
|
||||
//- sixten: State management
|
||||
per_thread workspace *ThreadLocal_Workspace;
|
||||
|
||||
static void Workspace_SetState(workspace *Workspace)
|
||||
{
|
||||
ThreadLocal_Workspace = Workspace;
|
||||
}
|
||||
|
||||
static workspace *Workspace_GetState(void)
|
||||
{
|
||||
return(ThreadLocal_Workspace);
|
||||
}
|
||||
|
||||
//- sixten: Commands
|
||||
static void Workspace_IssueCommand(workspace *Workspace, workspace_command_sig *Sig, u64 Argument = 0)
|
||||
static void Workspace_IssueCommand(workspace_command_sig *Sig, u64 Argument = 0)
|
||||
{
|
||||
workspace_command *Result = 0;
|
||||
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
if(Workspace->FirstFreeCommand)
|
||||
{
|
||||
Result = Workspace->FirstFreeCommand;
|
||||
|
@ -15,7 +31,7 @@ static void Workspace_IssueCommand(workspace *Workspace, workspace_command_sig *
|
|||
|
||||
if(!Result)
|
||||
{
|
||||
Result = PushStruct(&Workspace->CommandArena, workspace_command);
|
||||
Result = PushStruct(Workspace->CommandArena, workspace_command);
|
||||
}
|
||||
|
||||
Result->Command = Sig;
|
||||
|
@ -23,12 +39,14 @@ static void Workspace_IssueCommand(workspace *Workspace, workspace_command_sig *
|
|||
DLLInsertLast(Workspace->FirstCommand, Workspace->LastCommand, Result);
|
||||
}
|
||||
|
||||
static void Workspace_ProcessCommands(workspace *Workspace)
|
||||
static void Workspace_ProcessCommands(void)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
workspace_command *Command = Workspace->FirstCommand;
|
||||
while(Command != 0)
|
||||
{
|
||||
Command->Command(Workspace, Command->Argument);
|
||||
Command->Command(Command->Argument);
|
||||
|
||||
workspace_command *ToRemove = Command;
|
||||
Command = Command->Next;
|
||||
|
@ -39,8 +57,10 @@ static void Workspace_ProcessCommands(workspace *Workspace)
|
|||
}
|
||||
}
|
||||
|
||||
static void Workspace_ProcessKeyBinds(workspace *Workspace)
|
||||
static void Workspace_ProcessKeyBinds()
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
platform_event_list *EventList = Workspace->EventList;
|
||||
|
||||
for(platform_event *Event = EventList->First;
|
||||
|
@ -56,7 +76,7 @@ static void Workspace_ProcessKeyBinds(workspace *Workspace)
|
|||
workspace_keybind *Keybind = Workspace->Keybinds + KeybindIndex;
|
||||
if((Event->Key == Keybind->Key) && (Event->Modifiers == Keybind->Modifiers))
|
||||
{
|
||||
Workspace_IssueCommand(Workspace, Keybind->Command, Keybind->Argument);
|
||||
Workspace_IssueCommand(Keybind->Command, Keybind->Argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +85,10 @@ static void Workspace_ProcessKeyBinds(workspace *Workspace)
|
|||
|
||||
|
||||
//- sixten: Builder code
|
||||
static ui_signal Workspace_BuildToolbarButton(workspace *Workspace, char *Text, toolbar_menu Menu)
|
||||
static ui_signal Workspace_BuildToolbarButton(char *Text, toolbar_menu Menu)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
UI_SetNextWidth(UI_TextContent(20, 1));
|
||||
UI_SetNextHeight(UI_Pixels(30, 1));
|
||||
UI_SetNextCornerRadius(4);
|
||||
|
@ -136,8 +158,10 @@ static ui_signal Workspace_BuildMenuItem(u32 Icon, char *Text, char *Shortcut)
|
|||
return(Signal);
|
||||
}
|
||||
|
||||
static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame)
|
||||
static void Workspace_BuildToolbar()
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
UI_SetNextLayoutAxis(Axis2_X);
|
||||
UI_SetNextHeight(UI_Pixels(30, 1));
|
||||
UI_SetNextBackgroundColor(Theme_BackgroundColor);
|
||||
|
@ -147,9 +171,9 @@ static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame)
|
|||
|
||||
UI_Parent(ToolbarBox)
|
||||
{
|
||||
Workspace_BuildToolbarButton(Workspace, "Panel", ToolbarMenu_Panel);
|
||||
Workspace_BuildToolbarButton(Workspace, "View", ToolbarMenu_View);
|
||||
Workspace_BuildToolbarButton(Workspace, "Window", ToolbarMenu_Window);
|
||||
Workspace_BuildToolbarButton("Panel", ToolbarMenu_Panel);
|
||||
Workspace_BuildToolbarButton("View", ToolbarMenu_View);
|
||||
Workspace_BuildToolbarButton("Window", ToolbarMenu_Window);
|
||||
}
|
||||
|
||||
if(Workspace->Menu != ToolbarMenu_None)
|
||||
|
@ -179,13 +203,12 @@ static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame)
|
|||
{
|
||||
if(Workspace_BuildMenuItem(FontIcon_ResizeHorizontal, "Split Horizontal", "Ctrl + P").Clicked)
|
||||
{
|
||||
Workspace_IssueCommand(Workspace, Workspace_Command_SplitPanelHorizontal);
|
||||
Workspace_IssueCommand(Workspace_Command_SplitPanelHorizontal);
|
||||
Workspace->Menu = ToolbarMenu_None;
|
||||
}
|
||||
|
||||
if(Workspace_BuildMenuItem(FontIcon_ResizeVertical, "Split Vertical", "Ctrl + L").Clicked)
|
||||
{
|
||||
Workspace_IssueCommand(Workspace, Workspace_Command_SplitPanelVertical);
|
||||
Workspace_IssueCommand(Workspace_Command_SplitPanelVertical);
|
||||
Workspace->Menu = ToolbarMenu_None;
|
||||
}
|
||||
}
|
||||
|
@ -193,28 +216,28 @@ static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame)
|
|||
{
|
||||
workspace_panel *CurrentPanel = Workspace->CurrentPanel;
|
||||
|
||||
// sixten(TODO): The views should be created at the end of the frame i.e. this should be done via
|
||||
// commmands instead. (since we want all things that deals with major state changes to occur at a fixed
|
||||
// point in our programs execution)
|
||||
|
||||
if(Workspace_BuildMenuItem(FontIcon_None, "Welcome", "").Clicked)
|
||||
{
|
||||
Workspace_CreateNewView(Workspace_View_Startup, CurrentPanel);
|
||||
|
||||
Workspace->Menu = ToolbarMenu_None;
|
||||
}
|
||||
if(Workspace_BuildMenuItem(FontIcon_None, "Editor", "").Clicked)
|
||||
{
|
||||
Workspace_CreateNewView(Workspace_View_Editor, CurrentPanel);
|
||||
|
||||
Workspace->Menu = ToolbarMenu_None;
|
||||
}
|
||||
if(Workspace_BuildMenuItem(FontIcon_None, "Command Palette", "").Clicked)
|
||||
{
|
||||
Workspace_CreateNewView(Workspace_View_CommandPalette, CurrentPanel);
|
||||
|
||||
Workspace->Menu = ToolbarMenu_None;
|
||||
}
|
||||
if(Workspace_BuildMenuItem(FontIcon_Wrench, "Settings", "").Clicked)
|
||||
{
|
||||
Workspace_CreateNewView(Workspace_View_Settings, CurrentPanel);
|
||||
|
||||
Workspace->Menu = ToolbarMenu_None;
|
||||
}
|
||||
}
|
||||
|
@ -239,13 +262,15 @@ static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame)
|
|||
}
|
||||
|
||||
//- sixten: Panels
|
||||
static workspace_panel *Workspace_CreateNewPanel(workspace *Workspace, workspace_panel *Parent)
|
||||
static workspace_panel *Workspace_CreateNewPanel(workspace_panel *Parent)
|
||||
{
|
||||
workspace_panel *Result = 0;
|
||||
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
if(DLLIsEmpty(Workspace->FirstFreePanel))
|
||||
{
|
||||
Result = PushStruct(&Workspace->PanelArena, workspace_panel);
|
||||
Result = PushStruct(Workspace->PanelArena, workspace_panel);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -260,8 +285,10 @@ static workspace_panel *Workspace_CreateNewPanel(workspace *Workspace, workspace
|
|||
return(Result);
|
||||
}
|
||||
|
||||
static void Workspace_DeletePanel(workspace *Workspace, workspace_panel *Panel)
|
||||
static void Workspace_DeletePanel(workspace_panel *Panel)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
if(Workspace->CurrentPanel == Panel)
|
||||
{
|
||||
Workspace->CurrentPanel = 0;
|
||||
|
@ -271,22 +298,24 @@ static void Workspace_DeletePanel(workspace *Workspace, workspace_panel *Panel)
|
|||
DLLInsertLast(Workspace->FirstFreePanel, Workspace->LastFreePanel, Panel);
|
||||
}
|
||||
|
||||
static void Workspace_SplitPanel(workspace *Workspace, workspace_panel *Panel, axis2 Axis)
|
||||
static void Workspace_SplitPanel(workspace_panel *Panel, axis2 Axis)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
if(Panel)
|
||||
{
|
||||
workspace_panel *Parent = Panel->Parent;
|
||||
|
||||
if(Parent && (Parent->SplitAxis == Axis))
|
||||
{
|
||||
workspace_panel *NewPanel = Workspace_CreateNewPanel(Workspace, Parent);
|
||||
workspace_panel *NewPanel = Workspace_CreateNewPanel(Parent);
|
||||
NewPanel->PercentOfParent = Panel->PercentOfParent = Panel->PercentOfParent * 0.5;
|
||||
|
||||
DLLInsert_NP(Parent->First, Parent->Last, Panel, NewPanel, Next, Prev);
|
||||
}
|
||||
else
|
||||
{
|
||||
workspace_panel *NewPanel = Workspace_CreateNewPanel(Workspace, Panel);
|
||||
workspace_panel *NewPanel = Workspace_CreateNewPanel(Panel);
|
||||
NewPanel->FirstView = Panel->FirstView;
|
||||
NewPanel->LastView = Panel->LastView;
|
||||
|
||||
|
@ -302,7 +331,7 @@ static void Workspace_SplitPanel(workspace *Workspace, workspace_panel *Panel, a
|
|||
NewPanel->PercentOfParent = 0.5;
|
||||
DLLInsertLast(Panel->First, Panel->Last, NewPanel);
|
||||
|
||||
NewPanel = Workspace_CreateNewPanel(Workspace, Panel);
|
||||
NewPanel = Workspace_CreateNewPanel(Panel);
|
||||
NewPanel->PercentOfParent = 0.5;
|
||||
DLLInsertLast(Panel->First, Panel->Last, NewPanel);
|
||||
|
||||
|
@ -319,8 +348,10 @@ static void Workspace_SplitPanel(workspace *Workspace, workspace_panel *Panel, a
|
|||
}
|
||||
}
|
||||
|
||||
inline void Workspace_BeginDrag(workspace *Workspace, workspace_drag_payload *Payload)
|
||||
inline void Workspace_BeginDrag(workspace_drag_payload *Payload)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
// sixten(TODO): Right now, if you spam-click a draggable item, you can trigger this
|
||||
// assertion. I don't know what I want to do about this at the moment, but I'm sure
|
||||
// future me will have a soulution at hand. ^.^
|
||||
|
@ -330,15 +361,19 @@ inline void Workspace_BeginDrag(workspace *Workspace, workspace_drag_payload *Pa
|
|||
Workspace->DragPayloadState = Workspace_DragPayload_Active;
|
||||
}
|
||||
|
||||
inline b32 Workspace_GetDragPayload(workspace *Workspace, workspace_drag_payload *Dest)
|
||||
inline b32 Workspace_GetDragPayload(workspace_drag_payload *Dest)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
b32 Result = (Workspace->DragPayloadState != Workspace_DragPayload_Inactive);
|
||||
*Dest = Workspace->DragPayload;
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static void Workspace_BuildTabItem(workspace *Workspace, workspace_panel *Panel, workspace_view *View)
|
||||
static void Workspace_BuildTabItem(workspace_panel *Panel, workspace_view *View)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
b32 ViewIsCurrent = (Panel->CurrentView == View);
|
||||
b32 PanelIsCurrent = (Workspace->CurrentPanel == Panel);
|
||||
|
||||
|
@ -396,6 +431,7 @@ static void Workspace_BuildTabItem(workspace *Workspace, workspace_panel *Panel,
|
|||
Panel->CurrentView = View;
|
||||
}
|
||||
|
||||
// sixten: Handle dragging the view.
|
||||
if(Signal.Dragging)
|
||||
{
|
||||
if(Signal.Pressed)
|
||||
|
@ -404,13 +440,15 @@ static void Workspace_BuildTabItem(workspace *Workspace, workspace_panel *Panel,
|
|||
Payload.View = View;
|
||||
Payload.Key = TabBox->Key;
|
||||
|
||||
Workspace_BeginDrag(Workspace, &Payload);
|
||||
Workspace_BeginDrag(&Payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Workspace_BuildPanelHeader(workspace *Workspace, workspace_panel *Panel)
|
||||
static void Workspace_BuildPanelHeader(workspace_panel *Panel)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
UI_SetNextLayoutAxis(Axis2_X);
|
||||
UI_SetNextBackgroundColor(ColorFromHex(0x252728FF));
|
||||
UI_SetNextCornerRadius(0);
|
||||
|
@ -422,14 +460,14 @@ static void Workspace_BuildPanelHeader(workspace *Workspace, workspace_panel *Pa
|
|||
View != 0;
|
||||
View = View->Next)
|
||||
{
|
||||
Workspace_BuildTabItem(Workspace, Panel, View);
|
||||
Workspace_BuildTabItem(Panel, View);
|
||||
}
|
||||
|
||||
UI_Spacer(UI_Percent(1, 0));
|
||||
|
||||
// sixten: Panel Close Button
|
||||
if(Panel != Workspace->RootPanel)
|
||||
{
|
||||
UI_Spacer(UI_Percent(1, 0));
|
||||
|
||||
UI_SetNextSize(UI_Pixels(30, 1), UI_Pixels(30, 1));
|
||||
UI_SetNextFont(Font_Icons);
|
||||
UI_SetNextBorderColor(ColorFromHex(0xA6514288));
|
||||
|
@ -447,7 +485,7 @@ static void Workspace_BuildPanelHeader(workspace *Workspace, workspace_panel *Pa
|
|||
ui_signal Signal = UI_SignalFromBox(CloseBox);
|
||||
if(Signal.Clicked)
|
||||
{
|
||||
Workspace_IssueCommand(Workspace, Workspace_Command_ClosePanel, PointerToU64(Panel));
|
||||
Workspace_IssueCommand(Workspace_Command_ClosePanel, PointerToU64(Panel));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,8 +493,10 @@ static void Workspace_BuildPanelHeader(workspace *Workspace, workspace_panel *Pa
|
|||
}
|
||||
}
|
||||
|
||||
static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel)
|
||||
static void Workspace_BuildPanel(workspace_panel *Panel)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
// sixten: Fill remaining percent of parent.
|
||||
workspace_panel *Parent = Panel->Parent;
|
||||
if(Parent && Panel != Parent->First)
|
||||
|
@ -480,7 +520,7 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel)
|
|||
{
|
||||
if(DLLIsEmpty(Panel->First))
|
||||
{
|
||||
Workspace_BuildPanelHeader(Workspace, Panel);
|
||||
Workspace_BuildPanelHeader(Panel);
|
||||
|
||||
// sixten: Main body
|
||||
{
|
||||
|
@ -522,7 +562,7 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel)
|
|||
{
|
||||
ui_key CurrentActive = UI_GetActive();
|
||||
|
||||
Workspace_BuildView(Workspace, Panel->CurrentView);
|
||||
Workspace_BuildView(Panel->CurrentView);
|
||||
|
||||
if(!AreEqual(CurrentActive, UI_GetActive()))
|
||||
{
|
||||
|
@ -533,7 +573,7 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel)
|
|||
// sixten: Draw dragged view overlay.
|
||||
{
|
||||
workspace_drag_payload Payload;
|
||||
b32 DragActive = Workspace_GetDragPayload(Workspace, &Payload);
|
||||
b32 DragActive = Workspace_GetDragPayload(&Payload);
|
||||
|
||||
b32 OverlayActive = (DragActive && (Payload.View->Parent != Panel) &&
|
||||
InRange(BodyBox->Rect, UI_GetState()->MouseP));
|
||||
|
@ -630,8 +670,9 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel)
|
|||
{
|
||||
UI_SetNextAxisSize(Panel->SplitAxis, UI_Percent(Child->PercentOfParent*SizeScalar, 0));
|
||||
UI_SetNextAxisSize(Opposite(Panel->SplitAxis), UI_Percent(1, 0));
|
||||
Workspace_BuildPanel(Workspace, Child);
|
||||
Workspace_BuildPanel(Child);
|
||||
|
||||
// sixten: Create drag area
|
||||
if(Child->Next)
|
||||
{
|
||||
UI_SetNextAxisSize(Panel->SplitAxis, UI_Pixels(PaddingSize, 1));
|
||||
|
@ -667,10 +708,13 @@ static void Workspace_BuildPanel(workspace *Workspace, workspace_panel *Panel)
|
|||
}
|
||||
}
|
||||
|
||||
static void Workspace_BuildDragPayload(workspace *Workspace, vn_input *Input)
|
||||
static void Workspace_BuildDragPayload()
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
vn_input *Input = Workspace->Input;
|
||||
|
||||
workspace_drag_payload Payload;
|
||||
if(Workspace_GetDragPayload(Workspace, &Payload))
|
||||
if(Workspace_GetDragPayload(&Payload))
|
||||
{
|
||||
if(Workspace->DragPayloadState == Workspace_DragPayload_Released)
|
||||
{
|
||||
|
@ -709,15 +753,28 @@ static void Workspace_BuildDragPayload(workspace *Workspace, vn_input *Input)
|
|||
//- sixten: Workspace
|
||||
static void Workspace_Init(workspace *Workspace)
|
||||
{
|
||||
Workspace->RootPanel = Workspace->CurrentPanel = Workspace_CreateNewPanel(Workspace, 0);
|
||||
Workspace_SetState(Workspace);
|
||||
|
||||
Workspace->CommandArena = ArenaAllocate(Gigabytes(1));
|
||||
Workspace->PanelArena = ArenaAllocate(Gigabytes(1));
|
||||
|
||||
Workspace->RootPanel = Workspace->CurrentPanel = Workspace_CreateNewPanel(0);
|
||||
|
||||
if(DEBUG_DebugSettings->ShowWelcomeMessage)
|
||||
{
|
||||
Workspace_CreateNewView(Workspace_View_Startup, Workspace->RootPanel);
|
||||
}
|
||||
Workspace_CreateNewView(Workspace_View_TextEditor, Workspace->RootPanel);
|
||||
|
||||
// sixten: Setup keybinds
|
||||
{
|
||||
#define BIND_COMMAND(...) Workspace->Keybinds[Workspace->KeybindCount++] = {__VA_ARGS__}
|
||||
BIND_COMMAND(Key_P, PlatformModifier_Ctrl, Workspace_Command_SplitPanelHorizontal);
|
||||
BIND_COMMAND(Key_L, PlatformModifier_Ctrl, Workspace_Command_SplitPanelVertical);
|
||||
|
||||
#if VN_INTERNAL
|
||||
BIND_COMMAND(Key_U, PlatformModifier_Ctrl|PlatformModifier_Shift, Workspace_Command_ToggleRenderUIDebugRects);
|
||||
#endif
|
||||
#undef BIND_COMMAND
|
||||
}
|
||||
}
|
||||
|
@ -725,13 +782,12 @@ static void Workspace_Init(workspace *Workspace)
|
|||
static void Workspace_Update(workspace *Workspace, vn_render_commands *RenderCommands,
|
||||
vn_input *Input, glyph_atlas *GlyphAtlas)
|
||||
{
|
||||
Workspace_SetState(Workspace);
|
||||
|
||||
Workspace->Input = Input;
|
||||
Workspace->EventList = Input->EventList;
|
||||
|
||||
// sixten: Process last frame's commands.
|
||||
Workspace_ProcessKeyBinds(Workspace);
|
||||
Workspace_ProcessCommands(Workspace);
|
||||
|
||||
// sixten: Make sure that a view/panel is always selected.
|
||||
if(!Workspace->CurrentPanel)
|
||||
{
|
||||
Workspace->CurrentPanel = Workspace->RootPanel;
|
||||
|
@ -743,12 +799,16 @@ static void Workspace_Update(workspace *Workspace, vn_render_commands *RenderCom
|
|||
|
||||
// sixten: Build the UI.
|
||||
{
|
||||
Workspace_BuildToolbar(Workspace, Input->dtForFrame);
|
||||
Workspace_BuildToolbar();
|
||||
|
||||
UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 0));
|
||||
Workspace_BuildPanel(Workspace, Workspace->RootPanel);
|
||||
Workspace_BuildPanel(Workspace->RootPanel);
|
||||
|
||||
Workspace_BuildDragPayload(Workspace, Input);
|
||||
Workspace_BuildDragPayload();
|
||||
}
|
||||
|
||||
// sixten: Process the keybinds and commands.
|
||||
Workspace_ProcessKeyBinds();
|
||||
Workspace_ProcessCommands();
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
#define VN_WORKSPACE_H
|
||||
|
||||
#include "vn_workspace_editor.h"
|
||||
#include "vn_workspace_text_editor.h"
|
||||
|
||||
// sixten(TODO): Remove this type entirely.
|
||||
enum toolbar_menu
|
||||
|
@ -33,7 +34,7 @@ struct workspace_panel
|
|||
|
||||
#include "vn_workspace_view.h"
|
||||
|
||||
#define WORKSPACE_COMMAND(name, ...) void name(workspace *Workspace, u64 Argument)
|
||||
#define WORKSPACE_COMMAND(name, ...) void name(u64 Argument)
|
||||
typedef WORKSPACE_COMMAND(workspace_command_sig);
|
||||
|
||||
struct workspace_command
|
||||
|
@ -72,7 +73,7 @@ struct workspace
|
|||
platform_event_list *EventList;
|
||||
|
||||
// sixten: Command Allocation
|
||||
memory_arena CommandArena;
|
||||
memory_arena *CommandArena;
|
||||
workspace_command *FirstFreeCommand;
|
||||
workspace_command *LastFreeCommand;
|
||||
|
||||
|
@ -85,7 +86,7 @@ struct workspace
|
|||
s32 KeybindCount;
|
||||
|
||||
// sixten: Panels
|
||||
memory_arena PanelArena;
|
||||
memory_arena *PanelArena;
|
||||
workspace_panel *FirstFreePanel;
|
||||
workspace_panel *LastFreePanel;
|
||||
|
||||
|
@ -100,19 +101,23 @@ struct workspace
|
|||
workspace_panel *CurrentPanel;
|
||||
};
|
||||
|
||||
//- sixten: State management
|
||||
static void Workspace_SetState(workspace *Workspace);
|
||||
static workspace *Workspace_GetState(void);
|
||||
|
||||
//- sixten: Commands
|
||||
static void Workspace_IssueCommand(workspace *Workspace, workspace_command_sig *Sig, u64 Argument);
|
||||
static void Workspace_ProcessCommands(workspace *Workspace);
|
||||
static void Workspace_IssueCommand(workspace_command_sig *Sig, u64 Argument);
|
||||
static void Workspace_ProcessCommands(void);
|
||||
|
||||
//- sixten: Panels
|
||||
static workspace_panel *Workspace_CreateNewPanel(workspace *Workspace, workspace_panel *Parent);
|
||||
static void Workspace_DeletePanel(workspace *Workspace, workspace_panel *Panel);
|
||||
static void Workspace_SplitPanel(workspace *Workspace, workspace_panel *Panel, axis2 Axis);
|
||||
static workspace_panel *Workspace_CreateNewPanel(workspace_panel *Parent);
|
||||
static void Workspace_DeletePanel(workspace_panel *Panel);
|
||||
static void Workspace_SplitPanel(workspace_panel *Panel, axis2 Axis);
|
||||
|
||||
//- sixten: Builder code
|
||||
static ui_signal Workspace_BuildToolbarButton(workspace *Workspace, char *Text, toolbar_menu Menu);
|
||||
static ui_signal Workspace_BuildToolbarButton(char *Text, toolbar_menu Menu);
|
||||
static ui_signal Workspace_BuildMenuItem(u32 Icon, char *Text, char *Shortcut);
|
||||
static void Workspace_BuildToolbar(workspace *Workspace, r32 dtForFrame);
|
||||
static void Workspace_BuildToolbar(r32 dtForFrame);
|
||||
|
||||
//- sixten: Workspace
|
||||
static void Workspace_Init(workspace *Workspace);
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
WORKSPACE_COMMAND(Workspace_Command_SplitPanelHorizontal)
|
||||
{
|
||||
Workspace_SplitPanel(Workspace, Workspace->CurrentPanel, Axis2_X);
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
Workspace_SplitPanel(Workspace->CurrentPanel, Axis2_X);
|
||||
}
|
||||
|
||||
WORKSPACE_COMMAND(Workspace_Command_SplitPanelVertical)
|
||||
{
|
||||
Workspace_SplitPanel(Workspace, Workspace->CurrentPanel, Axis2_Y);
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
Workspace_SplitPanel(Workspace->CurrentPanel, Axis2_Y);
|
||||
}
|
||||
|
||||
WORKSPACE_COMMAND(Workspace_Command_ClosePanel)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
workspace_panel *Panel = (workspace_panel *)Argument;
|
||||
if(!Panel)
|
||||
{
|
||||
|
@ -50,7 +54,7 @@ WORKSPACE_COMMAND(Workspace_Command_ClosePanel)
|
|||
}
|
||||
|
||||
DLLRemove(Parent->First, Parent->Last, Child);
|
||||
Workspace_DeletePanel(Workspace, Child);
|
||||
Workspace_DeletePanel(Child);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -79,5 +83,12 @@ WORKSPACE_COMMAND(Workspace_Command_ClosePanel)
|
|||
//Workspace_DeleteView(Child);
|
||||
}
|
||||
|
||||
Workspace_DeletePanel(Workspace, Panel);
|
||||
Workspace_DeletePanel(Panel);
|
||||
}
|
||||
|
||||
#if VN_INTERNAL
|
||||
WORKSPACE_COMMAND(Workspace_Command_ToggleRenderUIDebugRects)
|
||||
{
|
||||
DEBUG_DebugSettings->RenderUIDebugRects = !DEBUG_DebugSettings->RenderUIDebugRects;
|
||||
}
|
||||
#endif
|
|
@ -9,7 +9,7 @@ static workspace_editor_node *Workspace_GetNewEditorNode(workspace_view *View)
|
|||
|
||||
if(DLLIsEmpty(Editor->FirstFreeNode))
|
||||
{
|
||||
Result = PushStruct(&View->Arena, workspace_editor_node);
|
||||
Result = PushStruct(View->Arena, workspace_editor_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -63,6 +63,126 @@ inline r32 Workspace_CalculateScaleFromZoomLevel(r32 ZoomLevel)
|
|||
|
||||
|
||||
//- sixten: Builder code
|
||||
static void Workspace_BuildEditorListerDropdown(workspace_editor_lister_dropdown *ListerDropdown)
|
||||
{
|
||||
b32 ActiveInDropdown = false;
|
||||
|
||||
UI_PushBackgroundColor(SetAlpha(Color_Black, 0.3));
|
||||
UI_PushBorderColor(SetAlpha(Color_Black, 0.7));
|
||||
|
||||
r32 HeightTransition = AnimationCurve_AnimateValueF(1, 0, 0.3, "Editor Lister Dropdown %p", ListerDropdown);
|
||||
|
||||
UI_SetNextSize(UI_Em(15, 1), UI_Em(25*HeightTransition, 1));
|
||||
UI_SetNextCornerRadius(4.0);
|
||||
UI_SetNextFixedP(ListerDropdown->P);
|
||||
UI_SetNextLayoutAxis(Axis2_Y);
|
||||
|
||||
ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawBackground |
|
||||
UI_BoxFlag_FloatingX |
|
||||
UI_BoxFlag_FloatingY |
|
||||
UI_BoxFlag_Clickable |
|
||||
UI_BoxFlag_Clip,
|
||||
StrLit("Editor Lister Dropdown"));
|
||||
|
||||
UI_Parent(Box)
|
||||
{
|
||||
|
||||
UI_BorderColor(SetAlpha(Color_Grey, 0.7))
|
||||
UI_Row() UI_Column()
|
||||
{
|
||||
UI_Size(UI_TextContent(15, 1), UI_TextContent(12, 1)) UI_LabelF("Create new node");
|
||||
|
||||
// sixten: Build search bar
|
||||
UI_SetNextWidth(UI_Percent(1, 1));
|
||||
UI_SetNextHeight(UI_Em(2, 1));
|
||||
UI_SetNextCornerRadius(4.0);
|
||||
UI_SetNextLayoutAxis(Axis2_X);
|
||||
UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, StrLit("Editor Lister Field")))
|
||||
{
|
||||
UI_Spacer(UI_Em(0.8, 1));
|
||||
UI_Width(UI_TextContent(1, 1)) UI_Font(Font_Icons) UI_LabelF("%U", FontIcon_Filter);
|
||||
UI_Width(UI_Percent(1, 0))
|
||||
{
|
||||
UI_MakeBox(0, StrLit(""));
|
||||
}
|
||||
}
|
||||
|
||||
struct lister_option
|
||||
{
|
||||
char *Text;
|
||||
u32 Icons;
|
||||
};
|
||||
|
||||
lister_option Options[] =
|
||||
{
|
||||
{"Text", FontIcon_Pencil},
|
||||
{"Branch", FontIcon_Menu},
|
||||
};
|
||||
|
||||
for(s32 OptionIndex = 0;
|
||||
OptionIndex < ArrayCount(Options);
|
||||
++OptionIndex)
|
||||
{
|
||||
lister_option *Option = Options + OptionIndex;
|
||||
|
||||
UI_SetNextSize(UI_Percent(1, 1), UI_Em(2, 1));
|
||||
UI_SetNextLayoutAxis(Axis2_X);
|
||||
ui_box *ClickableBox = UI_MakeBoxF(UI_BoxFlag_Clickable, "Editor Lister %s", Option->Text);
|
||||
|
||||
ui_signal Signal = UI_SignalFromBox(ClickableBox);
|
||||
|
||||
if(AreEqual(UI_GetActive(), ClickableBox->Key))
|
||||
{
|
||||
ActiveInDropdown = true;
|
||||
}
|
||||
|
||||
UI_Parent(ClickableBox)
|
||||
UI_Height(UI_Percent(1, 1))
|
||||
{
|
||||
b32 IsHot = AreEqual(UI_GetHot(), ClickableBox->Key);
|
||||
r32 TargetHot = IsHot*0.8;
|
||||
|
||||
UI_SetNextCornerRadius(3.0);
|
||||
UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1));
|
||||
UI_SetNextBackgroundColor(SetAlpha(Theme_HighlightBorderColor,
|
||||
AnimationCurve_AnimateValueF(TargetHot, TargetHot, 0.1, "Editor Lister %s%p",
|
||||
Option->Text, ListerDropdown)));
|
||||
UI_SetNextLayoutAxis(Axis2_X);
|
||||
ui_box *HighlightBox = UI_MakeBox(UI_BoxFlag_DrawBackground, StrLit(""));
|
||||
|
||||
UI_Parent(HighlightBox)
|
||||
UI_Padding(UI_Em(0.8, 1))
|
||||
{
|
||||
UI_Width(UI_TextContent(0, 1))
|
||||
UI_Font(Font_Icons) UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Option->Icons);
|
||||
UI_Spacer(UI_Em(0.6, 1));
|
||||
UI_Width(UI_TextContent(0, 1)) UI_MakeBoxF(UI_BoxFlag_DrawText, "%s", Option->Text);
|
||||
|
||||
if(Signal.Clicked)
|
||||
{
|
||||
// sixten(TODO): Issue the requested command.
|
||||
ListerDropdown->Open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui_signal Signal = UI_SignalFromBox(Box);
|
||||
if(AreEqual(UI_GetActive(), Box->Key))
|
||||
{
|
||||
ActiveInDropdown = true;
|
||||
}
|
||||
}
|
||||
UI_PopBackgroundColor();
|
||||
UI_PopBorderColor();
|
||||
|
||||
if(!ActiveInDropdown && !AreEqual(UI_GetActive(), UI_EmptyKey()))
|
||||
{
|
||||
ListerDropdown->Open = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data)
|
||||
{
|
||||
workspace_view_editor *Editor = (workspace_view_editor *)Data;
|
||||
|
@ -79,7 +199,9 @@ static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas
|
|||
++LineIndex)
|
||||
{
|
||||
r32 OffsetX = Workspace_WorldToView(Editor->Offset.x, Scale, Dim.x, LineIndex - (s32)Editor->Offset.x);
|
||||
PushQuad(Group, Box->Rect.Min + V2(OffsetX, 0), V2(1.5, Dim.y), LineColor, 0, 1.2, 0);
|
||||
v2 Min = Box->Rect.Min + V2(OffsetX, 0);
|
||||
v2 Max = Min + V2(1.5, Dim.y);
|
||||
PushQuad(Group, Range2R32(Min, Max), LineColor, 0, 1.2, 0);
|
||||
}
|
||||
|
||||
s32 HorizontalLineCount = Dim.y / Scale + 4;
|
||||
|
@ -88,18 +210,21 @@ static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas
|
|||
++LineIndex)
|
||||
{
|
||||
r32 OffsetY = Workspace_WorldToView(Editor->Offset.y, Scale, Dim.y, LineIndex - (s32)Editor->Offset.y);
|
||||
v2 Min = Box->Rect.Min + V2(0, OffsetY);
|
||||
v2 Max = Min + V2(Dim.x, 1.5);
|
||||
|
||||
PushQuad(Group, Box->Rect.Min + V2(0, OffsetY), V2(Dim.x, 1.5), LineColor, 0, 1.2, 0);
|
||||
PushQuad(Group, Range2R32(Min, Max), LineColor, 0, 1.2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View)
|
||||
static void Workspace_BuildEditor(workspace_view *View)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
workspace_view_editor *Editor = (workspace_view_editor *)View->Data;
|
||||
|
||||
// sixten(TODO): These should be able to have a strictness of 1, but they can't. Fix that.
|
||||
UI_SetNextWidth(UI_Percent(1, 0));
|
||||
UI_SetNextHeight(UI_Percent(1, 0));
|
||||
UI_SetNextWidth(UI_Percent(1, 1));
|
||||
UI_SetNextHeight(UI_Percent(1, 1));
|
||||
|
||||
ui_box *EditorBox = UI_MakeBoxF(UI_BoxFlag_Clip|UI_BoxFlag_Clickable, "Workspace Editor %p", View);
|
||||
EditorBox->DrawCallback = Workspace_EditorDrawCallback;
|
||||
|
@ -110,6 +235,8 @@ static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View)
|
|||
|
||||
v2 EditorDim = DimOfRange(EditorBox->Rect);
|
||||
|
||||
UI_Parent(EditorBox)
|
||||
{
|
||||
// sixten: Build the node boxes.
|
||||
for(workspace_editor_node *Node = Editor->FirstNode;
|
||||
Node != 0;
|
||||
|
@ -132,8 +259,8 @@ static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View)
|
|||
UI_Parent(Node->Box)
|
||||
{
|
||||
UI_SetNextBackgroundColor(LinearBlend(Theme_BackgroundColor, Color_Black, 0.3));
|
||||
|
||||
UI_SetNextSize(UI_Percent(1, 1), UI_Pixels(20, 1));
|
||||
UI_SetNextLayoutAxis(Axis2_X);
|
||||
UI_SetNextSize(UI_Percent(1, 1), UI_Em(1.8, 1));
|
||||
Node->TitleBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
||||
UI_BoxFlag_DrawBorder |
|
||||
UI_BoxFlag_Clickable,
|
||||
|
@ -141,11 +268,24 @@ static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View)
|
|||
|
||||
UI_Parent(Node->TitleBox)
|
||||
{
|
||||
UI_Width(UI_TextContent(7, 0)) UI_Font(Font_Bold) UI_LabelF("Node");
|
||||
UI_Spacer(UI_Em(0.5, 1));
|
||||
|
||||
UI_Width(UI_TextContent(0, 1)) UI_Font(Font_Bold) UI_LabelF("Node");
|
||||
UI_Spacer(UI_Percent(1, 0));
|
||||
|
||||
UI_SetNextSize(UI_Em(1.8, 1), UI_Percent(1, 1));
|
||||
UI_SetNextFont(Font_Icons);
|
||||
Node->CloseBox = UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", FontIcon_Cancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(Editor->ListerDropdown.Open)
|
||||
{
|
||||
Workspace_BuildEditorListerDropdown(&Editor->ListerDropdown);
|
||||
}
|
||||
}
|
||||
|
||||
// sixten: Get input from boxes.
|
||||
{
|
||||
workspace_editor_node *Next = 0;
|
||||
|
@ -176,10 +316,6 @@ static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View)
|
|||
}
|
||||
}
|
||||
|
||||
// sixten: Process the movement of the node.
|
||||
{
|
||||
}
|
||||
|
||||
// sixten: Process panning and zooming of the editor.
|
||||
ui_signal Signal = UI_SignalFromBox(EditorBox);
|
||||
{
|
||||
|
@ -227,6 +363,16 @@ static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View)
|
|||
// sixten: Process shortcuts.
|
||||
if(Platform_KeyPress(Workspace->EventList, Key_Space, PlatformModifier_Ctrl))
|
||||
{
|
||||
Workspace_GetNewEditorNode(View);
|
||||
workspace_editor_lister_dropdown *ListerDropdown = &Editor->ListerDropdown;
|
||||
|
||||
if(ListerDropdown->Open)
|
||||
{
|
||||
ListerDropdown->Open = false;
|
||||
}
|
||||
else if(InRange(EditorBox->Rect, UI_GetState()->MouseP))
|
||||
{
|
||||
ListerDropdown->Open = true;
|
||||
ListerDropdown->P = UI_GetState()->MouseP - EditorBox->Rect.Min;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,15 +6,16 @@
|
|||
/* sixten(NOTE): Node types
|
||||
*
|
||||
* Text(String, opt. Character) -> Node
|
||||
* Menu(Strings[0..n]) -> Nodes[0..n]
|
||||
* Branch(Strings[0..n]) -> Nodes[0..n]
|
||||
*
|
||||
*/
|
||||
|
||||
enum workspace_editor_node_type
|
||||
{
|
||||
Workspace_EditorNode_None,
|
||||
Workspace_EditorNode_Start,
|
||||
Workspace_EditorNode_Text,
|
||||
Workspace_EditorNode_Menu,
|
||||
Workspace_EditorNode_Branch,
|
||||
};
|
||||
|
||||
struct workspace_editor_node
|
||||
|
@ -26,10 +27,23 @@ struct workspace_editor_node
|
|||
|
||||
ui_box *Box;
|
||||
ui_box *TitleBox;
|
||||
ui_box *CloseBox;
|
||||
|
||||
v2 P;
|
||||
};
|
||||
|
||||
struct workspace_editor_lister_dropdown
|
||||
{
|
||||
// sixten: Input field
|
||||
char InputField[128];
|
||||
s32 InputFieldUsed;
|
||||
b32 InputFieldSelected;
|
||||
|
||||
// sixten: Properties
|
||||
b32 Open;
|
||||
v2 P;
|
||||
};
|
||||
|
||||
//- sixten: Managing nodes
|
||||
static workspace_editor_node *Workspace_GetNewEditorNode(struct workspace_view *View);
|
||||
|
||||
|
@ -38,8 +52,12 @@ inline v2 Workspace_ViewToWorld(v2 Offset, r32 Scale, v2 Dim, v2 P);
|
|||
inline v2 Workspace_WorldToView(v2 Offset, r32 Scale, v2 Dim, v2 P);
|
||||
inline r32 Workspace_CalculateScaleFromZoomLevel(r32 ZoomLevel);
|
||||
|
||||
//- sixten: Lister dropdown
|
||||
static void Workspace_OpenListerDropdown(v2 MouseP);
|
||||
|
||||
//- sixten: Builder code
|
||||
static void Workspace_BuildEditorListerDropdown(workspace_editor_lister_dropdown *ListerDropdown);
|
||||
static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data);
|
||||
static void Workspace_BuildEditor(struct workspace *Workspace, struct workspace_view *View);
|
||||
static void Workspace_BuildEditor(struct workspace_view *View);
|
||||
|
||||
#endif //VN_WORKSPACE_EDITOR_H
|
||||
|
|
|
@ -0,0 +1,362 @@
|
|||
////////////////////////////////
|
||||
//~ sixten: Mutable String Functions
|
||||
|
||||
static mutable_string MutableStringAllocate(u64 Size)
|
||||
{
|
||||
mutable_string Result = {};
|
||||
Result.Arena = ArenaAllocate(Size);
|
||||
ArenaSetAlign(Result.Arena, 1);
|
||||
Result.String = MakeString(PushArray(Result.Arena, u8, 1), 0);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static void MutableStringRelease(mutable_string *String)
|
||||
{
|
||||
ArenaRelease(String->Arena);
|
||||
}
|
||||
|
||||
static void MutableStringReplaceRange(mutable_string *MutString, string ReplaceString, range1_s64 Range)
|
||||
{
|
||||
Range = Intersection(Range, Range1S64(0, MutString->String.Count));
|
||||
|
||||
s64 NewCount = MutString->String.Count+ReplaceString.Count-DimOfRange(Range);
|
||||
if(NewCount > MutString->String.Count)
|
||||
{
|
||||
s64 ToAllocate = NewCount-MutString->String.Count;
|
||||
PushArray(MutString->Arena, u8, ToAllocate);
|
||||
}
|
||||
|
||||
Move(MutString->String.Data+Range.Min+ReplaceString.Count, MutString->String.Data+Range.Max, MutString->String.Count-DimOfRange(Range));
|
||||
Copy(MutString->String.Data+Range.Min, ReplaceString.Data, ReplaceString.Count);
|
||||
|
||||
MutString->String.Count = NewCount;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Workspace Text Editor Functions
|
||||
|
||||
static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *Arena, string Text)
|
||||
{
|
||||
temporary_memory Scratch = GetScratch(&Arena, 1);
|
||||
|
||||
//- sixten: tokenize the text
|
||||
tokenize_result TokenizeResult = T_TokenizeFromText(Arena, StrLit("*scratch*"), Text);
|
||||
token_array Tokens = TokenizeResult.Tokens;
|
||||
|
||||
//- sixten: gather all line ranges
|
||||
range1_s64_list Lines = {};
|
||||
{
|
||||
u8 *TextBegin = Text.Data;
|
||||
u8 *TextEnd = TextBegin + Text.Count;
|
||||
range1_s64 Range = Range1S64(0, 0);
|
||||
for(u8 *Char = TextBegin; Char <= TextEnd; Char += 1)
|
||||
{
|
||||
Range.Max += 1;
|
||||
|
||||
//- sixten: push line range on newline and EOF
|
||||
if(Char == TextEnd || *Char == '\n')
|
||||
{
|
||||
Range1S64ListPush(Scratch.Arena, &Lines, Range);
|
||||
Range = Range1S64(Range.Max, Range.Max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: fill & return
|
||||
workspace_text_data Result = {};
|
||||
{
|
||||
Result.Tokens = Tokens;
|
||||
Result.Lines = Range1S64ArrayFromList(Arena, &Lines);;
|
||||
}
|
||||
ReleaseScratch(Scratch);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback)
|
||||
{
|
||||
temporary_memory Scratch = GetScratch();
|
||||
workspace_view_text_editor *Editor = (workspace_view_text_editor *)Data;
|
||||
|
||||
//- sixten: get dimensions & scroll offset from container
|
||||
ui_box *ContainerBox = Editor->ContainerBox;
|
||||
range2_r32 ParentRect = ContainerBox->Rect;
|
||||
v2 ParentDim = DimOfRange(ParentRect);
|
||||
v2 Offset = Box->Parent->Offset;
|
||||
|
||||
//- sixten: rendering properties
|
||||
r32 FontSize = 16.0f;
|
||||
r32 LineHeight = FontSize + 4.0f;
|
||||
|
||||
//- sixten: calculate the dimensions of the glyphs
|
||||
glyph *Glyph = GetGlyph(Atlas, Font_Monospace, 'A', FontSize, 0);
|
||||
r32 GlyphAdvance = Glyph->Advance;
|
||||
|
||||
//- sixten: find the text point
|
||||
text_point CursorTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor);
|
||||
text_point MarkTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Mark);
|
||||
|
||||
//- sixten: get the line count
|
||||
range1_s64_array *Lines = &Editor->Lines;
|
||||
s64 LineCount = Lines->Count;
|
||||
|
||||
//- sixten: calculate the text dim
|
||||
Editor->TextDim = V2(1900, LineCount*LineHeight);
|
||||
|
||||
//- sixten: calculate the line margin dim
|
||||
s32 LineMarginDigitsRequired = 6;
|
||||
v2_r32 LineMarginDim = V2((LineMarginDigitsRequired)*GlyphAdvance, ParentRect.Max.y - ParentRect.Min.y);
|
||||
|
||||
//- sixten: tokenize text
|
||||
tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, StrLit("nobody cares"), Editor->Text.String);
|
||||
token_array Tokens = TokenizeResult.Tokens;
|
||||
token *TokensBegin = Tokens.Tokens;
|
||||
token *TokensEnd = TokensBegin + Tokens.Count;
|
||||
|
||||
//- sixten: find the first visible token
|
||||
token *VisibleTokensBegin = TokensBegin;
|
||||
s64 TopMostLine = Min((s64)Floor(-Offset.y / LineHeight), LineCount);
|
||||
for(s64 LinesFound = 0; LinesFound < TopMostLine && VisibleTokensBegin < TokensEnd; VisibleTokensBegin += 1)
|
||||
{
|
||||
if(VisibleTokensBegin->Flags & TokenFlag_Newline)
|
||||
{
|
||||
LinesFound += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: find the last visible token
|
||||
token *VisibleTokensEnd = VisibleTokensBegin;
|
||||
s64 LinesOnScreen = Min((s64)Floor(ParentDim.y / LineHeight)+1, LineCount-TopMostLine);
|
||||
for(s64 LinesFound = 0; LinesFound < LinesOnScreen && VisibleTokensEnd < TokensEnd; VisibleTokensEnd += 1)
|
||||
{
|
||||
if(VisibleTokensEnd->Flags & TokenFlag_Newline)
|
||||
{
|
||||
LinesFound += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: draw line numbers & line highlights
|
||||
{
|
||||
//- sixten: draw the background
|
||||
v4 LineMarginColor = ColorFromHex(0x10203080);
|
||||
range2_r32 LineMarginBox = Range2R32(ParentRect.Min, ParentRect.Min+LineMarginDim);
|
||||
PushQuad(Group, LineMarginBox, LineMarginColor, LineMarginColor, LineMarginColor, LineMarginColor, 0, 0, 0);
|
||||
|
||||
//- sixten: draw the numbers
|
||||
v2_r32 LineOffset = Box->Rect.Min;
|
||||
for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1)
|
||||
{
|
||||
r32 LineY = LineOffset.y + LineIndex*LineHeight;
|
||||
PushTextF(Group, Atlas, Font_Monospace, V2(0, LineY), FontSize, Color_Grey, "%*.i", LineMarginDigitsRequired, LineIndex+1);
|
||||
|
||||
if(LineIndex + 1 == CursorTextP.Line)
|
||||
{
|
||||
v4_r32 LineHighlightColor = ColorFromHex(0x10204080);
|
||||
range2_r32 LineHighlightBox = Range2R32(V2(LineMarginBox.Max.x, LineY), V2(Box->Rect.Max.x, LineY+LineHeight));
|
||||
PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: render tokens
|
||||
v2 BaseTokenP = Box->Rect.Min+V2(LineMarginDim.x, TopMostLine*LineHeight);
|
||||
v2 TokenP = BaseTokenP;
|
||||
for(token *Token = VisibleTokensBegin; Token < VisibleTokensEnd; Token += 1)
|
||||
{
|
||||
string TokenString = T_StringFromToken(Editor->Text.String, *Token);
|
||||
|
||||
//- sixten: get color from token
|
||||
v4 Color = Color_Magenta;
|
||||
if(Token->Flags & TokenGroup_Comment) { Color = Color_Grey; }
|
||||
else if(Token->Flags & TokenFlag_Reserved) { Color = Color_Grey; }
|
||||
else if(Token->Flags & TokenFlag_Symbol) { Color = ColorFromHex(0xbd2d2dff); }
|
||||
else if(Token->Flags & TokenFlag_StringLiteral) { Color = ColorFromHex(0xffa900ff); }
|
||||
else if(Token->Flags & TokenFlag_Numeric) { Color = ColorFromHex(0xffa900ff); }
|
||||
else if(Token->Flags & TokenFlag_Identifier)
|
||||
{
|
||||
//- sixten: check for keywords
|
||||
if(AreEqual(TokenString, StrLit("var")) ||
|
||||
AreEqual(TokenString, StrLit("proc")) ||
|
||||
AreEqual(TokenString, StrLit("branch")) ||
|
||||
AreEqual(TokenString, StrLit("jump")) ||
|
||||
AreEqual(TokenString, StrLit("if")) ||
|
||||
AreEqual(TokenString, StrLit("true")) ||
|
||||
AreEqual(TokenString, StrLit("false")))
|
||||
{
|
||||
Color = ColorFromHex(0xf0c674ff);
|
||||
}
|
||||
else
|
||||
{
|
||||
Color = Theme_TextColor;
|
||||
}
|
||||
}
|
||||
//- sixten: render & advance by token
|
||||
if(!(Token->Flags & TokenGroup_Whitespace))
|
||||
{
|
||||
TokenP.x += PushText(Group, Atlas, Font_Monospace, TokenP, FontSize, Color, TokenString);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Token->Flags & TokenFlag_Newline)
|
||||
{
|
||||
TokenP.x = BaseTokenP.x;
|
||||
TokenP.y += LineHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 *StringBegin = TokenString.Data;
|
||||
u8 *StringEnd = StringBegin + TokenString.Count;
|
||||
for(u8 *Char = StringBegin; Char < StringEnd; Char += 1)
|
||||
{
|
||||
if(*Char == ' ' || *Char == '\t')
|
||||
{
|
||||
TokenP.x += GlyphAdvance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- sixten: render cursor
|
||||
{
|
||||
v2 TargetCursorP = Box->Rect.Min+V2(LineMarginDim.x+(CursorTextP.Column-1)*GlyphAdvance,(CursorTextP.Line-1)*LineHeight);
|
||||
v2 CursorP = V2(AnimationCurve_AnimateValueF(TargetCursorP.x, TargetCursorP.x, 0.1, "Workspace Text Editor Cursor X %p", Editor),
|
||||
AnimationCurve_AnimateValueF(TargetCursorP.y, TargetCursorP.y, 0.1, "Workspace Text Editor Cursor Y %p", Editor));
|
||||
v2 CursorDim = V2(2, LineHeight);
|
||||
range2_r32 CursorRect = Range2R32(CursorP, CursorP+CursorDim);
|
||||
v4 CursorColor = ColorFromHex(0x10FF20FF);
|
||||
PushQuad(Group, CursorRect, CursorColor, CursorColor, CursorColor, CursorColor, 2, 0.4, 0);
|
||||
}
|
||||
|
||||
//- sixten: render the selection
|
||||
{
|
||||
text_range Selection = TextRange(CursorTextP, MarkTextP);
|
||||
range1_s64 LineRange = Range1S64(Selection.Min.Line, Selection.Max.Line);
|
||||
for(s64 Line = TopMostLine; Line < TopMostLine + LinesOnScreen; Line += 1)
|
||||
{
|
||||
r32 LineY = Box->Rect.Min.y + Line*LineHeight;
|
||||
if(Contains(LineRange, Line + 1))
|
||||
{
|
||||
range1_s64 ColumnRange = Lines->Ranges[Line];
|
||||
range1_s64 NormalizedColumnRange = Range1S64(0, DimOfRange(ColumnRange));
|
||||
if(Line + 1 == LineRange.Min && Line + 1 == LineRange.Max)
|
||||
{
|
||||
NormalizedColumnRange = Range1S64(Editor->EditState.Cursor - ColumnRange.Min, Editor->EditState.Mark - ColumnRange.Min);
|
||||
}
|
||||
else if(Line + 1 == LineRange.Min)
|
||||
{
|
||||
NormalizedColumnRange = Range1S64(Min(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min, DimOfRange(ColumnRange));
|
||||
}
|
||||
else if(Line + 1 == LineRange.Max)
|
||||
{
|
||||
NormalizedColumnRange = Range1S64(0, Max(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min);
|
||||
}
|
||||
v4_r32 LineHighlightColor = ColorFromHex(0x66B3CC4C);
|
||||
range2_r32 LineHighlightBox = Range2R32(V2(LineMarginDim.x+NormalizedColumnRange.Min*GlyphAdvance, LineY),
|
||||
V2(LineMarginDim.x+NormalizedColumnRange.Max*GlyphAdvance, LineY+LineHeight));
|
||||
PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 4, 1.4, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
ReleaseScratch(Scratch);
|
||||
}
|
||||
|
||||
static void Workspace_BuildTextEditor(workspace_view *View)
|
||||
{
|
||||
workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data;
|
||||
|
||||
temporary_memory Scratch = GetScratch();
|
||||
|
||||
//- sixten: rendering properties
|
||||
r32 FontSize = 16.0f;
|
||||
r32 LineHeight = FontSize + 4.0f;
|
||||
|
||||
//- sixten: calculate the dimensions of the glyphs
|
||||
glyph *Glyph = GetGlyph(UI_GlyphAtlas(), Font_Monospace, 'A', FontSize, 0);
|
||||
r32 GlyphAdvance = Glyph->Advance;
|
||||
|
||||
//- sixten: calculate the line margin dim
|
||||
s32 LineMarginDigitsRequired = 6;
|
||||
r32 LineMarginWidth = (LineMarginDigitsRequired)*GlyphAdvance;
|
||||
|
||||
ui_box *EditorBox = 0;
|
||||
UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1));
|
||||
UI_Scroll(0, &Editor->Offset.y)
|
||||
{
|
||||
//- sixten: find the container box for the scrollable region
|
||||
Editor->ContainerBox = UI_TopParent()->Parent->Parent;
|
||||
|
||||
UI_SetNextSize(UI_Pixels(Editor->TextDim.x, 1), UI_Pixels(Editor->TextDim.y, 1));
|
||||
EditorBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clickable, "Workspace Text Editor %p", View);
|
||||
EditorBox->DrawCallback = Workspace_TextEditorDrawCallback;
|
||||
EditorBox->DrawCallbackData = Editor;
|
||||
}
|
||||
|
||||
b32 CursorHasBeenModified = false;
|
||||
|
||||
//- sixten: keyboard input -> text op plus handling
|
||||
if(Workspace_ViewIsCurrent(View))
|
||||
{
|
||||
for(platform_event *Event = UI_EventList()->First;
|
||||
Event != 0;
|
||||
Event = Event->Next)
|
||||
{
|
||||
if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text)
|
||||
{
|
||||
text_action Action = MultiLineTextActionFromEvent(Event);
|
||||
if(IsValid(&Action))
|
||||
{
|
||||
text_op Op = TextOpFromAction(Scratch.Arena, Editor->Text.String, &Editor->EditState, &Action, &Editor->Lines, Editor->LastTextPoint.Column - 1);
|
||||
|
||||
CursorHasBeenModified = true;
|
||||
|
||||
{
|
||||
MutableStringReplaceRange(&Editor->Text, Op.ReplaceString, Op.Range);
|
||||
ArenaClear(Editor->ProcessingArena);
|
||||
workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String);
|
||||
Editor->Tokens = TextData.Tokens;
|
||||
Editor->Lines = TextData.Lines;
|
||||
}
|
||||
|
||||
Editor->EditState.Cursor = Op.NewCursor;
|
||||
Editor->EditState.Mark = Op.NewMark;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui_signal Signal = UI_SignalFromBox(EditorBox);
|
||||
if(Signal.Dragging)
|
||||
{
|
||||
if(Signal.Pressed)
|
||||
{
|
||||
//- sixten: translate mouse position to text point
|
||||
v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0);
|
||||
text_point Point = {(s64)(MouseOffset.y / LineHeight) + 1, (s64)(MouseOffset.x / GlyphAdvance) + 1};
|
||||
Editor->EditState.Cursor = Editor->EditState.Mark = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point);
|
||||
}
|
||||
|
||||
//- sixten: translate mouse position to text point
|
||||
v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0);
|
||||
text_point Point = {(s64)(MouseOffset.y / LineHeight) + 1, (s64)(MouseOffset.x / GlyphAdvance) + 1};
|
||||
Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point);
|
||||
|
||||
CursorHasBeenModified = true;
|
||||
}
|
||||
|
||||
//- sixten: update eventual text point extents
|
||||
if(CursorHasBeenModified)
|
||||
{
|
||||
text_point Point = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor);
|
||||
if(Editor->LastTextPoint.Line == Point.Line)
|
||||
{
|
||||
Editor->LastTextPoint = Point;
|
||||
}
|
||||
else
|
||||
{
|
||||
Editor->LastTextPoint.Line = Point.Line;
|
||||
Editor->LastTextPoint.Column = Max(Editor->LastTextPoint.Column, Point.Column);
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseScratch(Scratch);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/* date = July 11th 2023 0:34 pm */
|
||||
|
||||
#ifndef VN_WORKSPACE_TEXT_EDITOR_H
|
||||
#define VN_WORKSPACE_TEXT_EDITOR_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Mutable String Types
|
||||
|
||||
struct mutable_string
|
||||
{
|
||||
memory_arena *Arena;
|
||||
string String;
|
||||
};
|
||||
|
||||
struct history_node
|
||||
{
|
||||
range1_s64 Range;
|
||||
string ReplaceString;
|
||||
history_node *Next;
|
||||
};
|
||||
|
||||
struct history_list
|
||||
{
|
||||
memory_arena *HistoryArena;
|
||||
|
||||
history_node *First;
|
||||
history_node *Last;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Workspace Text Editor Types
|
||||
|
||||
struct workspace_text_data
|
||||
{
|
||||
token_array Tokens;
|
||||
range1_s64_array Lines;
|
||||
};
|
||||
|
||||
struct workspace_view_text_editor
|
||||
{
|
||||
//- sixten: processed text
|
||||
memory_arena *ProcessingArena;
|
||||
token_array Tokens;
|
||||
range1_s64_array Lines;
|
||||
|
||||
//- sixten: text being edited
|
||||
mutable_string Text;
|
||||
|
||||
//- sixten: text editing
|
||||
text_edit_state EditState;
|
||||
text_point LastTextPoint;
|
||||
|
||||
//- sixten: ui building & rendering
|
||||
ui_box *ContainerBox;
|
||||
v2 TextDim;
|
||||
v2 Offset;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Mutable String Functions
|
||||
|
||||
static mutable_string MutableStringAllocate(u64 Size);
|
||||
static void MutableStringRelease(mutable_string *String);
|
||||
static void MutableStringReplaceRange(mutable_string *String, string ReplaceString, range1_s64 Range);
|
||||
|
||||
////////////////////////////////
|
||||
//~ sixten: Workspace Text Editor Functions
|
||||
|
||||
static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *Arena, string Text);
|
||||
static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback);
|
||||
static void Workspace_BuildTextEditor(workspace_view *View);
|
||||
|
||||
#endif //VN_WORKSPACE_TEXT_EDITOR_H
|
|
@ -1,18 +1,27 @@
|
|||
//- sixten: Views
|
||||
inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspace_panel *Parent)
|
||||
{
|
||||
workspace_view *View = BootstrapPushStruct(workspace_view, Arena);
|
||||
memory_arena *Arena = ArenaAllocate(Gigabytes(1));
|
||||
workspace_view *View = PushStruct(Arena, workspace_view);
|
||||
View->Arena = Arena;
|
||||
View->Type = Type;
|
||||
View->Parent = Parent;
|
||||
|
||||
switch(View->Type)
|
||||
{
|
||||
case Workspace_View_Editor:
|
||||
{ View->Data = PushSize(&View->Arena, sizeof(workspace_view_editor)); } break;
|
||||
{ View->Data = PushStruct(View->Arena, workspace_view_editor); } break;
|
||||
case Workspace_View_CommandPalette:
|
||||
{ View->Data = PushSize(&View->Arena, sizeof(workspace_view_command_palette)); } break;
|
||||
{ View->Data = PushStruct(View->Arena, workspace_view_command_palette); } break;
|
||||
case Workspace_View_Settings:
|
||||
{ View->Data = PushSize(&View->Arena, sizeof(workspace_view_settings)); } break;
|
||||
{ View->Data = PushStruct(View->Arena, workspace_view_settings); } break;
|
||||
case Workspace_View_TextEditor:
|
||||
{
|
||||
View->Data = PushStruct(View->Arena, workspace_view_text_editor);
|
||||
workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data;
|
||||
Editor->ProcessingArena = ArenaAllocate(Gigabytes(1));
|
||||
Editor->Text = MutableStringAllocate(Gigabytes(2));
|
||||
} break;
|
||||
}
|
||||
|
||||
DLLInsertLast(Parent->FirstView, Parent->LastView, View);
|
||||
|
@ -28,11 +37,13 @@ inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspa
|
|||
inline void Workspace_DestroyView(workspace_view *View)
|
||||
{
|
||||
// sixten(NOTE): This function does not ensure that the view is not being used anywhere else.
|
||||
Release(&View->Arena);
|
||||
ArenaRelease(View->Arena);
|
||||
}
|
||||
|
||||
inline b32 Workspace_ViewIsCurrent(workspace *Workspace, workspace_view *View)
|
||||
inline b32 Workspace_ViewIsCurrent(workspace_view *View)
|
||||
{
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
b32 Result = (Workspace->CurrentPanel && Workspace->CurrentPanel->CurrentView == View);
|
||||
return(Result);
|
||||
}
|
||||
|
@ -82,7 +93,7 @@ static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas *
|
|||
|
||||
v2 P = Box->Rect.Min + Offset;
|
||||
v4 Color = V4(0.4, 0.7, 0.8, 0.3);
|
||||
PushQuad(Group, P, Dim, Color, 0, 0, 0);
|
||||
PushQuad(Group, Range2R32(P, P+Dim), Color, 0, 0, 0);
|
||||
}
|
||||
|
||||
// sixten: Draw cursor
|
||||
|
@ -95,14 +106,16 @@ static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas *
|
|||
|
||||
v2 P = Box->Rect.Min + Offset;
|
||||
v4 Color = V4(0.3, 1, 0.3, 0.7);
|
||||
PushQuad(Group, P, Dim, Color, 0, 0, 0);
|
||||
PushQuad(Group, Range2R32(P, P+Dim), Color, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void Workspace_BuildViewTypeLister(workspace *Workspace, workspace_view *View)
|
||||
static void Workspace_BuildViewTypeLister(workspace_view *View)
|
||||
{
|
||||
workspace_view_command_palette *CommandPalette = (workspace_view_command_palette *)View->Data;
|
||||
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
temporary_memory Scratch = GetScratch(0, 0);
|
||||
|
||||
UI_Size(UI_Percent(1, 1), UI_Percent(1, 1))
|
||||
|
@ -333,10 +346,12 @@ static b32 UI_DropdownSelection(char **Alternatives, s32 AlternativeCount, b32 *
|
|||
return(Result);
|
||||
}
|
||||
|
||||
static void Workspace_BuildSettings(workspace *Workspace, workspace_view *View)
|
||||
static void Workspace_BuildSettings(workspace_view *View)
|
||||
{
|
||||
workspace_view_settings *Settings = (workspace_view_settings *)View->Data;
|
||||
|
||||
workspace *Workspace = Workspace_GetState();
|
||||
|
||||
UI_Height(UI_ChildrenSum(1, 1))
|
||||
UI_Column() UI_Padding(UI_Pixels(50, 0))
|
||||
UI_Row() UI_Padding(UI_Pixels(50, 0))
|
||||
|
@ -447,10 +462,10 @@ static void Workspace_BuildSettings(workspace *Workspace, workspace_view *View)
|
|||
UI_Spacer(UI_Pixels(50, 1));
|
||||
}
|
||||
|
||||
static void Workspace_BuildView(workspace *Workspace, workspace_view *View)
|
||||
static void Workspace_BuildView(workspace_view *View)
|
||||
{
|
||||
r32 ViewHighlightTransition =
|
||||
AnimationCurve_AnimateValueF(Workspace_ViewIsCurrent(Workspace, View), 0, 0.25, "Workspace View Highlight %p", View);
|
||||
AnimationCurve_AnimateValueF(Workspace_ViewIsCurrent(View), 0, 0.25, "Workspace View Highlight %p", View);
|
||||
UI_SetNextBorderColor(LinearBlend(Theme_BorderColor, Theme_HighlightBorderColor, ViewHighlightTransition));
|
||||
UI_PushBackgroundColor(Theme_BackgroundColor);
|
||||
UI_SetNextCornerRadius(3);
|
||||
|
@ -474,20 +489,28 @@ static void Workspace_BuildView(workspace *Workspace, workspace_view *View)
|
|||
UI_Font(Font_Bold) UI_FontSize(36)
|
||||
UI_LabelF("Welcome to VN");
|
||||
UI_TextColor(Theme_BorderColor) UI_LabelF("An impractical way to make a game");
|
||||
|
||||
UI_Spacer(UI_Percent(1, 0));
|
||||
|
||||
UI_Checkbox(&DEBUG_DebugSettings->ShowWelcomeMessage, StrLit("Show this message on startup"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(View->Type == Workspace_View_CommandPalette)
|
||||
{
|
||||
Workspace_BuildViewTypeLister(Workspace, View);
|
||||
Workspace_BuildViewTypeLister(View);
|
||||
}
|
||||
else if(View->Type == Workspace_View_Editor)
|
||||
{
|
||||
Workspace_BuildEditor(Workspace, View);
|
||||
Workspace_BuildEditor(View);
|
||||
}
|
||||
else if(View->Type == Workspace_View_Settings)
|
||||
{
|
||||
Workspace_BuildSettings(Workspace, View);
|
||||
Workspace_BuildSettings(View);
|
||||
}
|
||||
else if(View->Type == Workspace_View_TextEditor)
|
||||
{
|
||||
Workspace_BuildTextEditor(View);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
struct workspace_view
|
||||
{
|
||||
memory_arena Arena;
|
||||
memory_arena *Arena;
|
||||
enum workspace_view_type Type;
|
||||
|
||||
workspace_panel *Parent;
|
||||
|
@ -21,6 +21,7 @@ enum workspace_view_type
|
|||
Workspace_View_Editor,
|
||||
Workspace_View_CommandPalette,
|
||||
Workspace_View_Settings,
|
||||
Workspace_View_TextEditor,
|
||||
};
|
||||
|
||||
struct workspace_view_editor
|
||||
|
@ -32,6 +33,8 @@ struct workspace_view_editor
|
|||
workspace_editor_node *LastNode;
|
||||
workspace_editor_node *FirstFreeNode;
|
||||
workspace_editor_node *LastFreeNode;
|
||||
|
||||
workspace_editor_lister_dropdown ListerDropdown;
|
||||
};
|
||||
|
||||
struct workspace_view_command_palette
|
||||
|
@ -64,12 +67,12 @@ struct workspace_view_settings
|
|||
//- sixten: Views
|
||||
inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspace_panel *Parent);
|
||||
inline void Workspace_DestroyView(workspace_view *View);
|
||||
inline b32 Workspace_ViewIsCurrent(workspace *Workspace, workspace_view *View);
|
||||
inline b32 Workspace_ViewIsCurrent(workspace_view *View);
|
||||
inline string Workspace_GetViewName(workspace_view *View);
|
||||
|
||||
//- sixten: Builder code
|
||||
static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data);
|
||||
static void Workspace_BuildViewTypeLister(workspace *Workspace, workspace_view *View);
|
||||
static void Workspace_BuildView(workspace *Workspace, workspace_view *View);
|
||||
static void Workspace_BuildViewTypeLister(workspace_view *View);
|
||||
static void Workspace_BuildView(workspace_view *View);
|
||||
|
||||
#endif //VN_WORKSPACE_VIEW_H
|
||||
|
|
|
@ -45,48 +45,36 @@ static PLATFORM_SHOW_MESSAGE(Win32_ShowMessage)
|
|||
}
|
||||
}
|
||||
|
||||
static PLATFORM_ALLOCATE_MEMORY(Win32_AllocateMemory)
|
||||
static u64 Win32_GetPageSize(void)
|
||||
{
|
||||
win32_state *State = &Global_Win32State;
|
||||
SYSTEM_INFO Info;
|
||||
GetSystemInfo(&Info);
|
||||
return(Info.dwPageSize);
|
||||
}
|
||||
|
||||
umm TotalSize = Size + sizeof(win32_memory_block);
|
||||
|
||||
win32_memory_block *Block =
|
||||
(win32_memory_block *)VirtualAlloc(0, TotalSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
Assert(Block);
|
||||
Block->Block.Base = (u8 *)Block + sizeof(win32_memory_block);
|
||||
Block->Block.Size = Size;
|
||||
|
||||
win32_memory_block *Sentinel = &State->MemorySentinel;
|
||||
Block->Next = Sentinel;
|
||||
|
||||
BeginTicketMutex(&State->MemoryMutex);
|
||||
Block->Prev = Sentinel->Prev;
|
||||
Block->Prev->Next = Block;
|
||||
Block->Next->Prev = Block;
|
||||
EndTicketMutex(&State->MemoryMutex);
|
||||
|
||||
platform_memory_block *Result = &Block->Block;
|
||||
static PLATFORM_RESERVE(Win32_Reserve)
|
||||
{
|
||||
u64 GigabyteAlignedSize = Size+Gigabytes(1)-1;
|
||||
GigabyteAlignedSize -= GigabyteAlignedSize%Gigabytes(1);
|
||||
void *Result = VirtualAlloc(0, GigabyteAlignedSize, MEM_RESERVE, PAGE_NOACCESS);
|
||||
return(Result);
|
||||
}
|
||||
|
||||
static PLATFORM_DEALLOCATE_MEMORY(Win32_DeallocateMemory)
|
||||
static PLATFORM_RELEASE(Win32_Release)
|
||||
{
|
||||
win32_state *State = &Global_Win32State;
|
||||
|
||||
win32_memory_block *Win32Block = (win32_memory_block *)Block;
|
||||
|
||||
if(Block)
|
||||
{
|
||||
BeginTicketMutex(&State->MemoryMutex);
|
||||
Win32Block->Prev->Next = Win32Block->Next;
|
||||
Win32Block->Next->Prev = Win32Block->Prev;
|
||||
EndTicketMutex(&State->MemoryMutex);
|
||||
VirtualFree(Pointer, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
BOOL Result = VirtualFree(Block, 0, MEM_RELEASE);
|
||||
return(Result);
|
||||
static PLATFORM_COMMIT(Win32_Commit)
|
||||
{
|
||||
u64 PageAlignedSize = Size+Win32_GetPageSize()-1;
|
||||
PageAlignedSize -= PageAlignedSize%Win32_GetPageSize();
|
||||
VirtualAlloc(Pointer, PageAlignedSize, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
|
||||
static PLATFORM_DECOMMIT(Win32_Decommit)
|
||||
{
|
||||
VirtualFree(Pointer, Size, MEM_DECOMMIT);
|
||||
}
|
||||
|
||||
static PLATFORM_OPEN_FILE(Win32_OpenFile)
|
||||
|
@ -257,6 +245,9 @@ static void Win32_UpdateCode(win32_loaded_code *Code)
|
|||
{
|
||||
Win32_UnloadCode(Code);
|
||||
*Code = Win32_LoadCode();
|
||||
|
||||
// sixten(NOTE): Sometimes the program decides to crash upon reloads, so we just wait for those to be over...
|
||||
Sleep(200);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,7 +262,7 @@ static PLATFORM_TOGGLE_FULLSCREEN(Win32_ToggleFullscreen)
|
|||
if(GetWindowPlacement(Window, &Global_WindowPosition) &&
|
||||
GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo))
|
||||
{
|
||||
// sixten: This doesn't work when the window is maximized. One wordaround would be to set the
|
||||
// sixten(NOTE): This doesn't work when the window is maximized. One wordaround would be to set the
|
||||
// window to "normal" size using ShowWindow(Window, SW_SHOWNORMAL) but it looks *very* scuffed.
|
||||
SetWindowLong(Window, GWL_STYLE, Style & ~WS_OVERLAPPEDWINDOW);
|
||||
SetWindowPos(Window, HWND_TOP,
|
||||
|
@ -346,7 +337,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP
|
|||
{
|
||||
case WM_CLOSE:
|
||||
{
|
||||
Event = PushStruct(&State->EventArena, platform_event);
|
||||
Event = PushStruct(State->EventArena, platform_event);
|
||||
Event->Type = PlatformEvent_WindowClose;
|
||||
} break;
|
||||
|
||||
|
@ -391,7 +382,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP
|
|||
} fallthrough;
|
||||
case WM_MOUSEWHEEL:
|
||||
{
|
||||
Event = PushStruct(&State->EventArena, platform_event);
|
||||
Event = PushStruct(State->EventArena, platform_event);
|
||||
Event->Type = PlatformEvent_MouseScroll;
|
||||
Event->Scroll.E[ScrollAxis] = GET_WHEEL_DELTA_WPARAM(WParam) / 120.0;
|
||||
} break;
|
||||
|
@ -417,7 +408,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP
|
|||
case WM_RBUTTONUP: case WM_RBUTTONDOWN: { Key = Key_MouseRight; } break;
|
||||
}
|
||||
|
||||
Event = PushStruct(&State->EventArena, platform_event);
|
||||
Event = PushStruct(State->EventArena, platform_event);
|
||||
Event->Type = Type;
|
||||
Event->Key = Key;
|
||||
Event->P = Win32_GetMouseP(Window);
|
||||
|
@ -458,7 +449,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP
|
|||
|
||||
if(Key != Key_Invalid)
|
||||
{
|
||||
Event = PushStruct(&State->EventArena, platform_event);
|
||||
Event = PushStruct(State->EventArena, platform_event);
|
||||
Event->Type = Type;
|
||||
Event->Key = Key;
|
||||
}
|
||||
|
@ -484,7 +475,7 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP
|
|||
|
||||
if((Codepoint >= 32 && Codepoint != 127) || Codepoint == '\t' || Codepoint == '\n')
|
||||
{
|
||||
Event = PushStruct(&State->EventArena, platform_event);
|
||||
Event = PushStruct(State->EventArena, platform_event);
|
||||
Event->Type = PlatformEvent_Text;
|
||||
Event->Codepoint = Codepoint;
|
||||
}
|
||||
|
@ -551,7 +542,7 @@ static void Win32_ProcessInput(vn_input *Input, HWND Window, r32 dtForFrame)
|
|||
EndTemporaryMemory(State->EventArenaTemp);
|
||||
}
|
||||
|
||||
State->EventArenaTemp = BeginTemporaryMemory(&State->EventArena);
|
||||
State->EventArenaTemp = BeginTemporaryMemory(State->EventArena);
|
||||
}
|
||||
|
||||
MSG Message;
|
||||
|
@ -634,7 +625,9 @@ inline void Win32_GetRelevantPaths(win32_state *State)
|
|||
|
||||
int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, int ShowCommand)
|
||||
{
|
||||
thread_context ThreadContext = {};
|
||||
RegisterPlatformFunctions(Win32);
|
||||
|
||||
thread_context ThreadContext = AllocateThreadContext();
|
||||
SetThreadContext(&ThreadContext);
|
||||
|
||||
// sixten: Setup Win32 platform state.
|
||||
|
@ -646,26 +639,11 @@ int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, i
|
|||
QueryPerformanceFrequency(&FrequencyQuery);
|
||||
State->PerformanceFrequency = FrequencyQuery.QuadPart;
|
||||
|
||||
State->MemorySentinel.Next = &State->MemorySentinel;
|
||||
State->MemorySentinel.Prev = &State->MemorySentinel;
|
||||
State->EventArena = ArenaAllocate(Gigabytes(1));
|
||||
|
||||
State->SleepIsGranular = (timeBeginPeriod(1) == TIMERR_NOERROR);
|
||||
}
|
||||
|
||||
// sixten: Setup platform layer
|
||||
{
|
||||
Platform.AllocateMemory = Win32_AllocateMemory;
|
||||
Platform.DeallocateMemory = Win32_DeallocateMemory;
|
||||
Platform.OpenFile = Win32_OpenFile;
|
||||
Platform.CloseFile = Win32_CloseFile;
|
||||
Platform.ReadFile = Win32_ReadFile;
|
||||
Platform.WriteFile = Win32_WriteFile;
|
||||
Platform.GetFileSize = Win32_GetFileSize;
|
||||
Platform.SetCursor = Win32_SetCursor;
|
||||
Platform.ToggleFullscreen = Win32_ToggleFullscreen;
|
||||
Platform.ShowMessage = Win32_ShowMessage;
|
||||
}
|
||||
|
||||
WNDCLASS WindowClass = {};
|
||||
WindowClass.lpszClassName = "vn-window-class";
|
||||
WindowClass.lpfnWndProc = Win32_WindowCallback;
|
||||
|
@ -676,7 +654,7 @@ int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, i
|
|||
{
|
||||
HWND Window = CreateWindowEx(0,
|
||||
WindowClass.lpszClassName,
|
||||
"vn - June 2023 Build",
|
||||
"vn - July 2023 Build",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
|
|
|
@ -3,27 +3,14 @@
|
|||
#ifndef WIN32_MAIN_H
|
||||
#define WIN32_MAIN_H
|
||||
|
||||
struct win32_memory_block
|
||||
{
|
||||
platform_memory_block Block;
|
||||
win32_memory_block *Next;
|
||||
win32_memory_block *Prev;
|
||||
u64 Padding[2];
|
||||
};
|
||||
|
||||
CTAssert(sizeof(win32_memory_block) == 64);
|
||||
|
||||
struct win32_state
|
||||
{
|
||||
win32_memory_block MemorySentinel;
|
||||
ticket_mutex MemoryMutex;
|
||||
|
||||
u64 PerformanceFrequency;
|
||||
b32 SleepIsGranular;
|
||||
|
||||
HWND Window;
|
||||
|
||||
memory_arena EventArena;
|
||||
memory_arena *EventArena;
|
||||
temporary_memory EventArenaTemp;
|
||||
platform_event_list EventList;
|
||||
|
||||
|
|
|
@ -7,5 +7,6 @@ Dev
|
|||
{
|
||||
RenderUIDebugRects = false;
|
||||
RenderFPSCounter = false;
|
||||
ListHotAndActive = true;
|
||||
ListHotAndActive = false;
|
||||
ShowWelcomeMessage = false;
|
||||
}
|
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 833 KiB |
After Width: | Height: | Size: 758 KiB |
After Width: | Height: | Size: 981 KiB |
After Width: | Height: | Size: 969 KiB |
After Width: | Height: | Size: 969 KiB |
After Width: | Height: | Size: 2.1 MiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 1.2 MiB |
|
@ -0,0 +1,22 @@
|
|||
RULES
|
||||
-Credit "Noraneko Games"
|
||||
-For games: credit can be given in the in-game credits, in a note accompanying the game, or on a website where the project is downloaded. Any one of those is fine. It doesn't have to be all of them.
|
||||
-For Art: Credit must accompany the picture where it is posted or on the image itself.
|
||||
-For Vtubing/video: Credit can be given in the description or in a panel of your streaming platform or on the video itself. Any of those are fine.
|
||||
-If this pack is re-uploaded to another site for download as is, do not remove this text file or any part of it.
|
||||
-These assets cannot be sold as is. For merchandising options, contact me and we can discuss options.
|
||||
-Modifications are allowed. (Change colors, add blood splatters, draw characters in the image, etc)
|
||||
-Using for 18+ is fine as long as it doesn't contain illegal themes/content (such as homophobia, racism, threats to real people, or pedophilia) that could reflect poorly on the Noraneko Games name and reputation.
|
||||
-Commercial use in a game is ok if you can provide proof of credit if I contact you for it and it follows all other rules. Contact me for commercial use outside of games.
|
||||
-If you aren't making money from your project, you can use without contacting me if you'd prefer.
|
||||
-Using this asset for Game Jams and Contests is okay!
|
||||
-If you want to use this media for anything other than a game, make sure it follows the rules above.
|
||||
|
||||
|
||||
Find more of my work at:
|
||||
@NoranekoGames on Twitter
|
||||
Noranekokgames.itch.io
|
||||
|
||||
Not required, but I would love for you to link me to your project!
|
||||
|
||||
Good luck with your project!
|
After Width: | Height: | Size: 994 KiB |
After Width: | Height: | Size: 871 KiB |
After Width: | Height: | Size: 999 KiB |
After Width: | Height: | Size: 951 KiB |
After Width: | Height: | Size: 626 KiB |
After Width: | Height: | Size: 793 KiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 1.0 MiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 1.3 MiB |
|
@ -0,0 +1,41 @@
|
|||
// This just experiments with the scripting language
|
||||
|
||||
var times = 0
|
||||
|
||||
proc "Start"
|
||||
{
|
||||
"so, I actually changed my mind."
|
||||
"the editor will not be node based"
|
||||
"I realised that it would just be slower to write dialog that way soooo..."
|
||||
"instead, I present to you the.........."
|
||||
"vn scene - scripting language"
|
||||
|
||||
"btw something happens if you go through this dialog 10 times"
|
||||
|
||||
times += 1
|
||||
|
||||
branch
|
||||
{
|
||||
"Return to start"
|
||||
{
|
||||
jump "Start"
|
||||
}
|
||||
|
||||
if(times >= 10)
|
||||
{
|
||||
"SUPER EPIC SECRET"
|
||||
{
|
||||
jump "Epic Scene"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc "Epic Scene"
|
||||
{
|
||||
"woah... so epic"
|
||||
@s "oh, right. almost forgot to mention that you can talk as different characters."
|
||||
@s "you know... "
|
||||
wait
|
||||
@s #noclear "the usual"
|
||||
}
|