diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..dc33ed7
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,71 @@
+---
+Language: Cpp
+BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignConsecutiveAssignments:
+ Enabled: true
+AlignConsecutiveDeclarations:
+ Enabled: true
+AlignOperands: Align
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: WithoutElse
+BraceWrapping:
+ AfterCaseLabel: true
+ AfterClass: true
+ AfterControlStatement: true
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterStruct: true
+ AfterUnion: true
+ AfterExternBlock: false
+ BeforeCatch: true
+ BeforeElse: true
+ BeforeLambdaBody: true
+ BeforeWhile: true
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakBeforeBraces: Custom
+BreakConstructorInitializers: BeforeComma
+BreakTemplateDeclarations: Yes
+ColumnLimit: 120
+ConstructorInitializerIndentWidth: 2
+Cpp11BracedListStyle: false
+IncludeCategories:
+ - Regex: '^<.*'
+ Priority: 1
+ - Regex: '^".*'
+ Priority: 2
+ - Regex: '.*'
+ Priority: 3
+IncludeIsMainRegex: '([-_](test|unittest))?$'
+IndentAccessModifiers: false
+IndentCaseBlocks: true
+IndentWidth: 4
+InsertNewlineAtEOF: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+NamespaceIndentation: All
+ObjCSpaceAfterProperty: true
+PenaltyBreakBeforeFirstCallParameter: 100
+PenaltyBreakComment: 100
+PenaltyBreakFirstLessLess: 0
+PenaltyBreakString: 100
+PenaltyExcessCharacter: 1
+PenaltyReturnTypeOnItsOwnLine: 1000
+PointerAlignment: Left
+SortIncludes: CaseInsensitive
+SpacesInAngles: Never
+SpacesInContainerLiterals: false
+Standard: Cpp11
+StatementMacros:
+ - UPROPERTY
+ - UCLASS
+ - USTRUCT
+ - GENERATED_BODY
+ - UENUM
+TabWidth: 4
+...
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..c8430a7
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,169 @@
+* text=auto
+
+# Unity files
+*.meta -text merge=unityyamlmerge diff
+*.unity -text merge=unityyamlmerge diff
+*.asset -text merge=unityyamlmerge diff
+*.prefab -text merge=unityyamlmerge diff
+*.mat -text merge=unityyamlmerge diff
+*.anim -text merge=unityyamlmerge diff
+*.controller -text merge=unityyamlmerge diff
+*.overrideController -text merge=unityyamlmerge diff
+*.physicMaterial -text merge=unityyamlmerge diff
+*.physicsMaterial2D -text merge=unityyamlmerge diff
+*.playable -text merge=unityyamlmerge diff
+*.mask -text merge=unityyamlmerge diff
+*.brush -text merge=unityyamlmerge diff
+*.flare -text merge=unityyamlmerge diff
+*.fontsettings -text merge=unityyamlmerge diff
+*.guiskin -text merge=unityyamlmerge diff
+*.giparams -text merge=unityyamlmerge diff
+*.renderTexture -text merge=unityyamlmerge diff
+*.spriteatlas -text merge=unityyamlmerge diff
+*.terrainlayer -text merge=unityyamlmerge diff
+*.mixer -text merge=unityyamlmerge diff
+*.shadervariants -text merge=unityyamlmerge diff
+
+# Unreal files
+*.locres filter=lfs diff=lfs merge=lfs -text
+*.locmeta filter=lfs diff=lfs merge=lfs -text
+*.ico filter=lfs diff=lfs merge=lfs -text
+*.uasset filter=lfs diff=lfs merge=lfs -text
+*.umap filter=lfs diff=lfs merge=lfs -text
+
+*.3DS filter=lfs diff=lfs merge=lfs -text
+*.3ds filter=lfs diff=lfs merge=lfs -text
+*.ABC filter=lfs diff=lfs merge=lfs -text
+*.AEP filter=lfs diff=lfs merge=lfs -text
+*.AFDESIGN filter=lfs diff=lfs merge=lfs -text
+*.AFPHOTO filter=lfs diff=lfs merge=lfs -text
+*.AI filter=lfs diff=lfs merge=lfs -text
+*.AIF filter=lfs diff=lfs merge=lfs -text
+*.AVI filter=lfs diff=lfs merge=lfs -text
+*.BGEO filter=lfs diff=lfs merge=lfs -text
+*.BIN filter=lfs diff=lfs merge=lfs -text
+*.BLEND filter=lfs diff=lfs merge=lfs -text
+*.BMP filter=lfs diff=lfs merge=lfs -text
+*.BPOLY filter=lfs diff=lfs merge=lfs -text
+*.C4D filter=lfs diff=lfs merge=lfs -text
+*.DOC filter=lfs diff=lfs merge=lfs -text
+*.DOCX filter=lfs diff=lfs merge=lfs -text
+*.DWG filter=lfs diff=lfs merge=lfs -text
+*.DXF filter=lfs diff=lfs merge=lfs -text
+*.EXR filter=lfs diff=lfs merge=lfs -text
+*.FBX filter=lfs diff=lfs merge=lfs -text
+*.GEO filter=lfs diff=lfs merge=lfs -text
+*.GI filter=lfs diff=lfs merge=lfs -text
+*.GI2 filter=lfs diff=lfs merge=lfs -text
+*.GIF filter=lfs diff=lfs merge=lfs -text
+*.GLB filter=lfs diff=lfs merge=lfs -text
+*.GLTF filter=lfs diff=lfs merge=lfs -text
+*.HDR filter=lfs diff=lfs merge=lfs -text
+*.HIP filter=lfs diff=lfs merge=lfs -text
+*.HIPLC filter=lfs diff=lfs merge=lfs -text
+*.HIPNC filter=lfs diff=lfs merge=lfs -text
+*.JPEG filter=lfs diff=lfs merge=lfs -text
+*.JPG filter=lfs diff=lfs merge=lfs -text
+*.MA filter=lfs diff=lfs merge=lfs -text
+*.MAX filter=lfs diff=lfs merge=lfs -text
+*.MB filter=lfs diff=lfs merge=lfs -text
+*.MOV filter=lfs diff=lfs merge=lfs -text
+*.MP3 filter=lfs diff=lfs merge=lfs -text
+*.MP4 filter=lfs diff=lfs merge=lfs -text
+*.MPEG filter=lfs diff=lfs merge=lfs -text
+*.MPG filter=lfs diff=lfs merge=lfs -text
+*.OBJ filter=lfs diff=lfs merge=lfs -text
+*.PDF filter=lfs diff=lfs merge=lfs -text
+*.PFM filter=lfs diff=lfs merge=lfs -text
+*.PIC filter=lfs diff=lfs merge=lfs -text
+*.PMB filter=lfs diff=lfs merge=lfs -text
+*.PNG filter=lfs diff=lfs merge=lfs -text
+*.POLY filter=lfs diff=lfs merge=lfs -text
+*.PPT filter=lfs diff=lfs merge=lfs -text
+*.PPTX filter=lfs diff=lfs merge=lfs -text
+*.PROFRAW filter=lfs diff=lfs merge=lfs -text
+*.PRPROJ filter=lfs diff=lfs merge=lfs -text
+*.PSB filter=lfs diff=lfs merge=lfs -text
+*.PSD filter=lfs diff=lfs merge=lfs -text
+*.RAT filter=lfs diff=lfs merge=lfs -text
+*.RIB filter=lfs diff=lfs merge=lfs -text
+*.SKETCH filter=lfs diff=lfs merge=lfs -text
+*.STL filter=lfs diff=lfs merge=lfs -text
+*.TAR filter=lfs diff=lfs merge=lfs -text
+*.TIF filter=lfs diff=lfs merge=lfs -text
+*.TIFF filter=lfs diff=lfs merge=lfs -text
+*.USD filter=lfs diff=lfs merge=lfs -text
+*.USDC filter=lfs diff=lfs merge=lfs -text
+*.USDZ filter=lfs diff=lfs merge=lfs -text
+*.VDB filter=lfs diff=lfs merge=lfs -text
+*.WAV filter=lfs diff=lfs merge=lfs -text
+*.XLS filter=lfs diff=lfs merge=lfs -text
+*.XLSX filter=lfs diff=lfs merge=lfs -text
+*.ZIP filter=lfs diff=lfs merge=lfs -text
+*.abc filter=lfs diff=lfs merge=lfs -text
+*.aep filter=lfs diff=lfs merge=lfs -text
+*.afdesign filter=lfs diff=lfs merge=lfs -text
+*.afphoto filter=lfs diff=lfs merge=lfs -text
+*.ai filter=lfs diff=lfs merge=lfs -text
+*.aif filter=lfs diff=lfs merge=lfs -text
+*.avi filter=lfs diff=lfs merge=lfs -text
+*.bgeo filter=lfs diff=lfs merge=lfs -text
+*.bin filter=lfs diff=lfs merge=lfs -text
+*.blend filter=lfs diff=lfs merge=lfs -text
+*.bmp filter=lfs diff=lfs merge=lfs -text
+*.bpoly filter=lfs diff=lfs merge=lfs -text
+*.c4d filter=lfs diff=lfs merge=lfs -text
+*.doc filter=lfs diff=lfs merge=lfs -text
+*.docx filter=lfs diff=lfs merge=lfs -text
+*.dwg filter=lfs diff=lfs merge=lfs -text
+*.dxf filter=lfs diff=lfs merge=lfs -text
+*.exr filter=lfs diff=lfs merge=lfs -text
+*.fbx filter=lfs diff=lfs merge=lfs -text
+*.geo filter=lfs diff=lfs merge=lfs -text
+*.gi filter=lfs diff=lfs merge=lfs -text
+*.gi2 filter=lfs diff=lfs merge=lfs -text
+*.gif filter=lfs diff=lfs merge=lfs -text
+*.glb filter=lfs diff=lfs merge=lfs -text
+*.gltf filter=lfs diff=lfs merge=lfs -text
+*.hdr filter=lfs diff=lfs merge=lfs -text
+*.hip filter=lfs diff=lfs merge=lfs -text
+*.hiplc filter=lfs diff=lfs merge=lfs -text
+*.hipnc filter=lfs diff=lfs merge=lfs -text
+*.jpeg filter=lfs diff=lfs merge=lfs -text
+*.jpg filter=lfs diff=lfs merge=lfs -text
+*.ma filter=lfs diff=lfs merge=lfs -text
+*.max filter=lfs diff=lfs merge=lfs -text
+*.mb filter=lfs diff=lfs merge=lfs -text
+*.mov filter=lfs diff=lfs merge=lfs -text
+*.mp3 filter=lfs diff=lfs merge=lfs -text
+*.mp4 filter=lfs diff=lfs merge=lfs -text
+*.mpeg filter=lfs diff=lfs merge=lfs -text
+*.mpg filter=lfs diff=lfs merge=lfs -text
+*.obj filter=lfs diff=lfs merge=lfs -text
+*.pdf filter=lfs diff=lfs merge=lfs -text
+*.pfm filter=lfs diff=lfs merge=lfs -text
+*.pic filter=lfs diff=lfs merge=lfs -text
+*.pmb filter=lfs diff=lfs merge=lfs -text
+*.png filter=lfs diff=lfs merge=lfs -text
+*.poly filter=lfs diff=lfs merge=lfs -text
+*.ppt filter=lfs diff=lfs merge=lfs -text
+*.pptx filter=lfs diff=lfs merge=lfs -text
+*.profraw filter=lfs diff=lfs merge=lfs -text
+*.prproj filter=lfs diff=lfs merge=lfs -text
+*.psb filter=lfs diff=lfs merge=lfs -text
+*.psd filter=lfs diff=lfs merge=lfs -text
+*.rat filter=lfs diff=lfs merge=lfs -text
+*.rib filter=lfs diff=lfs merge=lfs -text
+*.sketch filter=lfs diff=lfs merge=lfs -text
+*.stl filter=lfs diff=lfs merge=lfs -text
+*.tar filter=lfs diff=lfs merge=lfs -text
+*.tif filter=lfs diff=lfs merge=lfs -text
+*.tiff filter=lfs diff=lfs merge=lfs -text
+*.usd filter=lfs diff=lfs merge=lfs -text
+*.usdc filter=lfs diff=lfs merge=lfs -text
+*.usdz filter=lfs diff=lfs merge=lfs -text
+*.vdb filter=lfs diff=lfs merge=lfs -text
+*.wav filter=lfs diff=lfs merge=lfs -text
+*.xls filter=lfs diff=lfs merge=lfs -text
+*.xlsx filter=lfs diff=lfs merge=lfs -text
+*.zip filter=lfs diff=lfs merge=lfs -text
diff --git a/.gitignore b/.gitignore
index e257658..1f1b4b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,18 @@
# ---> C++
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Binaries
+[Bb]in/
+[Ll]ib/
+.idea/
+.vs
+
# Prerequisites
*.d
diff --git a/Directory.build.props b/Directory.build.props
new file mode 100644
index 0000000..97b8ab4
--- /dev/null
+++ b/Directory.build.props
@@ -0,0 +1,6 @@
+
+
+ C:\Program Files\LLVM
+ 19.1.0
+
+
diff --git a/Juliet.sln b/Juliet.sln
new file mode 100644
index 0000000..1686529
--- /dev/null
+++ b/Juliet.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.12.35506.116
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Juliet", "Juliet\Juliet.vcxproj", "{1BBC0B92-E4D8-4838-974B-439C5C501E82}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JulietApp", "JulietApp\JulietApp.vcxproj", "{4B2A0F9C-5F78-4BC9-BEE9-1E58BB85FA79}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1BBC0B92-E4D8-4838-974B-439C5C501E82} = {1BBC0B92-E4D8-4838-974B-439C5C501E82}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1BBC0B92-E4D8-4838-974B-439C5C501E82}.Debug|x64.ActiveCfg = Debug|x64
+ {1BBC0B92-E4D8-4838-974B-439C5C501E82}.Debug|x64.Build.0 = Debug|x64
+ {1BBC0B92-E4D8-4838-974B-439C5C501E82}.Release|x64.ActiveCfg = Release|x64
+ {1BBC0B92-E4D8-4838-974B-439C5C501E82}.Release|x64.Build.0 = Release|x64
+ {4B2A0F9C-5F78-4BC9-BEE9-1E58BB85FA79}.Debug|x64.ActiveCfg = Debug|x64
+ {4B2A0F9C-5F78-4BC9-BEE9-1E58BB85FA79}.Debug|x64.Build.0 = Debug|x64
+ {4B2A0F9C-5F78-4BC9-BEE9-1E58BB85FA79}.Release|x64.ActiveCfg = Release|x64
+ {4B2A0F9C-5F78-4BC9-BEE9-1E58BB85FA79}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Juliet/Juliet.vcxproj b/Juliet/Juliet.vcxproj
new file mode 100644
index 0000000..73fb23d
--- /dev/null
+++ b/Juliet/Juliet.vcxproj
@@ -0,0 +1,284 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {1bbc0b92-e4d8-4838-974b-439c5c501e82}
+ Juliet
+ 10.0
+
+
+
+ StaticLibrary
+ true
+ ClangCL
+ Unicode
+
+
+ StaticLibrary
+ false
+ ClangCL
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(SolutionDir)\lib\$(Platform)\$(Configuration)\
+ $(SolutionDir)Intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(SolutionDir)Juliet\include\;$(SolutionDir)Juliet\src\;$(SolutionDir)Juliet\src\Graphics\RHI\DX12\D3D12;$(IncludePath)
+
+
+ $(SolutionDir)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)Intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(SolutionDir)Juliet\include\;$(SolutionDir)Juliet\src\;$(SolutionDir)Juliet\src\Graphics\RHI\DX12\D3D12;$(IncludePath)
+
+
+
+ Level3
+ true
+ _DEBUG;_LIB;JULIET_WIN32;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+ stdcpp20
+ Fast
+ false
+ false
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_LIB;JULIET_WIN32;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+ stdcpp20
+ Fast
+ false
+ false
+
+
+
+
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Use
+ Use
+
+
+
+ MultiThreadedDebugDll
+ Disabled
+ true
+ NoListing
+ W:\Classified\RomeoAndJuliet\Intermediate\Juliet\x64\Debug\
+ false
+ W:\Classified\RomeoAndJuliet\Intermediate\Juliet\x64\Debug\
+ Default
+ Default
+ Column
+ false
+ false
+ false
+ NotSet
+ Fast
+ Default
+ false
+ stdcpp20
+ Default
+ W:\Classified\RomeoAndJuliet\Intermediate\Juliet\x64\Debug\
+ false
+ Neither
+ W:\Classified\RomeoAndJuliet\Intermediate\Juliet\x64\Debug\
+ Cdecl
+ Use
+ pch.h
+ W:\Classified\RomeoAndJuliet\Intermediate\Juliet\x64\Debug\Juliet.pch
+ false
+ false
+ false
+ false
+ false
+ W:\Classified\RomeoAndJuliet\Intermediate\Juliet\x64\Debug\
+ true
+ true
+ false
+ Default
+ W:\Classified\RomeoAndJuliet\Intermediate\Juliet\x64\Debug\Juliet.tlog\
+ true
+ false
+ Level3
+ W:\Classified\RomeoAndJuliet\Intermediate\Juliet\x64\Debug\
+ ProgramDatabase
+ false
+ false
+ true
+ _DEBUG;_LIB;JULIET_WIN32;_UNICODE;UNICODE;
+ false
+ true
+ true
+ Default
+ --target=amd64-pc-windows-msvc
+
+
+
+ Create
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Juliet/Juliet.vcxproj.filters b/Juliet/Juliet.vcxproj.filters
new file mode 100644
index 0000000..3b67e0a
--- /dev/null
+++ b/Juliet/Juliet.vcxproj.filters
@@ -0,0 +1,75 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {04f78748-69ab-4ed6-9a09-6c5803b9349d}
+
+
+ {31dfc257-b706-4ff0-9e42-6eaed85e98fd}
+
+
+ {4b8741bf-1fb9-4a70-9306-580c83cc0a6f}
+
+
+ {aa0d4fbd-e094-4319-a06a-cc2c01dc20b5}
+
+
+ {a210c51b-2a2a-4230-b78f-2aa7ed60963c}
+
+
+ {7a443547-1774-4b54-b7f5-33569cb78ac9}
+
+
+ {866a221e-ead3-4274-9ea6-16fc33bbfa97}
+
+
+ {fb5b80c0-3192-4fa8-927e-754264313c6f}
+
+
+ {e0c8b73d-66bc-446a-906f-e0e69fc74e25}
+
+
+ {caf51413-0c34-4b09-81db-9194fe3b244c}
+
+
+
+
+ Header Files\Core\Application
+
+
+ Header Files\Core\Application
+
+
+ Header Files\Engine
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files\Core\Application
+
+
+ Source Files\Engine
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/Juliet/include/Core/Application/ApplicationManager.h b/Juliet/include/Core/Application/ApplicationManager.h
new file mode 100644
index 0000000..b158234
--- /dev/null
+++ b/Juliet/include/Core/Application/ApplicationManager.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ enum class JulietInit_Flags : uint8;
+ void StartApplication(IApplication& app, JulietInit_Flags flags);
+} // namespace Juliet
diff --git a/Juliet/include/Core/Application/IApplication.h b/Juliet/include/Core/Application/IApplication.h
new file mode 100644
index 0000000..e1721ae
--- /dev/null
+++ b/Juliet/include/Core/Application/IApplication.h
@@ -0,0 +1,14 @@
+#pragma once
+
+namespace Juliet
+{
+ class IApplication
+ {
+ public:
+ virtual ~IApplication() = default;
+ virtual void Init() = 0;
+ virtual void Shutdown() = 0;
+ virtual void Update() = 0;
+ virtual bool IsRunning() = 0;
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Common/CoreTypes.h b/Juliet/include/Core/Common/CoreTypes.h
new file mode 100644
index 0000000..a719e94
--- /dev/null
+++ b/Juliet/include/Core/Common/CoreTypes.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include
+#include
+
+namespace Juliet
+{
+ using uint8 = uint8_t;
+ using uint16 = uint16_t;
+ using uint32 = uint32_t;
+ using uint64 = uint64_t;
+
+ using int8 = int8_t;
+ using int16 = int16_t;
+ using int32 = int32_t;
+ using int64 = int64_t;
+
+ using byte = std::byte;
+
+ using size_t = std::size_t;
+
+ struct ByteBuffer
+ {
+ const byte* Data;
+ size_t Size;
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Common/CoreUtils.h b/Juliet/include/Core/Common/CoreUtils.h
new file mode 100644
index 0000000..de9ed3f
--- /dev/null
+++ b/Juliet/include/Core/Common/CoreUtils.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include
+#include
+#include
+
+#if _DEBUG
+#define Assert(expression) \
+ do \
+ { \
+ if (!(expression)) \
+ { \
+ JulietAssert("Assertion Failed: " #expression); \
+ } \
+ } \
+ while (0)
+
+#else
+#define Assert(Expression)
+#endif
+
+void JulietAssert(const char* expression);
diff --git a/Juliet/include/Core/Common/EnumUtils.h b/Juliet/include/Core/Common/EnumUtils.h
new file mode 100644
index 0000000..5364a47
--- /dev/null
+++ b/Juliet/include/Core/Common/EnumUtils.h
@@ -0,0 +1,91 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+
+ template
+ requires std::is_enum_v
+ E operator~(E lhs)
+ {
+ using underlying = std::underlying_type_t;
+ return static_cast(~static_cast(lhs));
+ }
+
+ template
+ requires std::is_enum_v
+ E operator|(E lhs, E rhs)
+ {
+ using underlying = std::underlying_type_t;
+ return static_cast(static_cast(lhs) | static_cast(rhs));
+ }
+
+ template
+ requires std::is_enum_v
+ E& operator|=(E& lhs, E rhs)
+ {
+ return lhs = lhs | rhs;
+ }
+
+ template
+ requires std::is_enum_v
+ E operator&(E lhs, E rhs)
+ {
+ using underlying = std::underlying_type_t;
+ return static_cast(static_cast(lhs) & static_cast(rhs));
+ }
+
+ template
+ requires std::is_enum_v
+ E& operator&=(E& lhs, E rhs)
+ {
+ return lhs = lhs & rhs;
+ }
+
+ template
+ requires std::is_enum_v
+ E operator+(const E& lhs, const E& rhs)
+ {
+ using underlying = std::underlying_type_t;
+ return static_cast(static_cast(lhs) + static_cast(rhs));
+ }
+
+ template
+ requires std::is_enum_v
+ E operator-(const E& lhs, const E& rhs)
+ {
+ using underlying = std::underlying_type_t;
+ return static_cast(static_cast(lhs) - static_cast(rhs));
+ }
+
+ template
+ requires std::is_enum_v
+ E operator^(const E& lhs, const E& rhs)
+ {
+ using underlying = std::underlying_type_t;
+ return static_cast(static_cast(lhs) ^ static_cast(rhs));
+ }
+
+ template
+ requires std::is_enum_v
+ E& operator^=(E& lhs, const E& rhs)
+ {
+ return lhs = lhs ^ rhs;
+ }
+
+ template
+ requires std::is_enum_v
+ constexpr std::underlying_type_t ToUnderlying(E enm)
+ {
+ using underlying = std::underlying_type_t;
+ return static_cast(enm);
+ }
+
+ template
+ requires std::is_enum_v
+ constexpr E ToEnum(std::underlying_type_t value)
+ {
+ return static_cast(value);
+ }
+} // namespace Juliet
diff --git a/Juliet/include/Core/Common/NonCopyable.h b/Juliet/include/Core/Common/NonCopyable.h
new file mode 100644
index 0000000..ec3d169
--- /dev/null
+++ b/Juliet/include/Core/Common/NonCopyable.h
@@ -0,0 +1,14 @@
+#pragma once
+
+namespace Juliet
+{
+ class NonCopyable
+ {
+ public:
+ NonCopyable() = default;
+ virtual ~NonCopyable() = default;
+
+ NonCopyable(const NonCopyable&) = delete;
+ const NonCopyable& operator=(const NonCopyable&) = delete;
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Common/NonMovable.h b/Juliet/include/Core/Common/NonMovable.h
new file mode 100644
index 0000000..b80185f
--- /dev/null
+++ b/Juliet/include/Core/Common/NonMovable.h
@@ -0,0 +1,14 @@
+#pragma once
+
+namespace Juliet
+{
+ class NonMovable
+ {
+ public:
+ NonMovable() = default;
+ virtual ~NonMovable() = default;
+
+ NonMovable(const NonMovable&&) = delete;
+ const NonMovable& operator=(const NonMovable&&) = delete;
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Common/NonNullPtr.h b/Juliet/include/Core/Common/NonNullPtr.h
new file mode 100644
index 0000000..1883c82
--- /dev/null
+++ b/Juliet/include/Core/Common/NonNullPtr.h
@@ -0,0 +1,108 @@
+#pragma once
+
+#include
+#include
+
+namespace Juliet
+{
+ template
+ concept NonNullPtr_Convertible = std::is_convertible_v;
+
+ template
+ concept NonNullPtr_SameType = std::is_same_v;
+
+ template
+ class NonNullPtr
+ {
+ public:
+ NonNullPtr(Type* ptr)
+ : InternalPtr(ptr)
+ {
+ Assert(ptr && "Tried to initialize a NonNullPtr with a null pointer");
+ }
+
+ template
+ requires NonNullPtr_Convertible
+ NonNullPtr(const NonNullPtr& otherPtr)
+ : InternalPtr(otherPtr.Get())
+ {
+ Assert(InternalPtr && "Fatal Error: Assigned a non null ptr using another NonNullPtr but its was null.");
+ }
+
+ // Assignment
+ NonNullPtr& operator=(Type* ptr)
+ {
+ Assert(ptr && "Tried to assign a null pointer to a NonNullPtr!");
+ InternalPtr = ptr;
+ return *this;
+ }
+
+ template
+ requires NonNullPtr_Convertible
+ NonNullPtr& operator=(const NonNullPtr& otherPtr)
+ {
+ InternalPtr = otherPtr.Get();
+ return *this;
+ }
+
+ // Accessors
+ operator Type*() const
+ {
+ Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null");
+ return InternalPtr;
+ }
+
+ Type* Get() const
+ {
+ Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null");
+ return InternalPtr;
+ }
+
+ Type& operator*() const
+ {
+ Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null");
+ return *InternalPtr;
+ }
+
+ Type* operator->() const
+ {
+ Assert(InternalPtr && "NonNullPtr: Internal Pointer is Null");
+ return InternalPtr;
+ }
+
+ // Comparisons
+ bool operator==(const NonNullPtr& otherPtr) const { return InternalPtr == otherPtr.InternalPtr; }
+
+ template
+ requires NonNullPtr_SameType
+ bool operator==(OtherType* otherRawPtr) const
+ {
+ return InternalPtr == otherRawPtr;
+ }
+
+ template
+ requires NonNullPtr_SameType
+ friend bool operator==(OtherType* otherRawPtr, const NonNullPtr& nonNullPtr)
+ {
+ return otherRawPtr == nonNullPtr.InternalPtr;
+ }
+
+ // Forbid assigning a nullptr at compile time
+ NonNullPtr(std::nullptr_t)
+ : InternalPtr(nullptr)
+ {
+ static_assert(sizeof(Type) == 0, "Trying to initialize a NonNullPtr with a nullptr value");
+ }
+
+ NonNullPtr& operator=(std::nullptr_t)
+ {
+ static_assert(sizeof(Type) == 0, "Trying to assign a NonNullPtr with a nullptr value");
+ return *this;
+ }
+
+ explicit operator bool() const { return true; };
+
+ private:
+ Type* InternalPtr;
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Common/Singleton.h b/Juliet/include/Core/Common/Singleton.h
new file mode 100644
index 0000000..63b0b20
--- /dev/null
+++ b/Juliet/include/Core/Common/Singleton.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include
+#include
+#include
+
+namespace Juliet
+{
+ // Two usages : Inherit from Singleton or use the static methods to register to it when you want.
+ // 1)
+ // class IThing {};
+ // class Thing : public Singleton::AutoRegister {};
+ template
+ class Singleton final : public NonMovable, public NonCopyable
+ {
+ public:
+ static void Register(NonNullPtr type);
+ static void Unregister(NonNullPtr type);
+ static Type* Get();
+
+ class AutoRegister : public Type
+ {
+ public:
+ AutoRegister();
+ virtual ~AutoRegister();
+ };
+
+ private:
+ static Type* sInstance;
+ static bool sIsRegistered;
+ };
+
+ template
+ bool Singleton::sIsRegistered = false;
+
+ template
+ Type* Singleton::sInstance = nullptr;
+
+ template
+ void Singleton::Register(NonNullPtr type)
+ {
+ Assert(!Get() && "Singleton is already registered. Make sure to only register it once");
+ sInstance = type.Get();
+ sIsRegistered = true;
+ }
+
+ template
+ void Singleton::Unregister(NonNullPtr type)
+ {
+ Assert(sIsRegistered && "Trying to unregister a singleton that is not registered");
+ Assert(sInstance == type &&
+ "Trying to unregister an instance that is different from the one currently registered");
+
+ sInstance = nullptr;
+ sIsRegistered = false;
+ }
+
+ template
+ Type* Singleton::Get()
+ {
+ if (sIsRegistered)
+ {
+ return sInstance;
+ }
+ return nullptr;
+ }
+
+ template
+ Singleton::AutoRegister::AutoRegister()
+ {
+ Singleton::Register(this);
+ }
+
+ template
+ Singleton::AutoRegister::~AutoRegister()
+ {
+ Singleton::Unregister(this);
+ }
+} // namespace Juliet
diff --git a/Juliet/include/Core/Container/Vector.h b/Juliet/include/Core/Container/Vector.h
new file mode 100644
index 0000000..abddf5a
--- /dev/null
+++ b/Juliet/include/Core/Container/Vector.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ // TODO : Create my own Vector class based on https://github.com/niklas-ourmachinery/bitsquid-foundation/blob/master/collection_types.h
+ template class Vector : public std::vector
+ {
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/HAL/Display/Display.h b/Juliet/include/Core/HAL/Display/Display.h
new file mode 100644
index 0000000..e88c8f7
--- /dev/null
+++ b/Juliet/include/Core/HAL/Display/Display.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include
+#include
+
+namespace Juliet
+{
+ struct Window;
+
+ using WindowID = uint8;
+ extern Window* CreatePlatformWindow(const char* title, uint16 width, uint16 height, int flags = 0 /* unused */);
+ extern void DestroyPlatformWindow(NonNullPtr window);
+
+ extern void ShowWindow(NonNullPtr window);
+ extern void HideWindow(NonNullPtr window);
+
+ extern WindowID GetWindowID(NonNullPtr window);
+} // namespace Juliet
diff --git a/Juliet/include/Core/HAL/Event/SystemEvent.h b/Juliet/include/Core/HAL/Event/SystemEvent.h
new file mode 100644
index 0000000..bd29843
--- /dev/null
+++ b/Juliet/include/Core/HAL/Event/SystemEvent.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+// Handles all events from systems handling the Hardware
+// Very inspired by SDL3
+
+namespace Juliet
+{
+ enum class EventType : uint32
+ {
+ None = 0,
+ First = None,
+
+ // Application Events
+ // User querying an exit
+ Application_Exit = 100,
+ // OS terminating the application
+ Application_OS_Terminate,
+ Application_Begin = Application_Exit,
+ Application_End = Application_OS_Terminate,
+
+ // Window Events
+ Window_Close_Request = 200,
+ Window_Begin = Window_Close_Request,
+ Window_End = Window_Close_Request,
+
+ // Keyboard Event
+ Key_Down = 300,
+ Key_Up,
+ Keyboard_Begin = Key_Down,
+ Keyboard_End = Key_Up,
+
+ // Mouse Event
+ Mouse_Move = 400,
+ Mouse_ButtonPressed,
+ Mouse_ButtonReleased,
+
+ Mouse_Begin = Mouse_Move,
+ Mouse_End = Mouse_ButtonReleased,
+
+ Last // Get value from the previous one
+ };
+
+ struct WindowEvent
+ {
+ WindowID AssociatedWindowID;
+ uint32 DataPadding[2]; // TODO : define how much data param we need
+ };
+
+ struct KeyboardEvent
+ {
+ KeyboardID AssociatedKeyboardID;
+ WindowID WindowID;
+ Key Key;
+ KeyState KeyState;
+ KeyMod KeyModeState;
+ };
+
+ // =====================================================
+ // Mouse Events
+ // =====================================================
+ struct MouseMovementEvent
+ {
+ MouseID AssociatedMouseID;
+ WindowID WindowID;
+ float X;
+ float Y;
+ float X_Displacement;
+ float Y_Displacement;
+ MouseButton ButtonState;
+ };
+
+ struct MouseButtonEvent
+ {
+ MouseID AssociatedMouseID;
+ WindowID WindowID;
+ float X;
+ float Y;
+ MouseButton ButtonState;
+ bool IsPressed : 1;
+ };
+
+ // Tagged union representing ALL possible system events + a bit of data for custom event if needed
+ union AllSystemEventUnion
+ {
+ WindowEvent Window;
+ KeyboardEvent Keyboard;
+ MouseMovementEvent MouseMovement;
+ MouseButtonEvent MouseButton;
+ uint8 Padding[128]; // Make sure that the union is fixed in size and big enough on all platforms.
+ };
+ // Make sure we do not bust the union size
+ static_assert(sizeof(AllSystemEventUnion) == sizeof(((AllSystemEventUnion*)nullptr)->Padding));
+
+ struct SystemEvent
+ {
+ EventType Type;
+ uint64 Timestamp;
+ AllSystemEventUnion Data;
+ };
+
+ // Poll for any event, return false if no event is available.
+ // Equivalent to WaitEvent(event, 0);
+ // Will not block
+ extern bool GetEvent(SystemEvent& event);
+
+ // TODO : use chrono to tag the timeout correctly with nanosec
+ // timeout == -1 means wait for any event before pursuing
+ // timeout == 0 means checking once for the frame and getting out
+ // timeout > 0 means wait until time is out
+ extern bool WaitEvent(SystemEvent& event, int32 timeoutInNS = -1);
+
+ // Add an event onto the event queue.
+ // TODO : support array of events
+ extern bool AddEvent(SystemEvent& event);
+} // namespace Juliet
diff --git a/Juliet/include/Core/HAL/Keyboard/KeyCode.h b/Juliet/include/Core/HAL/Keyboard/KeyCode.h
new file mode 100644
index 0000000..35ee21b
--- /dev/null
+++ b/Juliet/include/Core/HAL/Keyboard/KeyCode.h
@@ -0,0 +1,191 @@
+#pragma once
+
+namespace Juliet
+{
+ // Represents a Virtual Key corresponding to the Physical key, but localized using the keyboard layout
+ // ScanCode reprensent US ASCII Keyboard
+ // WASD Scan codes are ZQSD in KeyCode for French keyboard
+ // We use the ASCII value of the generated character as value, when possible.
+ // Keys that do not produce a character are converted to an abritrary value high enough to not conflict
+ // Reference: https://www.asciitable.com/
+ // Reference: https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa299374(v=vs.60)
+ enum class KeyCode : uint32
+ {
+ Unknown = 0x0, // 0
+ Unsupported = 0x0, // 0
+ Return = 0X0Du, // '\r'
+ Escape = 0X1Bu, // '\X1B'
+ Backspace = 0X08u, // '\b'
+ Tab = 0X09u, // '\t'
+ Space = 0X20u, // ' '
+ ExclamationPoint = 0X21u, // '!'
+ DoubleApostrophe = 0X22u, // '"'
+ Hash = 0X23u, // '#'
+ Dollar = 0X24u, // '$'
+ Percent = 0X25u, // '%'
+ Ampersand = 0X26u, // '&'
+ Apostrophe = 0X27u, // '\''
+ LeftParenthesis = 0X28u, // '('
+ RightParenthesis = 0X29u, // ')'
+ Asterisk = 0X2Au, // '*'
+ Plus = 0X2Bu, // '+'
+ Comma = 0X2Cu, // ','
+ Minus = 0X2Du, // '-'
+ Period = 0X2Eu, // '.'
+ Slash = 0X2Fu, // '/'
+ Num0 = 0X30u, // '0'
+ Num1 = 0X31u, // '1'
+ Num2 = 0X32u, // '2'
+ Num3 = 0X33u, // '3'
+ Num4 = 0X34u, // '4'
+ Num5 = 0X35u, // '5'
+ Num6 = 0X36u, // '6'
+ Num7 = 0X37u, // '7'
+ Num8 = 0X38u, // '8'
+ Num9 = 0X39u, // '9'
+ Colon = 0X3Au, // ':'
+ Semicolon = 0X3Bu, // ';'
+ LessThan = 0X3Cu, // '<'
+ Equals = 0X3Du, // '='
+ GreaterThan = 0X3Eu, // '>'
+ QuestionMark = 0X3Fu, // '?'
+ CommercialAt = 0x40u, // '@'
+ LeftBracket = 0X5Bu, // '['
+ Backslash = 0X5Cu, // '\\'
+ RightBracket = 0X5DU, // ']'
+ Caret = 0X5Eu, // '^'
+ Underscore = 0X5Fu, // '_'
+ GraveAccent = 0X60u, // '`'
+ A = 0x61u, // 'a'
+ B = 0x62u, // 'b'
+ C = 0x63u, // 'c'
+ D = 0x64u, // 'd'
+ E = 0x65u, // 'e'
+ F = 0x66u, // 'f'
+ G = 0x67u, // 'g'
+ H = 0x68u, // 'h'
+ I = 0x69u, // 'i'
+ J = 0x6Au, // 'j'
+ K = 0x6Bu, // 'k'
+ L = 0x6CU, // 'l'
+ M = 0x6DU, // 'm'
+ N = 0x6Eu, // 'n'
+ O = 0x6Fu, // 'o'
+ P = 0x70u, // 'p'
+ Q = 0x71u, // 'q'
+ R = 0x72u, // 'r'
+ S = 0x73u, // 's'
+ T = 0x74u, // 't'
+ U = 0x75u, // 'y'
+ V = 0x76u, // 'v'
+ W = 0x77u, // 'w'
+ X = 0x78u, // 'x'
+ Y = 0x79u, // 'y'
+ Z = 0x7Au, // 'z'
+ LeftBrace = 0x7BU, // '{'
+ Pipe = 0x7CU, // '|'
+ RightBrace = 0x7DU, // '}'
+ Tilde = 0x7Eu, // '~'
+ Delete = 0x7Fu, // '\x7F'
+ PlusMinus = 0xb1u, // '\xB1'
+
+ // Keys not producing a character
+ // Based on SDL Algo: ScanCode | 0x40000000
+ CapsLock = 0x40000039u,
+ F1 = 0x4000003Au,
+ F2 = 0x4000003Bu,
+ F3 = 0x4000003CU,
+ F4 = 0x4000003DU,
+ F5 = 0x4000003Eu,
+ F6 = 0x4000003Fu,
+ F7 = 0x40000040u,
+ F8 = 0x40000041u,
+ F9 = 0x40000042u,
+ F10 = 0x40000043u,
+ F11 = 0x40000044u,
+ F12 = 0x40000045u,
+ PrintScreen = 0x40000046u,
+ ScrollLock = 0x40000047u,
+ Pause = 0x40000048u,
+ Insert = 0x40000049u,
+ Home = 0x4000004Au,
+ PageUp = 0x4000004Bu,
+ End = 0x4000004DU,
+ PageDown = 0x4000004Eu,
+ RightArrow = 0x4000004Fu,
+ LeftArrow = 0x40000050u,
+ DownArrow = 0x40000051u,
+ UpArrow = 0x40000052u,
+ NumlockClear = 0x40000053u,
+ KeyPad_Divide = 0x40000054u,
+ KeyPad_Multiply = 0x40000055u,
+ KeyPad_Minus = 0x40000056u,
+ KeyPad_Plus = 0x40000057u,
+ KeyPad_Enter = 0x40000058u,
+ KeyPad_Num1 = 0x40000059u,
+ KeyPad_Num2 = 0x4000005Au,
+ KeyPad_Num3 = 0x4000005Bu,
+ KeyPad_Num4 = 0x4000005Cu,
+ KeyPad_Num5 = 0x4000005Du,
+ KeyPad_Num6 = 0x4000005Eu,
+ KeyPad_Num7 = 0x4000005Fu,
+ KeyPad_Num8 = 0x40000060u,
+ KeyPad_Num9 = 0x40000061u,
+ KeyPad_Num0 = 0x40000062u,
+ KeyPad_Period = 0x40000063u,
+ Power = 0x40000066u,
+ KeyPad_Equals = 0x40000067u,
+ F13 = 0x40000068u,
+ F14 = 0x40000069u,
+ F15 = 0x4000006Au,
+ F16 = 0x4000006Bu,
+ F17 = 0x4000006Cu,
+ F18 = 0x4000006Du,
+ F19 = 0x4000006Eu,
+ F20 = 0x4000006Fu,
+ F21 = 0x40000070u,
+ F22 = 0x40000071u,
+ F23 = 0x40000072u,
+ F24 = 0x40000073u,
+ Mute = 0x4000007Fu,
+ VolumeUp = 0x40000080u,
+ VolumeDown = 0x40000081u,
+ KeyPad_Comma = 0x40000085u,
+ LeftControl = 0x400000E0u,
+ LeftShift = 0x400000E1u,
+ LeftAlt = 0x400000E2u,
+ LeftOSCommand = 0x400000E3u,
+ RightControl = 0x400000E4u,
+ RightShift = 0x400000E5u,
+ RightAlt = 0x400000E6u,
+ RightOSCommand = 0x400000E7u,
+ Sleep = 0x40000102u,
+ WakeUp = 0x40000103u,
+ Media_NextTrack = 0x4000010Bu,
+ Media_PreviousTrack = 0x4000010Cu,
+ Media_Stop = 0x4000010Du,
+ Media_Eject = 0x4000010Eu,
+ Media_PlayPause = 0x4000010Fu,
+ Media_Select = 0x40000110u,
+ };
+
+ enum class KeyMod : uint16
+ {
+ None = 0b0,
+ LeftShift = 0b0000'0000'0001u,
+ RightShift = 0b0000'0000'0010u,
+ LeftControl = 0b0000'0000'0100u,
+ RightControl = 0b0000'0000'1000u,
+ LeftAlt = 0b0000'0001'000u,
+ RightAlt = 0b0000'0010'0000u,
+ LeftOSCommand = 0b0000'0100'0000u,
+ RightOSCommand = 0b0000'1000'0000u,
+ NumLock = 0b0001'0000'0000u,
+ CapsLock = 0b0010'0000'0000u,
+ ScrollLock = 0b0100'0000'0000u,
+ Control = LeftControl | RightControl,
+ Shift = LeftShift | RightShift,
+ Alt = LeftAlt | RightAlt,
+ OSCommand = LeftOSCommand | RightOSCommand,
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/HAL/Keyboard/Keyboard.h b/Juliet/include/Core/HAL/Keyboard/Keyboard.h
new file mode 100644
index 0000000..fbb6c56
--- /dev/null
+++ b/Juliet/include/Core/HAL/Keyboard/Keyboard.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include
+#include
+
+namespace Juliet
+{
+ using KeyboardID = uint8;
+
+ enum class KeyState : bool
+ {
+ Up = false,
+ Down = true
+ };
+
+ struct Key
+ {
+ ScanCode ScanCode;
+ KeyCode KeyCode;
+ uint16 Raw;
+ };
+
+ extern bool IsKeyDown(ScanCode scanCode);
+
+ extern KeyMod GetKeyModState();
+ extern KeyCode GetKeyCodeFromScanCode(ScanCode scanCode, KeyMod keyModState);
+} // namespace Juliet
diff --git a/Juliet/include/Core/HAL/Keyboard/ScanCode.h b/Juliet/include/Core/HAL/Keyboard/ScanCode.h
new file mode 100644
index 0000000..f45601f
--- /dev/null
+++ b/Juliet/include/Core/HAL/Keyboard/ScanCode.h
@@ -0,0 +1,186 @@
+#pragma once
+
+namespace Juliet
+{
+ // Follow the HID Usage page for USB
+ // https://usb.org/sites/default/files/hut1_5.pdf
+ // 0 to 256 Is dedicated to Keyboard Usage Page (0x07)
+ // 257 to 286 Is dedicated to Consumer Usage Page (0xC)
+ // 287 to 511 Is not implemented. Could be used for Mobile or Consoles
+ // ScanCode reprensent Physical Keys and Buttons
+ enum class ScanCode : uint16
+ {
+ Unknown = 0,
+ Unsupported = 0,
+
+ A = 4,
+ B = 5,
+ C = 6,
+ D = 7,
+ E = 8,
+ F = 9,
+ G = 10,
+ H = 11,
+ I = 12,
+ J = 13,
+ K = 14,
+ L = 15,
+ M = 16,
+ N = 17,
+ O = 18,
+ P = 19,
+ Q = 20,
+ R = 21,
+ S = 22,
+ T = 23,
+ U = 24,
+ V = 25,
+ W = 26,
+ X = 27,
+ Y = 28,
+ Z = 29,
+
+ Num1 = 30,
+ Num2 = 31,
+ Num3 = 32,
+ Num4 = 33,
+ Num5 = 34,
+ Num6 = 35,
+ Num7 = 36,
+ Num8 = 37,
+ Num9 = 38,
+ Num0 = 39,
+
+ Return = 40,
+ Escape = 41,
+ Backspace = 42,
+ Tab = 43,
+ Space = 44,
+
+ Minus = 45,
+ Equals = 46,
+ LeftBracket = 47,
+ RightBracket = 48,
+ Backslash = 49,
+ NonUSHash = 50, // Same as 49 but for ISO keyboards
+ Semicolon = 51,
+ Apostrophe = 52,
+ GraveAccent = 53,
+ Comma = 54,
+ Period = 55,
+ Slash = 56,
+ CapsLock = 57,
+
+ F1 = 58,
+ F2 = 59,
+ F3 = 60,
+ F4 = 61,
+ F5 = 62,
+ F6 = 63,
+ F7 = 64,
+ F8 = 65,
+ F9 = 66,
+ F10 = 67,
+ F11 = 68,
+ F12 = 69,
+
+ PrintScreen = 70,
+ ScrollLock = 71,
+ Pause = 72,
+ Insert = 73,
+
+ Home = 74,
+ PageUp = 75,
+ Delete = 76,
+ End = 77,
+ PageDown = 78,
+ RightArrow = 79,
+ LeftArrow = 80,
+ DownArrow = 81,
+ UpArrow = 82,
+
+ NumlockClear = 83, // Pc = Numlock / Mac = Clear
+
+ KeyPad_Divide = 84,
+ KeyPad_Multiply = 85,
+ KeyPad_Minus = 86,
+ KeyPad_Plus = 87,
+ KeyPad_Enter = 88,
+ KeyPad_Num1 = 89,
+ KeyPad_Num2 = 90,
+ KeyPad_Num3 = 91,
+ KeyPad_Num4 = 92,
+ KeyPad_Num5 = 93,
+ KeyPad_Num6 = 94,
+ KeyPad_Num7 = 95,
+ KeyPad_Num8 = 96,
+ KeyPad_Num9 = 97,
+ KeyPad_Num0 = 98,
+ KeyPad_Period = 99,
+
+ NonUSBackslash = 100, // ISO keyboards only
+ Power = 102, // Some mac have a Power key
+
+ KeyPad_Equals = 103,
+ F13 = 104,
+ F14 = 105,
+ F15 = 106,
+ F16 = 107,
+ F17 = 108,
+ F18 = 109,
+ F19 = 110,
+ F20 = 111,
+ F21 = 112,
+ F22 = 113,
+ F23 = 114,
+ F24 = 115,
+
+ Mute = 127,
+ VolumeUp = 128,
+ VolumeDown = 129,
+
+ KeyPad_Comma = 133,
+
+ International1 = 135, // Mostly used on Asian keyboards
+ International2 = 136,
+ International3 = 137, // Yen Symbol
+ International4 = 138,
+ International5 = 139,
+ International6 = 140,
+ International7 = 141,
+ International8 = 142,
+ International9 = 143,
+ Lang1 = 144, // Hangul (Korean)
+ Lang2 = 145, // Hanja (Korean)
+ Lang3 = 146, // Katakana (Japanese)
+ Lang4 = 147, // Hiragana (Japanese)
+ Lang5 = 148, // Zenkaku/Hankaku (Japanese)
+ Lang6 = 149, // Unused
+ Lang7 = 150, // Unused
+ Lang8 = 151, // Unused
+ Lang9 = 152, // Unused
+
+ LeftControl = 224,
+ LeftShift = 225,
+ LeftAlt = 226, // Alt for PC, Option for Mac
+ LeftOSCommand = 227, // Window key for PC, Command for Mac
+ RightControl = 228,
+ RightShift = 229,
+ RightAlt = 230, // Alt Gr for PC, Option for Mac
+ RightOSCommand = 231, // Window key for PC, Command for Mac
+
+ Sleep = 258,
+ WakeUp = 259,
+
+ Media_NextTrack = 267,
+ Media_PreviousTrack = 268,
+ Media_Stop = 269,
+ Media_Eject = 270,
+ Media_PlayPause = 271,
+ Media_Select = 272,
+
+ Reserved = 287,
+
+ Count = 512
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/HAL/Mouse/Mouse.h b/Juliet/include/Core/HAL/Mouse/Mouse.h
new file mode 100644
index 0000000..e0ec93a
--- /dev/null
+++ b/Juliet/include/Core/HAL/Mouse/Mouse.h
@@ -0,0 +1,27 @@
+#pragma once
+
+namespace Juliet
+{
+ using MouseID = uint8;
+
+ enum class MouseButton : uint8
+ {
+ None = 0,
+ Left = 1 << 0,
+ Right = 1 << 1,
+ Middle = 1 << 2,
+ Button1 = 1 << 3,
+ Button2 = 1 << 4,
+ };
+
+ // TODO : Replace by Vector2f
+ struct MousePosition
+ {
+ float X;
+ float Y;
+ };
+
+ extern bool IsMouseButtonDown(MouseButton button);
+ extern MousePosition GetMousePosition();
+ extern MouseButton GetMouseButtonState();
+} // namespace Juliet
diff --git a/Juliet/include/Core/JulietInit.h b/Juliet/include/Core/JulietInit.h
new file mode 100644
index 0000000..330ab30
--- /dev/null
+++ b/Juliet/include/Core/JulietInit.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ enum class JulietInit_Flags : uint8
+ {
+ None = 0,
+ Display = 1 << 0,
+ Audio = 1 << 1,
+ Count = Audio,
+ All = 0xFb
+ };
+
+ void JulietInit(JulietInit_Flags flags);
+ void JulietShutdown();
+} // namespace Juliet
diff --git a/Juliet/include/Core/Logging/LogManager.h b/Juliet/include/Core/Logging/LogManager.h
new file mode 100644
index 0000000..7c6cd58
--- /dev/null
+++ b/Juliet/include/Core/Logging/LogManager.h
@@ -0,0 +1,43 @@
+#pragma once
+
+// TODO : Juliet strings
+#include
+// TODO Juliet Containers + Allocators...
+#include
+
+namespace Juliet
+{
+ enum class LogLevel : uint8;
+ enum class LogCategory : uint8;
+
+ class LogManager final
+ {
+ public:
+ LogManager();
+
+ void Init();
+ void Shutdown();
+ bool GetIsInitialized() const { return IsInitialized; }
+
+ private:
+ struct Entry
+ {
+ std::string Value;
+ uint64_t Time;
+ LogLevel Level;
+ LogCategory Category;
+
+ Entry(std::string& value, LogLevel level, LogCategory category);
+ };
+ std::deque Entries;
+ bool IsInitialized : 1;
+
+ friend void Log(LogLevel level, LogCategory category, const char* fmt, ...);
+ void AddEntry(Entry&& entry);
+ static void OutputLog(Entry& entry);
+ };
+
+ void InitializeLogManager();
+ void ShutdownLogManager();
+ void Log(LogLevel level, LogCategory category, const char* fmt, ...);
+} // namespace Juliet
diff --git a/Juliet/include/Core/Logging/LogTypes.h b/Juliet/include/Core/Logging/LogTypes.h
new file mode 100644
index 0000000..06e438f
--- /dev/null
+++ b/Juliet/include/Core/Logging/LogTypes.h
@@ -0,0 +1,20 @@
+#pragma once
+
+namespace Juliet
+{
+ enum class LogLevel : uint8
+ {
+ Message = 0,
+ Warning = 1,
+ Error = 2,
+ };
+
+ enum class LogCategory : uint8
+ {
+ Core = 0,
+ Networking = 1,
+ Engine = 2,
+ Editor = 3,
+ Game = 4,
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Memory/Allocator.h b/Juliet/include/Core/Memory/Allocator.h
new file mode 100644
index 0000000..13d9b7f
--- /dev/null
+++ b/Juliet/include/Core/Memory/Allocator.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ // Uninitialized allocation
+ void* Malloc(size_t nb_elem, size_t elem_size);
+ // Initialized to 0 allocation
+ void* Calloc(size_t nb_elem, size_t elem_size);
+ void* Realloc(void* memory, size_t newSize);
+
+ // Free
+ template
+ void Free(Type* memory)
+ {
+ Assert(memory);
+ free(memory);
+ }
+ // Free and Set the ptr to nullptr
+ template
+ void SafeFree(Type*& memory)
+ {
+ Assert(memory);
+ free(memory);
+ memory = nullptr;
+ }
+} // namespace Juliet
diff --git a/Juliet/include/Core/Memory/Utils.h b/Juliet/include/Core/Memory/Utils.h
new file mode 100644
index 0000000..af25026
--- /dev/null
+++ b/Juliet/include/Core/Memory/Utils.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define ArraySize(array) (sizeof(array) / sizeof(array[0]))
diff --git a/Juliet/include/Core/Networking/IPAddress.h b/Juliet/include/Core/Networking/IPAddress.h
new file mode 100644
index 0000000..64b945e
--- /dev/null
+++ b/Juliet/include/Core/Networking/IPAddress.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ // TODO : Do something better.
+ constexpr uint32 kLocalhost = (127 << 3) | (0 << 2) | (0 << 1) | 1;
+ constexpr uint32 kAnyIp = 0;
+ constexpr uint32 kBroadcastIp = (255 << 3) | (255 << 2) | (255 << 1) | 255;
+} // namespace Juliet
diff --git a/Juliet/include/Core/Networking/NetworkPacket.h b/Juliet/include/Core/Networking/NetworkPacket.h
new file mode 100644
index 0000000..2e72034
--- /dev/null
+++ b/Juliet/include/Core/Networking/NetworkPacket.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include
+#include
+
+namespace Juliet
+{
+ class NetworkPacket
+ {
+ public:
+ NetworkPacket();
+ virtual ~NetworkPacket();
+ NetworkPacket(NetworkPacket&);
+ NetworkPacket& operator=(const NetworkPacket&);
+ NetworkPacket(NetworkPacket&&) noexcept;
+ NetworkPacket& operator=(NetworkPacket&&) noexcept;
+
+ ByteBuffer GetRawData();
+
+ // Unpack
+
+ // Pack
+ NetworkPacket& operator<<(uint32 value);
+ NetworkPacket& operator<<(const char* data);
+
+ protected:
+ void Append(ByteBuffer buffer);
+
+ friend class TcpSocket;
+
+ private:
+ Vector Data;
+ size_t PartialSendIndex = 0;
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Networking/Socket.h b/Juliet/include/Core/Networking/Socket.h
new file mode 100644
index 0000000..3feef83
--- /dev/null
+++ b/Juliet/include/Core/Networking/Socket.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include
+#include
+
+namespace Juliet
+{
+ class Socket : NonCopyable
+ {
+ public:
+ ~Socket() override;
+
+ Socket(Socket&& other) noexcept;
+ Socket& operator=(Socket&& socket) noexcept;
+
+ bool IsValid() const;
+
+ enum class Status : uint8
+ {
+ Done,
+ Partial,
+ Ready,
+ NotReady,
+ Disconnected,
+ Error
+ };
+
+ protected:
+ enum class Protocol : uint8
+ {
+ TCP,
+ UDP
+ };
+
+ // To store the result of a send/receive on the socket
+ struct RequestStatus
+ {
+ Status Status = Status::Done;
+ size_t Length = 0;
+ };
+
+ explicit Socket(Protocol protocol);
+
+ SocketHandle GetHandle() const { return Handle; }
+
+ void Create();
+ void CreateFromHandle(SocketHandle handle);
+ void Close();
+
+ private:
+ SocketHandle Handle;
+ Protocol ProtocolType;
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Networking/SocketHandle.h b/Juliet/include/Core/Networking/SocketHandle.h
new file mode 100644
index 0000000..d8f6538
--- /dev/null
+++ b/Juliet/include/Core/Networking/SocketHandle.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#if JULIET_WIN32
+#include
+#endif
+
+namespace Juliet
+{
+#if JULIET_WIN32
+ using SocketHandle = UINT_PTR;
+#else
+ using SocketHandle = int;
+#endif
+} // namespace Juliet
diff --git a/Juliet/include/Core/Networking/TcpListener.h b/Juliet/include/Core/Networking/TcpListener.h
new file mode 100644
index 0000000..1d855d4
--- /dev/null
+++ b/Juliet/include/Core/Networking/TcpListener.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include
+#include
+#include
+
+namespace Juliet
+{
+ class TcpListener : public Socket
+ {
+ public:
+ TcpListener();
+
+ Status Listen(uint16 port, uint32 address = kAnyIp);
+ Status Accept(TcpSocket& socket);
+ void Close();
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Networking/TcpSocket.h b/Juliet/include/Core/Networking/TcpSocket.h
new file mode 100644
index 0000000..35ff226
--- /dev/null
+++ b/Juliet/include/Core/Networking/TcpSocket.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ class NetworkPacket;
+
+ class TcpSocket : public Socket
+ {
+ public:
+ TcpSocket();
+
+ RequestStatus Send(NetworkPacket& packet);
+ RequestStatus Send(ByteBuffer buffer);
+ Status Receive(NetworkPacket& outPacket);
+
+ private:
+ friend class TcpListener;
+ };
+} // namespace Juliet
diff --git a/Juliet/include/Core/Thread/Mutex.h b/Juliet/include/Core/Thread/Mutex.h
new file mode 100644
index 0000000..a47922d
--- /dev/null
+++ b/Juliet/include/Core/Thread/Mutex.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ using Mutex = std::mutex;
+ using LockGuard = std::lock_guard;
+} // namespace Juliet
diff --git a/Juliet/include/Core/Thread/Thread.h b/Juliet/include/Core/Thread/Thread.h
new file mode 100644
index 0000000..6fbc158
--- /dev/null
+++ b/Juliet/include/Core/Thread/Thread.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ using Thread = std::thread;
+}
diff --git a/Juliet/include/Engine/Engine.h b/Juliet/include/Engine/Engine.h
new file mode 100644
index 0000000..d66e6a0
--- /dev/null
+++ b/Juliet/include/Engine/Engine.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ enum class JulietInit_Flags : uint8;
+
+ struct Engine
+ {
+ IApplication* Application = nullptr;
+ };
+
+ void InitializeEngine(JulietInit_Flags flags);
+ void ShutdownEngine();
+
+ void LoadApplication(IApplication& app);
+ void UnloadApplication();
+
+ void RunEngine();
+} // namespace Juliet
diff --git a/Juliet/include/Graphics/Graphics.h b/Juliet/include/Graphics/Graphics.h
new file mode 100644
index 0000000..29e69a0
--- /dev/null
+++ b/Juliet/include/Graphics/Graphics.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include
+
+// Graphics Interface
+namespace Juliet::Graphics
+{
+ // Add functions to create windows, device, attach to the window etc...
+} // namespace Juliet::Graphics
diff --git a/Juliet/include/Graphics/GraphicsConfig.h b/Juliet/include/Graphics/GraphicsConfig.h
new file mode 100644
index 0000000..2b8f322
--- /dev/null
+++ b/Juliet/include/Graphics/GraphicsConfig.h
@@ -0,0 +1,14 @@
+#pragma once
+
+namespace Juliet
+{
+enum class RendererType
+{
+ DX12 = 0
+};
+
+struct GraphicsConfig
+{
+ RendererType Renderer = RendererType::DX12;
+};
+}
\ No newline at end of file
diff --git a/Juliet/include/pch.h b/Juliet/include/pch.h
new file mode 100644
index 0000000..65063f9
--- /dev/null
+++ b/Juliet/include/pch.h
@@ -0,0 +1,15 @@
+// pch.h: This is a precompiled header file.
+// Files listed below are compiled only once, improving build performance for future builds.
+// This also affects IntelliSense performance, including code completion and many code browsing features.
+// However, files listed here are ALL re-compiled if any one of them is updated between builds.
+// Do not add files here that you will be updating frequently as this negates the performance advantage.
+
+#ifndef PCH_H
+#define PCH_H
+
+#include
+#include
+#include
+#include
+
+#endif // PCH_H
diff --git a/Juliet/src/Core/Application/ApplicationManager.cpp b/Juliet/src/Core/Application/ApplicationManager.cpp
new file mode 100644
index 0000000..37637a7
--- /dev/null
+++ b/Juliet/src/Core/Application/ApplicationManager.cpp
@@ -0,0 +1,22 @@
+#include
+
+#include
+#include
+
+#include
+
+namespace Juliet
+{
+ void StartApplication(IApplication& app, JulietInit_Flags flags)
+ {
+ InitializeEngine(flags);
+
+ LoadApplication(app);
+
+ RunEngine();
+
+ UnloadApplication();
+
+ ShutdownEngine();
+ }
+} // namespace Juliet
diff --git a/Juliet/src/Core/Common/CoreUtils.cpp b/Juliet/src/Core/Common/CoreUtils.cpp
new file mode 100644
index 0000000..cac387a
--- /dev/null
+++ b/Juliet/src/Core/Common/CoreUtils.cpp
@@ -0,0 +1,7 @@
+#include
+
+void JulietAssert(const char* expression)
+{
+ Juliet::Log(Juliet::LogLevel::Error, Juliet::LogCategory::Core, expression);
+ __debugbreak();
+}
diff --git a/Juliet/src/Core/HAL/Display/Display.cpp b/Juliet/src/Core/HAL/Display/Display.cpp
new file mode 100644
index 0000000..c0b7142
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/Display.cpp
@@ -0,0 +1,119 @@
+#include
+
+#include
+#include
+#include
+#include
+
+namespace Juliet
+{
+ namespace
+ {
+ DisplayDevice* CurrentDisplayDevice = nullptr;
+
+ constexpr DisplayDeviceFactory* Factories[] = { &Win32DisplayDeviceFactory, nullptr };
+ } // namespace
+
+ void InitializeDisplaySystem()
+ {
+ Assert(!CurrentDisplayDevice);
+
+ DisplayDevice* candidateDevice = nullptr;
+ DisplayDeviceFactory* candidateFactory = nullptr;
+ for (DisplayDeviceFactory* factory : Factories)
+ {
+ if (factory)
+ {
+ candidateDevice = factory->CreateDevice();
+ if (candidateDevice)
+ {
+ candidateFactory = factory;
+ break;
+ }
+ }
+ }
+
+ // TODO : handle error instead of crashing
+ Assert(candidateDevice);
+
+ CurrentDisplayDevice = candidateDevice;
+ CurrentDisplayDevice->Name = candidateFactory->Name;
+
+ if (!CurrentDisplayDevice->Initialize(CurrentDisplayDevice))
+ {
+ ShutdownDisplaySystem();
+ }
+ }
+
+ void ShutdownDisplaySystem()
+ {
+ if (!CurrentDisplayDevice)
+ {
+ return;
+ }
+
+ // Destroy all Windows that are still alive
+ DestroyPlatformWindow(CurrentDisplayDevice->MainWindow);
+
+ CurrentDisplayDevice->Shutdown(CurrentDisplayDevice);
+ // Free anything that was freed by the shutdown and then free the display
+ // no op for now
+ CurrentDisplayDevice->Free(CurrentDisplayDevice);
+ CurrentDisplayDevice = nullptr;
+ }
+
+ Window* CreatePlatformWindow(const char* title, uint16 width, uint16 height, int flags /* = 0 unused */)
+ {
+ Assert(CurrentDisplayDevice->CreatePlatformWindow);
+
+ auto window = static_cast(Calloc(1, sizeof(Window)));
+ if (!window)
+ {
+ return nullptr;
+ }
+ window->Width = width;
+ window->Height = height;
+
+ CurrentDisplayDevice->MainWindow = window;
+ if (!CurrentDisplayDevice->CreatePlatformWindow(CurrentDisplayDevice, window))
+ {
+ // TODO : Destroy
+ return nullptr;
+ }
+
+ // TODO : make SHOW optional on creation with a flag
+ CurrentDisplayDevice->ShowWindow(CurrentDisplayDevice, window);
+
+ return window;
+ }
+
+ void DestroyPlatformWindow(NonNullPtr window)
+ {
+ HideWindow(window);
+
+ CurrentDisplayDevice->DestroyPlatformWindow(CurrentDisplayDevice, window);
+
+ Free(window.Get());
+ }
+
+ void ShowWindow(NonNullPtr window)
+ {
+ CurrentDisplayDevice->ShowWindow(CurrentDisplayDevice, window);
+ }
+
+ void HideWindow(NonNullPtr window)
+ {
+ CurrentDisplayDevice->HideWindow(CurrentDisplayDevice, window);
+ }
+
+ WindowID GetWindowID(NonNullPtr window)
+ {
+ return window->ID;
+ }
+
+ // Display Device Utils. Not exposed in the API
+ DisplayDevice* GetDisplayDevice()
+ {
+ return CurrentDisplayDevice;
+ }
+} // namespace Juliet
diff --git a/Juliet/src/Core/HAL/Display/DisplayDevice.h b/Juliet/src/Core/HAL/Display/DisplayDevice.h
new file mode 100644
index 0000000..8ef08b8
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/DisplayDevice.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include
+#include
+
+namespace Juliet
+{
+ // Driver to the display device.
+ // Functions ptr will be set by the chosen factory
+ // Acts as a singleton after Initialize has been called and is freed in Shutdown.
+ struct DisplayDevice
+ {
+ const char* Name = "Unknown";
+
+ // Initialize all subsystems needed for the device to works
+ bool (*Initialize)(NonNullPtr self);
+ void (*Shutdown)(NonNullPtr self);
+ void (*Free)(NonNullPtr self);
+
+ // Window management
+ bool (*CreatePlatformWindow)(NonNullPtr self, NonNullPtr window);
+ void (*DestroyPlatformWindow)(NonNullPtr self, NonNullPtr window);
+ void (*ShowWindow)(NonNullPtr self, NonNullPtr window);
+ void (*HideWindow)(NonNullPtr self, NonNullPtr window);
+
+ // Events
+ void (*PumpEvents)(NonNullPtr self);
+
+ // TODO : Use vector
+ Window* MainWindow = nullptr;
+ };
+
+ struct DisplayDeviceFactory
+ {
+ const char* Name = "Unknown";
+ DisplayDevice* (*CreateDevice)(void);
+ };
+
+ // TODO : Support more platforms
+ extern DisplayDeviceFactory Win32DisplayDeviceFactory;
+
+ // Utils
+ extern DisplayDevice* GetDisplayDevice();
+} // namespace Juliet
diff --git a/Juliet/src/Core/HAL/Display/Display_Private.h b/Juliet/src/Core/HAL/Display/Display_Private.h
new file mode 100644
index 0000000..0256c5b
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/Display_Private.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace Juliet
+{
+ void InitializeDisplaySystem();
+ void ShutdownDisplaySystem();
+} // namespace Juliet
diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.cpp b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.cpp
new file mode 100644
index 0000000..7618b36
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.cpp
@@ -0,0 +1,52 @@
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Juliet::Win32
+{
+ namespace
+ {
+ bool Initialize(NonNullPtr self)
+ {
+ return true;
+ }
+ void Shutdown(NonNullPtr self) {}
+ void Free(NonNullPtr self)
+ {
+ Juliet::Free(self.Get());
+ }
+
+ DisplayDevice* CreateDevice()
+ {
+ auto device = static_cast(Calloc(1, sizeof(DisplayDevice)));
+ if (!device)
+ {
+ return nullptr;
+ }
+
+ device->Initialize = Initialize;
+ device->Shutdown = Shutdown;
+ device->Free = Free;
+
+ device->CreatePlatformWindow = CreatePlatformWindow;
+ device->DestroyPlatformWindow = DestroyPlatformWindow;
+ device->ShowWindow = ShowWindow;
+ device->HideWindow = HideWindow;
+
+ device->PumpEvents = PumpEvents;
+
+ return device;
+ }
+ } // namespace
+
+} // namespace Juliet::Win32
+
+// Factory cannot be in an anonymous/unknown namespace
+namespace Juliet
+{
+ DisplayDeviceFactory Win32DisplayDeviceFactory = { .Name = "Win32", .CreateDevice = Win32::CreateDevice };
+}
diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.h b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.h
new file mode 100644
index 0000000..46ac501
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayDevice.h
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace Juliet::Win32
+{
+
+}
diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.cpp b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.cpp
new file mode 100644
index 0000000..b980fbb
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.cpp
@@ -0,0 +1,272 @@
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// For GET_X_LPARAM, GET_Y_LPARAM.
+#include
+
+namespace Juliet::Win32
+{
+ namespace
+ {
+ constexpr uint8 kPeekMessageLimit = 3;
+
+ // Win32 Display Utils
+ Window32State* GetWindowStateFromHandle(HWND handle)
+ {
+ if (auto* displayDevice = GetDisplayDevice())
+ {
+ auto* window = displayDevice->MainWindow;
+ // TODO : use a vector
+ // for (Window* window : displayDevice->MainWindow)
+ {
+ if (window)
+ {
+ auto state = reinterpret_cast(window->State);
+ if (state && state->Handle == handle)
+ {
+ return state;
+ }
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ // Win32 Keyboard Utils
+ Key GetScanScodeFromWinScanCode(WPARAM wParam, LPARAM lParam)
+ {
+ Key key = {};
+
+ uint16 flags = HIWORD(lParam);
+ uint16 scanCode = LOBYTE(flags);
+
+ if (scanCode != 0)
+ {
+ // Use extended keyboard
+ if ((flags & KF_EXTENDED) == KF_EXTENDED)
+ {
+ // Transforms into the range of our scancodes
+ scanCode = MAKEWORD(scanCode, 0xe0);
+ }
+ else if (scanCode == 0x45)
+ {
+ // Pause scancode in win32 is 0xE11D45 (for raw input) or 0x0045 for legacy KeyDown (which is the
+ // one we use) Break is 0x0046 (Ctrl + Pause). We merge it into one because nobody cares.
+ scanCode = 0x46;
+ }
+ }
+ else
+ {
+ Assert(false && "Virtual Key : Unimplemented");
+ // uint16 virtualKey = LOWORD(wParam);
+ }
+
+ // Pack the scancode to have everything within one byte.
+ // Because scancode low byte are never bigger than 0x7F
+ // We can use the most significant bit to store anything that is in the High Byte of the scancode
+ uint8 index = LOBYTE(scanCode) | (HIBYTE(scanCode) ? 0x80 : 0x00);
+ key.ScanCode = Win32ToHIDUsagePageTable[index];
+ key.Raw = scanCode;
+
+ return key;
+ }
+
+ static_assert(ArraySize(Win32ToHIDUsagePageTable) > sizeof(uint8));
+
+ void ExtractMouseButtonState(uint64 timestamp, bool buttonPressed, MouseButton buttonFlag, bool swapButtons,
+ NonNullPtr windowState, MouseButton button, MouseID mouseID)
+ {
+ if (swapButtons)
+ {
+ if (button == MouseButton::Left)
+ {
+ button = MouseButton::Right;
+ }
+ else if (button == MouseButton::Right)
+ {
+ button = MouseButton::Left;
+ }
+ }
+
+ if (buttonPressed && ((buttonFlag & button) == MouseButton::None))
+ {
+ SendMouseButton(timestamp, windowState->Window, mouseID, button, true);
+ }
+ else if (!buttonPressed && ((buttonFlag & button) != MouseButton::None))
+ {
+ SendMouseButton(timestamp, windowState->Window, mouseID, button, false);
+ }
+ }
+
+ WPARAM LastFrameButtonState = -1;
+ void ExtractAllMouseButtonState(uint64 timestamp, WPARAM wParam, NonNullPtr windowState, MouseID mouseID)
+ {
+ if (wParam != LastFrameButtonState)
+ {
+ MouseButton buttonFlags = GetMouseButtonState();
+ ExtractMouseButtonState(timestamp, (wParam & MK_LBUTTON), buttonFlags, false, windowState, MouseButton::Left, mouseID);
+ ExtractMouseButtonState(timestamp, (wParam & MK_MBUTTON), buttonFlags, false, windowState,
+ MouseButton::Middle, mouseID);
+ ExtractMouseButtonState(timestamp, (wParam & MK_RBUTTON), buttonFlags, false, windowState, MouseButton::Right, mouseID);
+ ExtractMouseButtonState(timestamp, (wParam & MK_XBUTTON1), buttonFlags, false, windowState,
+ MouseButton::Button1, mouseID);
+ ExtractMouseButtonState(timestamp, (wParam & MK_XBUTTON2), buttonFlags, false, windowState,
+ MouseButton::Button2, mouseID);
+ LastFrameButtonState = wParam;
+ }
+ }
+ } // namespace
+
+ void PumpEvents(NonNullPtr self)
+ {
+ uint8 peekedMessageCount = 0;
+ MSG message = {};
+ while (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&message);
+ DispatchMessage(&message);
+
+ // Since we peek at all messages of the program, it's possible that it stall here so we limit the number of peeked messages to an arbitrary limit
+ if (++peekedMessageCount > kPeekMessageLimit)
+ {
+ break;
+ }
+ }
+ }
+
+ LRESULT CALLBACK Win32MainWindowCallback(HWND handle, UINT message, WPARAM wParam, LPARAM lParam)
+ {
+ LRESULT returnCode = -1;
+
+ // Wait until the window state is created before doing anything
+ auto* windowState = GetWindowStateFromHandle(handle);
+ if (!windowState)
+ {
+ return CallWindowProcA(DefWindowProcA, handle, message, wParam, lParam);
+ }
+
+ switch (message)
+ {
+ case WM_CLOSE:
+ {
+ SendWindowEvent(windowState->Window, EventType::Window_Close_Request);
+ returnCode = 0;
+ break;
+ }
+ case WM_DESTROY:
+ {
+ PostQuitMessage(0);
+ break;
+ }
+
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ {
+ Key key = GetScanScodeFromWinScanCode(wParam, lParam);
+
+ // Catch Alt+F4 and close the window immediately
+ if (key.ScanCode == ScanCode::F4 && ((GetKeyModState() & KeyMod::Alt) != KeyMod::None))
+ {
+ SendWindowEvent(windowState->Window, EventType::Window_Close_Request);
+ }
+
+ SendKeyboardKey(1, kGlobalKeyboardID, key, KeyState::Down);
+
+ returnCode = 0;
+ break;
+ }
+ case WM_SYSKEYUP:
+ case WM_KEYUP:
+ {
+ Key key = GetScanScodeFromWinScanCode(wParam, lParam);
+ SendKeyboardKey(1, kGlobalKeyboardID, key, KeyState::Up);
+
+ returnCode = 0;
+ break;
+ }
+
+ case WM_MOUSEMOVE:
+ {
+ if (!windowState->IsMouseTracked)
+ {
+ TRACKMOUSEEVENT MouseEvent;
+ MouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
+ MouseEvent.hwndTrack = windowState->Handle;
+ MouseEvent.dwFlags = TME_LEAVE;
+ TrackMouseEvent(&MouseEvent);
+
+ windowState->IsMouseTracked = true;
+ }
+
+ SendMouseMotion(1, windowState->Window, kGlobalMouseID, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+
+ break;
+ }
+
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_XBUTTONUP:
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONDBLCLK:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONDBLCLK:
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONDBLCLK:
+ {
+ ExtractAllMouseButtonState(1, wParam, windowState, kGlobalMouseID);
+ break;
+ }
+
+ case WM_MOUSELEAVE:
+ {
+ // TODO : Check focus
+ const bool isNotMinimized = !IsIconic(handle);
+ if (isNotMinimized)
+ {
+ POINT cursorPos;
+ GetCursorPos(&cursorPos);
+ ScreenToClient(handle, &cursorPos);
+ SendMouseMotion(1, windowState->Window, kGlobalMouseID, static_cast(cursorPos.x),
+ static_cast(cursorPos.y));
+ }
+
+ windowState->IsMouseTracked = false;
+
+ returnCode = 0;
+ break;
+ }
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT Paint;
+ BeginPaint(handle, &Paint);
+ EndPaint(handle, &Paint);
+ break;
+ }
+ default: break;
+ }
+
+ if (returnCode >= 0)
+ {
+ return returnCode;
+ }
+
+ return CallWindowProcA(DefWindowProcA, handle, message, wParam, lParam);
+ }
+} // namespace Juliet::Win32
diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.h b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.h
new file mode 100644
index 0000000..a6af2e6
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/Win32/Win32DisplayEvent.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include
+#include
+
+namespace Juliet
+{
+ struct DisplayDevice;
+}
+
+namespace Juliet::Win32
+{
+ extern void PumpEvents(NonNullPtr self);
+ extern LRESULT CALLBACK Win32MainWindowCallback(HWND Handle, UINT Message, WPARAM WParam, LPARAM LParam);
+} // namespace Juliet::Win32
diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp b/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp
new file mode 100644
index 0000000..0f86baf
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp
@@ -0,0 +1,166 @@
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Juliet::Win32
+{
+ namespace
+ {
+ constexpr auto WindowClassName = L"JulietWindowClass";
+ constexpr LPCWSTR WindowClassPtr = WindowClassName;
+
+ bool SetupWindowState(NonNullPtr self, NonNullPtr window, HWND handle)
+ {
+ auto state = static_cast(Calloc(1, sizeof(Window32State)));
+ window->State = state;
+ state->Handle = handle;
+ state->Window = window;
+ state->HDC = GetDC(handle);
+
+ state->IsMouseTracked = false;
+
+ // TODO Use SetProp to associate data to the window handle. Could be used to fetch it from the WinProc
+
+ return true;
+ }
+
+ void CleanUpWindowState(NonNullPtr self, NonNullPtr window)
+ {
+ if (auto* state = reinterpret_cast(window->State))
+ {
+ ReleaseDC(state->Handle, state->HDC);
+ DestroyWindow(state->Handle);
+
+ Free(state);
+ }
+ window->State = nullptr;
+ }
+ } // namespace
+
+ bool CreatePlatformWindow(NonNullPtr self, NonNullPtr window)
+ {
+ // TODO : save the instance in app state or something
+ HINSTANCE instance = GetModuleHandle(NULL);
+
+ // TODO : Put outside, we should not create a new class for each new window
+ WNDCLASSEX WindowClass = {};
+ WindowClass.cbSize = sizeof(WNDCLASSEX);
+ WindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ WindowClass.lpfnWndProc = Win32MainWindowCallback;
+ WindowClass.hInstance = instance;
+ WindowClass.hCursor = LoadCursor(0, IDC_ARROW);
+ WindowClass.hbrBackground = static_cast(GetStockObject(LTGRAY_BRUSH));
+ WindowClass.lpszClassName = WindowClassName;
+ if (!RegisterClassEx(&WindowClass))
+ {
+ return false;
+ }
+
+ DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW;
+ DWORD styleEx = 0;
+
+ int x = CW_USEDEFAULT, y = CW_USEDEFAULT;
+ const int w = window->Width, h = window->Height;
+ HWND handle = CreateWindowEx(styleEx, WindowClassPtr, L"JULIET TODO PASS TITLE", style, x, y, w, h, nullptr,
+ nullptr, instance, nullptr);
+
+ PumpEvents(self);
+
+ if (!SetupWindowState(self, window, handle))
+ {
+ return false;
+ }
+ Assert(window->State);
+
+ return true;
+ }
+
+ void DestroyPlatformWindow(NonNullPtr self, NonNullPtr window)
+ {
+ CleanUpWindowState(self, window);
+ }
+
+ void ShowWindow(NonNullPtr self, NonNullPtr window)
+ {
+ Assert(window);
+ Assert(window->State);
+
+ auto& win32State = reinterpret_cast(*window->State);
+ ::ShowWindow(win32State.Handle, SW_SHOW);
+ }
+
+ void HideWindow(NonNullPtr self, NonNullPtr window)
+ {
+ auto& win32State = reinterpret_cast(*window->State);
+ ::ShowWindow(win32State.Handle, SW_HIDE);
+ }
+
+ /*
+ namespace
+ {
+ LRESULT CALLBACK Win32MainWindowCallback(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam);
+ }
+
+ void CreateOSWindow(WindowState& state, uint16 width, uint16 height)
+ {
+ auto& win32State = reinterpret_cast(state);
+
+ HINSTANCE Instance = GetModuleHandle(0);
+
+ WNDCLASSA WindowClass = {};
+ WindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ WindowClass.lpfnWndProc = Win32MainWindowCallback;
+ WindowClass.hInstance = Instance;
+ WindowClass.hCursor = LoadCursor(0, IDC_ARROW);
+ WindowClass.hbrBackground = static_cast(GetStockObject(LTGRAY_BRUSH));
+ WindowClass.lpszClassName = WindowClassName;
+
+ if (RegisterClassA(&WindowClass))
+ {
+ HWND handle = CreateWindowExA(0, WindowClass.lpszClassName, "Juliet", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
+ CW_USEDEFAULT, width, height, 0, 0, Instance, 0);
+ if (handle)
+ {
+ win32State.Handle = handle;
+ SetWindowLongPtr(win32State.Handle, GWLP_USERDATA, reinterpret_cast(&win32State));
+ ShowWindow(handle, SW_SHOW);
+ win32State.IsOpen = true;
+ }
+ else
+ {
+ Assert(false);
+ // Win32ErrorMessage(PlatformError_Fatal,
+ // "Unable to open game window.");
+ }
+ }
+ else
+ {
+ Assert(false);
+ // Win32ErrorMessage(PlatformError_Fatal,
+ // "Unable to register game window handle.");
+ }
+ }
+
+ void DestroyOSWindow(WindowState& state)
+ {
+ auto& win32State = reinterpret_cast(state);
+ ::DestroyWindow(win32State.Handle);
+ UnregisterClassA(WindowClassName, ::GetModuleHandle(nullptr));
+ }
+
+ void UpdateOSWindowState(WindowState& state)
+ {
+ auto& win32State = reinterpret_cast(state);
+ MSG msg = {};
+ while (::PeekMessage(&msg, win32State.Handle, 0, 0, PM_REMOVE))
+ {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+ }
+*/
+} // namespace Juliet::Win32
diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32Window.h b/Juliet/src/Core/HAL/Display/Win32/Win32Window.h
new file mode 100644
index 0000000..036cfb3
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/Win32/Win32Window.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include
+#include
+
+namespace Juliet
+{
+ struct DisplayDevice;
+ struct Window;
+} // namespace Juliet
+
+namespace Juliet::Win32
+{
+ // TODO : Evaluate if its worth the burden of casting to Window32State all the time
+ struct Window32State : WindowState
+ {
+ HWND Handle;
+ HDC HDC;
+
+ bool IsMouseTracked : 1;
+ };
+
+ extern bool CreatePlatformWindow(NonNullPtr self, NonNullPtr window);
+ extern void DestroyPlatformWindow(NonNullPtr self, NonNullPtr window);
+ extern void ShowWindow(NonNullPtr self, NonNullPtr window);
+ extern void HideWindow(NonNullPtr self, NonNullPtr window);
+} // namespace Juliet::Win32
diff --git a/Juliet/src/Core/HAL/Display/Window.h b/Juliet/src/Core/HAL/Display/Window.h
new file mode 100644
index 0000000..03a0be6
--- /dev/null
+++ b/Juliet/src/Core/HAL/Display/Window.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+
+namespace Juliet
+{
+ struct Window;
+ struct WindowState
+ {
+ Window* Window;
+ };
+
+ struct Window
+ {
+ WindowID ID;
+ WindowState* State;
+
+ int32 Width;
+ int32 Height;
+ };
+} // namespace Juliet
diff --git a/Juliet/src/Core/HAL/Event/Keyboard.cpp b/Juliet/src/Core/HAL/Event/Keyboard.cpp
new file mode 100644
index 0000000..0b27897
--- /dev/null
+++ b/Juliet/src/Core/HAL/Event/Keyboard.cpp
@@ -0,0 +1,139 @@
+#include
+
+#include
+#include
+#include
+#include
+
+namespace Juliet
+{
+ constexpr KeyboardID kGlobalKeyboardID = 0;
+
+ namespace
+ {
+ struct KeyboardState
+ {
+ KeyState KeyState[ToUnderlying(ScanCode::Count)];
+ KeyMod KeyModState;
+ } KeyboardState;
+
+ bool SendKeyboardKey_Internal(uint64 timestamp, KeyboardID ID, Key key, KeyState keyState)
+ {
+ Assert(key.KeyCode == KeyCode::Unknown); // At this point Keycode is not yet extracted
+
+ auto& keyboardState = KeyboardState; // Needed because MSVC debugger ignores variable in anonymouse namespace
+
+ auto type = EventType::None;
+ const bool isKeyDown = keyState == KeyState::Down;
+ if (isKeyDown)
+ {
+ type = EventType::Key_Down;
+ }
+ else
+ {
+ type = EventType::Key_Up;
+ }
+
+ bool isKeyRepeat = false;
+ if (key.ScanCode > ScanCode::Unknown && key.ScanCode < ScanCode::Count)
+ {
+ auto& currentKeyState = keyboardState.KeyState[ToUnderlying(key.ScanCode)];
+
+ // If state didn't change, this is a key repeat
+ if (isKeyDown)
+ {
+ if (currentKeyState == KeyState::Down)
+ {
+ isKeyRepeat = true;
+ }
+ }
+ else
+ {
+ if (currentKeyState == KeyState::Up)
+ {
+ return false;
+ }
+ }
+
+ keyboardState.KeyState[ToUnderlying(key.ScanCode)] = keyState;
+ key.KeyCode = GetKeyCodeFromScanCode(key.ScanCode, keyboardState.KeyModState);
+ }
+ else if (key.Raw == 0)
+ {
+ return false;
+ }
+
+ if (!isKeyRepeat)
+ {
+ KeyMod newModifier = {};
+
+ switch (key.KeyCode)
+ {
+ case KeyCode::LeftControl: newModifier = KeyMod::LeftControl; break;
+ case KeyCode::RightControl: newModifier = KeyMod::RightControl; break;
+ case KeyCode::LeftShift: newModifier = KeyMod::LeftShift; break;
+ case KeyCode::RightShift: newModifier = KeyMod::RightShift; break;
+ case KeyCode::LeftAlt: newModifier = KeyMod::LeftAlt; break;
+ case KeyCode::RightAlt: newModifier = KeyMod::RightAlt; break;
+ case KeyCode::LeftOSCommand: newModifier = KeyMod::LeftOSCommand; break;
+ case KeyCode::RightOSCommand: newModifier = KeyMod::RightOSCommand; break;
+ default: newModifier = KeyMod::None; break;
+ }
+ if (type == EventType::Key_Down)
+ {
+ switch (key.KeyCode)
+ {
+ case KeyCode::NumlockClear: newModifier ^= KeyMod::NumLock; break;
+ case KeyCode::CapsLock: newModifier ^= KeyMod::CapsLock; break;
+ case KeyCode::ScrollLock: newModifier ^= KeyMod::ScrollLock; break;
+ default: keyboardState.KeyModState |= newModifier;
+ }
+ }
+ else
+ {
+ // Remove from any keymod not pressed from the modifier
+ keyboardState.KeyModState &= ~newModifier;
+ }
+ }
+
+ SystemEvent evt;
+ evt.Timestamp = timestamp;
+ evt.Type = type;
+ evt.Data.Keyboard.AssociatedKeyboardID = kGlobalKeyboardID;
+ evt.Data.Keyboard.Key = key;
+ evt.Data.Keyboard.KeyState = keyState;
+ evt.Data.Keyboard.KeyModeState = keyboardState.KeyModState;
+
+ bool evtPosted = AddEvent(evt);
+
+ return evtPosted;
+ }
+ } // namespace
+
+ bool SendKeyboardKey(uint64 timestamp, KeyboardID ID, Key key, KeyState keyState)
+ {
+ return SendKeyboardKey_Internal(timestamp, ID, key, keyState);
+ }
+
+ bool IsKeyDown(ScanCode scanCode)
+ {
+ return KeyboardState.KeyState[ToUnderlying(scanCode)] == KeyState::Down;
+ }
+
+ KeyMod GetKeyModState()
+ {
+ auto& keyboardState = KeyboardState;
+ return keyboardState.KeyModState;
+ }
+
+ KeyCode GetKeyCodeFromScanCode(ScanCode scanCode, KeyMod keyModState)
+ {
+ return GetKeyCodeFromDefaultMapping(scanCode, keyModState);
+ }
+
+ static_assert(sizeof(KeyState) == sizeof(bool));
+ static_assert(ToUnderlying(KeyState::Down) == true);
+ static_assert(ToUnderlying(KeyState::Up) == false);
+ static_assert(sizeof(ScanCode) == sizeof(uint16));
+ static_assert(ToUnderlying(ScanCode::Count) == 512);
+} // namespace Juliet
diff --git a/Juliet/src/Core/HAL/Event/KeyboardMapping.cpp b/Juliet/src/Core/HAL/Event/KeyboardMapping.cpp
new file mode 100644
index 0000000..a83d099
--- /dev/null
+++ b/Juliet/src/Core/HAL/Event/KeyboardMapping.cpp
@@ -0,0 +1,205 @@
+#include
+
+#include
+#include
+#include
+#include
+
+namespace Juliet
+{
+ namespace
+ {
+ // clang-format off
+ KeyCode UnshiftedDefaultSymbols[] = {
+ KeyCode::Num1,
+ KeyCode::Num2,
+ KeyCode::Num3,
+ KeyCode::Num4,
+ KeyCode::Num5,
+ KeyCode::Num6,
+ KeyCode::Num7,
+ KeyCode::Num8,
+ KeyCode::Num9,
+ KeyCode::Num0,
+ KeyCode::Return,
+ KeyCode::Escape,
+ KeyCode::Backspace,
+ KeyCode::Tab,
+ KeyCode::Space,
+ KeyCode::Minus,
+ KeyCode::Equals,
+ KeyCode::LeftBracket,
+ KeyCode::RightBracket,
+ KeyCode::Backslash,
+ KeyCode::Hash,
+ KeyCode::Semicolon,
+ KeyCode::Apostrophe,
+ KeyCode::GraveAccent,
+ KeyCode::Comma,
+ KeyCode::Period,
+ KeyCode::Slash,
+ };
+
+ KeyCode ShiftedDefaultSymbols[] = {
+ KeyCode::ExclamationPoint,
+ KeyCode::CommercialAt,
+ KeyCode::Hash,
+ KeyCode::Dollar,
+ KeyCode::Percent,
+ KeyCode::Caret,
+ KeyCode::Ampersand,
+ KeyCode::Asterisk,
+ KeyCode::LeftParenthesis,
+ KeyCode::RightParenthesis,
+ KeyCode::Return,
+ KeyCode::Escape,
+ KeyCode::Backspace,
+ KeyCode::Tab,
+ KeyCode::Space,
+ KeyCode::Underscore,
+ KeyCode::Plus,
+ KeyCode::LeftBrace,
+ KeyCode::RightBrace,
+ KeyCode::Pipe,
+ KeyCode::Hash,
+ KeyCode::Colon,
+ KeyCode::DoubleApostrophe,
+ KeyCode::Tilde,
+ KeyCode::LessThan,
+ KeyCode::GreaterThan,
+ KeyCode::QuestionMark,
+ };
+ // clang-format on
+
+ KeyCode GetNonPrintableKeys(ScanCode scanCode)
+ {
+ switch (scanCode)
+ {
+ case ScanCode::Delete: return KeyCode::Delete;
+ case ScanCode::CapsLock: return KeyCode::CapsLock;
+ case ScanCode::F1: return KeyCode::F1;
+ case ScanCode::F2: return KeyCode::F2;
+ case ScanCode::F3: return KeyCode::F3;
+ case ScanCode::F4: return KeyCode::F4;
+ case ScanCode::F5: return KeyCode::F5;
+ case ScanCode::F6: return KeyCode::F6;
+ case ScanCode::F7: return KeyCode::F7;
+ case ScanCode::F8: return KeyCode::F8;
+ case ScanCode::F9: return KeyCode::F9;
+ case ScanCode::F10: return KeyCode::F10;
+ case ScanCode::F11: return KeyCode::F11;
+ case ScanCode::F12: return KeyCode::F12;
+ case ScanCode::PrintScreen: return KeyCode::PrintScreen;
+ case ScanCode::ScrollLock: return KeyCode::ScrollLock;
+ case ScanCode::Pause: return KeyCode::Pause;
+ case ScanCode::Insert: return KeyCode::Insert;
+ case ScanCode::Home: return KeyCode::Home;
+ case ScanCode::PageUp: return KeyCode::PageUp;
+ case ScanCode::End: return KeyCode::End;
+ case ScanCode::PageDown: return KeyCode::PageDown;
+ case ScanCode::RightArrow: return KeyCode::RightArrow;
+ case ScanCode::LeftArrow: return KeyCode::LeftArrow;
+ case ScanCode::DownArrow: return KeyCode::DownArrow;
+ case ScanCode::UpArrow: return KeyCode::UpArrow;
+ case ScanCode::NumlockClear: return KeyCode::NumlockClear;
+ case ScanCode::KeyPad_Divide: return KeyCode::KeyPad_Divide;
+ case ScanCode::KeyPad_Multiply: return KeyCode::KeyPad_Multiply;
+ case ScanCode::KeyPad_Minus: return KeyCode::KeyPad_Minus;
+ case ScanCode::KeyPad_Plus: return KeyCode::KeyPad_Plus;
+ case ScanCode::KeyPad_Enter: return KeyCode::KeyPad_Enter;
+ case ScanCode::KeyPad_Num1: return KeyCode::KeyPad_Num1;
+ case ScanCode::KeyPad_Num2: return KeyCode::KeyPad_Num2;
+ case ScanCode::KeyPad_Num3: return KeyCode::KeyPad_Num3;
+ case ScanCode::KeyPad_Num4: return KeyCode::KeyPad_Num4;
+ case ScanCode::KeyPad_Num5: return KeyCode::KeyPad_Num5;
+ case ScanCode::KeyPad_Num6: return KeyCode::KeyPad_Num6;
+ case ScanCode::KeyPad_Num7: return KeyCode::KeyPad_Num7;
+ case ScanCode::KeyPad_Num8: return KeyCode::KeyPad_Num8;
+ case ScanCode::KeyPad_Num9: return KeyCode::KeyPad_Num9;
+ case ScanCode::KeyPad_Num0: return KeyCode::KeyPad_Num0;
+ case ScanCode::KeyPad_Period: return KeyCode::KeyPad_Period;
+ case ScanCode::Power: return KeyCode::Power;
+ case ScanCode::KeyPad_Equals: return KeyCode::KeyPad_Equals;
+ case ScanCode::F13: return KeyCode::F13;
+ case ScanCode::F14: return KeyCode::F14;
+ case ScanCode::F15: return KeyCode::F15;
+ case ScanCode::F16: return KeyCode::F16;
+ case ScanCode::F17: return KeyCode::F17;
+ case ScanCode::F18: return KeyCode::F18;
+ case ScanCode::F19: return KeyCode::F19;
+ case ScanCode::F20: return KeyCode::F20;
+ case ScanCode::F21: return KeyCode::F21;
+ case ScanCode::F22: return KeyCode::F22;
+ case ScanCode::F23: return KeyCode::F23;
+ case ScanCode::F24: return KeyCode::F24;
+ case ScanCode::Mute: return KeyCode::Mute;
+ case ScanCode::VolumeUp: return KeyCode::VolumeUp;
+ case ScanCode::VolumeDown: return KeyCode::VolumeDown;
+ case ScanCode::KeyPad_Comma: return KeyCode::KeyPad_Comma;
+ case ScanCode::LeftControl: return KeyCode::LeftControl;
+ case ScanCode::LeftShift: return KeyCode::LeftShift;
+ case ScanCode::LeftAlt: return KeyCode::LeftAlt;
+ case ScanCode::LeftOSCommand: return KeyCode::LeftOSCommand;
+ case ScanCode::RightControl: return KeyCode::RightControl;
+ case ScanCode::RightShift: return KeyCode::RightShift;
+ case ScanCode::RightAlt: return KeyCode::RightAlt;
+ case ScanCode::RightOSCommand: return KeyCode::RightOSCommand;
+ case ScanCode::Sleep: return KeyCode::Sleep;
+ case ScanCode::WakeUp: return KeyCode::WakeUp;
+ case ScanCode::Media_NextTrack: return KeyCode::Media_NextTrack;
+ case ScanCode::Media_PreviousTrack: return KeyCode::Media_PreviousTrack;
+ case ScanCode::Media_Stop: return KeyCode::Media_Stop;
+ case ScanCode::Media_Eject: return KeyCode::Media_Eject;
+ case ScanCode::Media_PlayPause: return KeyCode::Media_PlayPause;
+ case ScanCode::Media_Select: return KeyCode::Media_Select;
+ default: return KeyCode::Unknown;
+ }
+ }
+ } // namespace
+
+ KeyCode GetKeyCodeFromDefaultMapping(ScanCode scanCode, KeyMod keyModState)
+ {
+ if (scanCode <= ScanCode::Unknown || scanCode > ScanCode::Count)
+ {
+ Assert(false && "Unsupported KeyCode (out of bounds)");
+ return KeyCode::Unknown;
+ }
+
+ if (scanCode < ScanCode::A)
+ {
+ return KeyCode::Unknown;
+ }
+
+ // Handles A-Z characters
+ if (scanCode <= ScanCode::Z)
+ {
+ const auto index = ToUnderlying(scanCode - ScanCode::A);
+ bool isShiftPressed = (keyModState & KeyMod::Shift) != KeyMod::None;
+ if ((keyModState & KeyMod::CapsLock) != KeyMod::None)
+ {
+ isShiftPressed = !isShiftPressed;
+ }
+
+ if (isShiftPressed)
+ {
+ return ToEnum('A' + index);
+ }
+ return ToEnum('a' + index);
+ }
+
+ // Handles Num1 to Num0
+ if (scanCode <= ScanCode::Num0)
+ {
+ const auto index = ToUnderlying<>(scanCode - ScanCode::Num1);
+ const bool isShiftPressed = (keyModState & KeyMod::Shift) != KeyMod::None;
+ if (isShiftPressed)
+ {
+ return ShiftedDefaultSymbols[index];
+ }
+ return UnshiftedDefaultSymbols[index];
+ }
+
+ // Handle everything else (characters that do not convert to ASCII code)
+ return GetNonPrintableKeys(scanCode);
+ }
+} // namespace Juliet
diff --git a/Juliet/src/Core/HAL/Event/KeyboardMapping.h b/Juliet/src/Core/HAL/Event/KeyboardMapping.h
new file mode 100644
index 0000000..c1ece03
--- /dev/null
+++ b/Juliet/src/Core/HAL/Event/KeyboardMapping.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include