mirror of
				https://github.com/NoelFB/blah.git
				synced 2025-11-04 01:41:34 +08:00 
			
		
		
		
	removing "backend" from internal namespaces
This commit is contained in:
		@ -43,11 +43,11 @@ add_library(blah
 | 
			
		||||
	src/streams/memorystream.cpp
 | 
			
		||||
	src/streams/stream.cpp
 | 
			
		||||
 | 
			
		||||
	src/internal/graphics_backend_gl.cpp
 | 
			
		||||
	src/internal/graphics_backend_d3d11.cpp
 | 
			
		||||
	src/internal/graphics_backend_dummy.cpp
 | 
			
		||||
	src/internal/platform_backend_sdl2.cpp
 | 
			
		||||
	src/internal/platform_backend_win32.cpp
 | 
			
		||||
	src/internal/graphics_gl.cpp
 | 
			
		||||
	src/internal/graphics_d3d11.cpp
 | 
			
		||||
	src/internal/graphics_dummy.cpp
 | 
			
		||||
	src/internal/platform_sdl2.cpp
 | 
			
		||||
	src/internal/platform_win32.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_include_directories(blah
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										70
									
								
								src/app.cpp
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								src/app.cpp
									
									
									
									
									
								
							@ -3,9 +3,9 @@
 | 
			
		||||
#include <blah/time.h>
 | 
			
		||||
#include <blah/numerics/point.h>
 | 
			
		||||
#include <blah/graphics/target.h>
 | 
			
		||||
#include "internal/platform_backend.h"
 | 
			
		||||
#include "internal/graphics_backend.h"
 | 
			
		||||
#include "internal/input_backend.h"
 | 
			
		||||
#include "internal/platform.h"
 | 
			
		||||
#include "internal/graphics.h"
 | 
			
		||||
#include "internal/input.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
#include <emscripten.h>
 | 
			
		||||
@ -44,7 +44,7 @@ namespace
 | 
			
		||||
		// TODO: allow a non-fixed step update?
 | 
			
		||||
		{
 | 
			
		||||
			u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
 | 
			
		||||
			u64 time_curr = PlatformBackend::ticks();
 | 
			
		||||
			u64 time_curr = Platform::ticks();
 | 
			
		||||
			u64 time_diff = time_curr - time_last;
 | 
			
		||||
			time_last = time_curr;
 | 
			
		||||
			time_accumulator += time_diff;
 | 
			
		||||
@ -53,9 +53,9 @@ namespace
 | 
			
		||||
			while (time_accumulator < time_target)
 | 
			
		||||
			{
 | 
			
		||||
				int milliseconds = (int)(time_target - time_accumulator) / (Time::ticks_per_second / 1000);
 | 
			
		||||
				PlatformBackend::sleep(milliseconds);
 | 
			
		||||
				Platform::sleep(milliseconds);
 | 
			
		||||
 | 
			
		||||
				time_curr = PlatformBackend::ticks();
 | 
			
		||||
				time_curr = Platform::ticks();
 | 
			
		||||
				time_diff = time_curr - time_last;
 | 
			
		||||
				time_last = time_curr;
 | 
			
		||||
				time_accumulator += time_diff;
 | 
			
		||||
@ -88,10 +88,10 @@ namespace
 | 
			
		||||
				Time::previous_seconds = Time::seconds;
 | 
			
		||||
				Time::seconds += Time::delta;
 | 
			
		||||
 | 
			
		||||
				InputBackend::update_state();
 | 
			
		||||
				PlatformBackend::update(Input::state);
 | 
			
		||||
				InputBackend::update_bindings();
 | 
			
		||||
				GraphicsBackend::update();
 | 
			
		||||
				Input::update_state();
 | 
			
		||||
				Platform::update(Input::state);
 | 
			
		||||
				Input::update_bindings();
 | 
			
		||||
				Graphics::update();
 | 
			
		||||
 | 
			
		||||
				if (app_config.on_update != nullptr)
 | 
			
		||||
					app_config.on_update();
 | 
			
		||||
@ -100,13 +100,13 @@ namespace
 | 
			
		||||
 | 
			
		||||
		// render
 | 
			
		||||
		{
 | 
			
		||||
			GraphicsBackend::before_render();
 | 
			
		||||
			Graphics::before_render();
 | 
			
		||||
 | 
			
		||||
			if (app_config.on_render != nullptr)
 | 
			
		||||
				app_config.on_render();
 | 
			
		||||
 | 
			
		||||
			GraphicsBackend::after_render();
 | 
			
		||||
			PlatformBackend::present();
 | 
			
		||||
			Graphics::after_render();
 | 
			
		||||
			Platform::present();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -126,31 +126,31 @@ bool App::run(const Config* c)
 | 
			
		||||
	app_is_exiting = false;
 | 
			
		||||
 | 
			
		||||
	// initialize the system
 | 
			
		||||
	if (!PlatformBackend::init(app_config))
 | 
			
		||||
	if (!Platform::init(app_config))
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Failed to initialize Platform module");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// initialize graphics
 | 
			
		||||
	if (!GraphicsBackend::init())
 | 
			
		||||
	if (!Graphics::init())
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Failed to initialize Graphics module");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// input
 | 
			
		||||
	InputBackend::init();
 | 
			
		||||
	Input::init();
 | 
			
		||||
 | 
			
		||||
	// startup
 | 
			
		||||
	if (app_config.on_startup != nullptr)
 | 
			
		||||
		app_config.on_startup();
 | 
			
		||||
 | 
			
		||||
	time_last = PlatformBackend::ticks();
 | 
			
		||||
	time_last = Platform::ticks();
 | 
			
		||||
	time_accumulator = 0;
 | 
			
		||||
 | 
			
		||||
	// display window
 | 
			
		||||
	PlatformBackend::ready();
 | 
			
		||||
	Platform::ready();
 | 
			
		||||
 | 
			
		||||
	// Begin main loop
 | 
			
		||||
	// Emscripten requires the main loop be separated into its own call
 | 
			
		||||
@ -166,8 +166,8 @@ bool App::run(const Config* c)
 | 
			
		||||
	if (app_config.on_shutdown != nullptr)
 | 
			
		||||
		app_config.on_shutdown();
 | 
			
		||||
 | 
			
		||||
	GraphicsBackend::shutdown();
 | 
			
		||||
	PlatformBackend::shutdown();
 | 
			
		||||
	Graphics::shutdown();
 | 
			
		||||
	Platform::shutdown();
 | 
			
		||||
 | 
			
		||||
	// clear static state
 | 
			
		||||
	app_is_running = false;
 | 
			
		||||
@ -195,46 +195,46 @@ const Config& App::config()
 | 
			
		||||
 | 
			
		||||
const char* App::path()
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::app_path();
 | 
			
		||||
	return Platform::app_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::user_path()
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::user_path();
 | 
			
		||||
	return Platform::user_path();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* App::get_title()
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::get_title();
 | 
			
		||||
	return Platform::get_title();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_title(const char* title)
 | 
			
		||||
{
 | 
			
		||||
	PlatformBackend::set_title(title);
 | 
			
		||||
	Platform::set_title(title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_position()
 | 
			
		||||
{
 | 
			
		||||
	Point result;
 | 
			
		||||
	PlatformBackend::get_position(&result.x, &result.y);
 | 
			
		||||
	Platform::get_position(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_position(Point point)
 | 
			
		||||
{
 | 
			
		||||
	PlatformBackend::set_position(point.x, point.y);
 | 
			
		||||
	Platform::set_position(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point App::get_size()
 | 
			
		||||
{
 | 
			
		||||
	Point result;
 | 
			
		||||
	PlatformBackend::get_size(&result.x, &result.y);
 | 
			
		||||
	Platform::get_size(&result.x, &result.y);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::set_size(Point point)
 | 
			
		||||
{
 | 
			
		||||
	PlatformBackend::set_size(point.x, point.y);
 | 
			
		||||
	Platform::set_size(point.x, point.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int App::width()
 | 
			
		||||
@ -250,35 +250,35 @@ int App::height()
 | 
			
		||||
int App::draw_width()
 | 
			
		||||
{
 | 
			
		||||
	int w, h;
 | 
			
		||||
	PlatformBackend::get_draw_size(&w, &h);
 | 
			
		||||
	Platform::get_draw_size(&w, &h);
 | 
			
		||||
	return w;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int App::draw_height()
 | 
			
		||||
{
 | 
			
		||||
	int w, h;
 | 
			
		||||
	PlatformBackend::get_draw_size(&w, &h);
 | 
			
		||||
	Platform::get_draw_size(&w, &h);
 | 
			
		||||
	return h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float App::content_scale()
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::get_content_scale();
 | 
			
		||||
	return Platform::get_content_scale();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void App::fullscreen(bool enabled)
 | 
			
		||||
{
 | 
			
		||||
	PlatformBackend::set_fullscreen(enabled);
 | 
			
		||||
	Platform::set_fullscreen(enabled);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Renderer App::renderer()
 | 
			
		||||
{
 | 
			
		||||
	return GraphicsBackend::renderer();
 | 
			
		||||
	return Graphics::renderer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RendererFeatures& Blah::App::renderer_features()
 | 
			
		||||
{
 | 
			
		||||
	return GraphicsBackend::features();
 | 
			
		||||
	return Graphics::features();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
@ -313,7 +313,7 @@ namespace
 | 
			
		||||
 | 
			
		||||
		void clear(Color color, float depth, u8 stencil, ClearMask mask) override
 | 
			
		||||
		{
 | 
			
		||||
			GraphicsBackend::clear_backbuffer(color, depth, stencil, mask);
 | 
			
		||||
			Graphics::clear_backbuffer(color, depth, stencil, mask);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,37 +1,37 @@
 | 
			
		||||
#include <blah/filesystem.h>
 | 
			
		||||
#include <blah/streams/filestream.h>
 | 
			
		||||
#include "internal/platform_backend.h"
 | 
			
		||||
#include "internal/platform.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
FileRef File::open(const FilePath& path, FileMode mode)
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::file_open(path.cstr(), mode);
 | 
			
		||||
	return Platform::file_open(path.cstr(), mode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool File::exists(const FilePath& path)
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::file_exists(path.cstr());
 | 
			
		||||
	return Platform::file_exists(path.cstr());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool File::destroy(const FilePath& path)
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::file_delete(path.cstr());
 | 
			
		||||
	return Platform::file_delete(path.cstr());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Directory::create(const FilePath& path)
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::dir_create(path.cstr());
 | 
			
		||||
	return Platform::dir_create(path.cstr());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Directory::exists(const FilePath& path)
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::dir_exists(path.cstr());
 | 
			
		||||
	return Platform::dir_exists(path.cstr());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Directory::destroy(const FilePath& path)
 | 
			
		||||
{
 | 
			
		||||
	return PlatformBackend::dir_delete(path.cstr());
 | 
			
		||||
	return Platform::dir_delete(path.cstr());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vector<FilePath> Directory::enumerate(const FilePath& path, bool recursive)
 | 
			
		||||
@ -39,7 +39,7 @@ Vector<FilePath> Directory::enumerate(const FilePath& path, bool recursive)
 | 
			
		||||
	Vector<FilePath> list;
 | 
			
		||||
 | 
			
		||||
	// get files
 | 
			
		||||
	PlatformBackend::dir_enumerate(list, path.cstr(), recursive);
 | 
			
		||||
	Platform::dir_enumerate(list, path.cstr(), recursive);
 | 
			
		||||
 | 
			
		||||
	// normalize path names
 | 
			
		||||
	for (auto& it : list)
 | 
			
		||||
@ -50,7 +50,7 @@ Vector<FilePath> Directory::enumerate(const FilePath& path, bool recursive)
 | 
			
		||||
 | 
			
		||||
void Directory::explore(const FilePath& path)
 | 
			
		||||
{
 | 
			
		||||
	PlatformBackend::dir_explore(path);
 | 
			
		||||
	Platform::dir_explore(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FilePath Path::get_file_name(const FilePath& path)
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,12 @@
 | 
			
		||||
#include <blah/graphics/mesh.h>
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MeshRef Mesh::create()
 | 
			
		||||
{
 | 
			
		||||
	return GraphicsBackend::create_mesh();
 | 
			
		||||
	return Graphics::create_mesh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VertexFormat::VertexFormat(std::initializer_list<VertexAttribute> attributes, int stride)
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
#include <blah/graphics/renderpass.h>
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
@ -86,5 +86,5 @@ void RenderPass::perform()
 | 
			
		||||
		pass.scissor = pass.scissor.overlap_rect(Rect(0, 0, draw_size.x, draw_size.y));
 | 
			
		||||
 | 
			
		||||
	// perform render
 | 
			
		||||
	GraphicsBackend::render(pass);
 | 
			
		||||
	Graphics::render(pass);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
#include <blah/graphics/shader.h>
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ ShaderRef Shader::create(const ShaderData& data)
 | 
			
		||||
	BLAH_ASSERT(data.fragment.length() > 0, "Must provide a Fragment Shader");
 | 
			
		||||
	BLAH_ASSERT(data.hlsl_attributes.size() > 0 || App::renderer() != Renderer::D3D11, "D3D11 Shaders must have hlsl_attributes assigned");
 | 
			
		||||
 | 
			
		||||
	auto shader = GraphicsBackend::create_shader(&data);
 | 
			
		||||
	auto shader = Graphics::create_shader(&data);
 | 
			
		||||
	
 | 
			
		||||
	// validate the shader
 | 
			
		||||
	if (shader)
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
#include <blah/graphics/target.h>
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ TargetRef Target::create(int width, int height, const AttachmentFormats& texture
 | 
			
		||||
	BLAH_ASSERT(depth_count <= 1, "Target can only have 1 Depth/Stencil Texture");
 | 
			
		||||
	BLAH_ASSERT(color_count <= Attachments::capacity - 1, "Exceeded maximum Color texture count");
 | 
			
		||||
 | 
			
		||||
	return GraphicsBackend::create_target(width, height, textures.data(), textures.size());
 | 
			
		||||
	return Graphics::create_target(width, height, textures.data(), textures.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TextureRef& Target::texture(int index)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
#include <blah/images/image.h>
 | 
			
		||||
#include <blah/streams/stream.h>
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include "../internal/graphics.h"
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ TextureRef Texture::create(int width, int height, TextureFormat format, unsigned
 | 
			
		||||
	BLAH_ASSERT(width > 0 && height > 0, "Texture width and height must be larger than 0");
 | 
			
		||||
	BLAH_ASSERT((int)format > (int)TextureFormat::None && (int)format < (int)TextureFormat::Count, "Invalid texture format");
 | 
			
		||||
 | 
			
		||||
	auto tex = GraphicsBackend::create_texture(width, height, format);
 | 
			
		||||
	auto tex = Graphics::create_texture(width, height, format);
 | 
			
		||||
 | 
			
		||||
	if (tex && data != nullptr)
 | 
			
		||||
		tex->set_data(data);
 | 
			
		||||
 | 
			
		||||
@ -5,19 +5,10 @@
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
#ifdef __CLANG__
 | 
			
		||||
#pragma clang diagnostic push
 | 
			
		||||
#pragma clang diagnostic ignored "-Wunused-function"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define STBTT_STATIC
 | 
			
		||||
#define STB_TRUETYPE_IMPLEMENTATION
 | 
			
		||||
#include "../third_party/stb_truetype.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __CLANG__
 | 
			
		||||
#pragma clang diagnostic pop
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
	String get_font_name(stbtt_fontinfo* font, int nameId)
 | 
			
		||||
 | 
			
		||||
@ -15,18 +15,18 @@ using namespace Blah;
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
	int Blah_STBI_Read(void* user, char* data, int size)
 | 
			
		||||
	int blah_stbi_read(void* user, char* data, int size)
 | 
			
		||||
	{
 | 
			
		||||
		i64 read = ((Stream*)user)->read(data, size);
 | 
			
		||||
		return (int)read;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Blah_STBI_Skip(void* user, int n)
 | 
			
		||||
	void blah_stbi_skip(void* user, int n)
 | 
			
		||||
	{
 | 
			
		||||
		((Stream*)user)->seek(((Stream*)user)->position() + n);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int Blah_STBI_Eof(void* user)
 | 
			
		||||
	int blaH_stbi_eof(void* user)
 | 
			
		||||
	{
 | 
			
		||||
		i64 position = ((Stream*)user)->position();
 | 
			
		||||
		i64 length = ((Stream*)user)->length();
 | 
			
		||||
@ -37,7 +37,7 @@ namespace
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Blah_STBI_Write(void* context, void* data, int size)
 | 
			
		||||
	void blah_stbi_write(void* context, void* data, int size)
 | 
			
		||||
	{
 | 
			
		||||
		((Stream*)context)->write((char*)data, size);
 | 
			
		||||
	}
 | 
			
		||||
@ -142,34 +142,30 @@ Image::~Image()
 | 
			
		||||
	dispose();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Image::from_stream(Stream& stream)
 | 
			
		||||
bool Image::from_stream(Stream& stream)
 | 
			
		||||
{
 | 
			
		||||
	dispose();
 | 
			
		||||
 | 
			
		||||
	if (!stream.is_readable())
 | 
			
		||||
	{
 | 
			
		||||
		BLAH_ASSERT(false, "Unable to load image as the Stream was not readable");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	stbi_io_callbacks callbacks;
 | 
			
		||||
	callbacks.eof = Blah_STBI_Eof;
 | 
			
		||||
	callbacks.read = Blah_STBI_Read;
 | 
			
		||||
	callbacks.skip = Blah_STBI_Skip;
 | 
			
		||||
	callbacks.eof = blaH_stbi_eof;
 | 
			
		||||
	callbacks.read = blah_stbi_read;
 | 
			
		||||
	callbacks.skip = blah_stbi_skip;
 | 
			
		||||
 | 
			
		||||
	int x, y, comps;
 | 
			
		||||
	u8* data = stbi_load_from_callbacks(&callbacks, &stream, &x, &y, &comps, 4);
 | 
			
		||||
 | 
			
		||||
	if (data == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		BLAH_ASSERT(false, "Unable to load image as the Stream's data was not a valid image");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	m_stbi_ownership = true;
 | 
			
		||||
	pixels = (Color*)data;
 | 
			
		||||
	width = x;
 | 
			
		||||
	height = y;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Image::dispose()
 | 
			
		||||
@ -223,14 +219,8 @@ bool Image::save_png(Stream& stream) const
 | 
			
		||||
		stbi_write_force_png_filter = 0;
 | 
			
		||||
		stbi_write_png_compression_level = 0;
 | 
			
		||||
 | 
			
		||||
		if (stbi_write_png_to_func(Blah_STBI_Write, &stream, width, height, 4, pixels, width * 4) != 0)
 | 
			
		||||
		if (stbi_write_png_to_func(blah_stbi_write, &stream, width, height, 4, pixels, width * 4) != 0)
 | 
			
		||||
			return true;
 | 
			
		||||
		else
 | 
			
		||||
			Log::error("stbi_write_png_to_func failed");
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Cannot save Image, the Stream is not writable");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
@ -260,14 +250,8 @@ bool Image::save_jpg(Stream& stream, int quality) const
 | 
			
		||||
 | 
			
		||||
	if (stream.is_writable())
 | 
			
		||||
	{
 | 
			
		||||
		if (stbi_write_jpg_to_func(Blah_STBI_Write, &stream, width, height, 4, pixels, quality) != 0)
 | 
			
		||||
		if (stbi_write_jpg_to_func(blah_stbi_write, &stream, width, height, 4, pixels, quality) != 0)
 | 
			
		||||
			return true;
 | 
			
		||||
		else
 | 
			
		||||
			Log::error("stbi_write_jpg_to_func failed");
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Cannot save Image, the Stream is not writable");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include <blah/numerics/point.h>
 | 
			
		||||
#include <blah/numerics/calc.h>
 | 
			
		||||
#include "internal/input_backend.h"
 | 
			
		||||
#include "internal/input.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
@ -21,7 +21,7 @@ namespace
 | 
			
		||||
InputState Blah::Input::state;
 | 
			
		||||
InputState Blah::Input::last_state;
 | 
			
		||||
 | 
			
		||||
void InputBackend::init()
 | 
			
		||||
void Input::init()
 | 
			
		||||
{
 | 
			
		||||
	g_empty_controller.name = "Disconnected";
 | 
			
		||||
	for (int i = 0; i < Input::max_controllers; i++)
 | 
			
		||||
@ -34,7 +34,7 @@ void InputBackend::init()
 | 
			
		||||
	g_sticks.dispose();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InputBackend::update_state()
 | 
			
		||||
void Input::update_state()
 | 
			
		||||
{
 | 
			
		||||
	// cycle states
 | 
			
		||||
	Input::last_state = Input::state;
 | 
			
		||||
@ -70,7 +70,7 @@ void InputBackend::update_state()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InputBackend::update_bindings()
 | 
			
		||||
void Input::update_bindings()
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < g_buttons.size(); i++)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	// Graphics backend API used for rendering.
 | 
			
		||||
	// All rendering ends up going through here.
 | 
			
		||||
	namespace GraphicsBackend
 | 
			
		||||
	namespace Graphics
 | 
			
		||||
	{
 | 
			
		||||
		// Initializes the graphics backend
 | 
			
		||||
		bool init();
 | 
			
		||||
@ -3,8 +3,8 @@
 | 
			
		||||
// TODO:
 | 
			
		||||
// Note the D3D11 Implementation is still a work-in-progress
 | 
			
		||||
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include "../internal/platform_backend.h"
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
@ -673,7 +673,7 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool GraphicsBackend::init()
 | 
			
		||||
	bool Graphics::init()
 | 
			
		||||
	{
 | 
			
		||||
		state = D3D11();
 | 
			
		||||
		state.last_size = Point(App::draw_width(), App::draw_height());
 | 
			
		||||
@ -687,7 +687,7 @@ namespace Blah
 | 
			
		||||
		desc.SampleDesc.Quality = 0;
 | 
			
		||||
		desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 | 
			
		||||
		desc.BufferCount = 1;
 | 
			
		||||
		desc.OutputWindow = (HWND)PlatformBackend::d3d11_get_hwnd();
 | 
			
		||||
		desc.OutputWindow = (HWND)Platform::d3d11_get_hwnd();
 | 
			
		||||
		desc.Windowed = true;
 | 
			
		||||
 | 
			
		||||
		// Creation Flags
 | 
			
		||||
@ -755,12 +755,12 @@ namespace Blah
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Renderer GraphicsBackend::renderer()
 | 
			
		||||
	Renderer Graphics::renderer()
 | 
			
		||||
	{
 | 
			
		||||
		return Renderer::D3D11;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::shutdown()
 | 
			
		||||
	void Graphics::shutdown()
 | 
			
		||||
	{
 | 
			
		||||
		// release cached objects
 | 
			
		||||
		for (auto& it : state.blend_cache)
 | 
			
		||||
@ -787,16 +787,16 @@ namespace Blah
 | 
			
		||||
		state = D3D11();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const RendererFeatures& GraphicsBackend::features()
 | 
			
		||||
	const RendererFeatures& Graphics::features()
 | 
			
		||||
	{
 | 
			
		||||
		return state.features;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::update()
 | 
			
		||||
	void Graphics::update()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::before_render()
 | 
			
		||||
	void Graphics::before_render()
 | 
			
		||||
	{
 | 
			
		||||
		HRESULT hr;
 | 
			
		||||
 | 
			
		||||
@ -824,13 +824,13 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::after_render()
 | 
			
		||||
	void Graphics::after_render()
 | 
			
		||||
	{
 | 
			
		||||
		auto hr = state.swap_chain->Present(1, 0);
 | 
			
		||||
		BLAH_ASSERT(SUCCEEDED(hr), "Failed to Present swap chain");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TextureRef GraphicsBackend::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	TextureRef Graphics::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	{
 | 
			
		||||
		auto result = new D3D11_Texture(width, height, format, false);
 | 
			
		||||
 | 
			
		||||
@ -841,12 +841,12 @@ namespace Blah
 | 
			
		||||
		return TextureRef();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TargetRef GraphicsBackend::create_target(int width, int height, const TextureFormat* attachments, int attachment_count)
 | 
			
		||||
	TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachment_count)
 | 
			
		||||
	{
 | 
			
		||||
		return TargetRef(new D3D11_Target(width, height, attachments, attachment_count));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ShaderRef GraphicsBackend::create_shader(const ShaderData* data)
 | 
			
		||||
	ShaderRef Graphics::create_shader(const ShaderData* data)
 | 
			
		||||
	{
 | 
			
		||||
		auto result = new D3D11_Shader(data);
 | 
			
		||||
		if (result->valid)
 | 
			
		||||
@ -856,12 +856,12 @@ namespace Blah
 | 
			
		||||
		return ShaderRef();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MeshRef GraphicsBackend::create_mesh()
 | 
			
		||||
	MeshRef Graphics::create_mesh()
 | 
			
		||||
	{
 | 
			
		||||
		return MeshRef(new D3D11_Mesh());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::render(const RenderPass& pass)
 | 
			
		||||
	void Graphics::render(const RenderPass& pass)
 | 
			
		||||
	{
 | 
			
		||||
		auto ctx = state.context;
 | 
			
		||||
		auto mesh = (D3D11_Mesh*)pass.mesh.get();
 | 
			
		||||
@ -1038,7 +1038,7 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
 | 
			
		||||
	void Graphics::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
 | 
			
		||||
	{
 | 
			
		||||
		if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color)
 | 
			
		||||
		{
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
#if !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11))
 | 
			
		||||
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include "../internal/platform_backend.h"
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
@ -155,58 +155,58 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool GraphicsBackend::init()
 | 
			
		||||
	bool Graphics::init()
 | 
			
		||||
	{
 | 
			
		||||
		Log::info("Dummy Renderer");
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Renderer GraphicsBackend::renderer()
 | 
			
		||||
	Renderer Graphics::renderer()
 | 
			
		||||
	{
 | 
			
		||||
		return Renderer::None;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::shutdown()
 | 
			
		||||
	void Graphics::shutdown()
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const RendererFeatures& GraphicsBackend::features()
 | 
			
		||||
	const RendererFeatures& Graphics::features()
 | 
			
		||||
	{
 | 
			
		||||
		static const RendererFeatures features{ false, true, 4096 };
 | 
			
		||||
		return features;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::update() {}
 | 
			
		||||
	void GraphicsBackend::before_render() {}
 | 
			
		||||
	void GraphicsBackend::after_render() {}
 | 
			
		||||
	void Graphics::update() {}
 | 
			
		||||
	void Graphics::before_render() {}
 | 
			
		||||
	void Graphics::after_render() {}
 | 
			
		||||
 | 
			
		||||
	TextureRef GraphicsBackend::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	TextureRef Graphics::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	{
 | 
			
		||||
		return TextureRef(new Dummy_Texture(width, height, format, false));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TargetRef GraphicsBackend::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount)
 | 
			
		||||
	TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount)
 | 
			
		||||
	{
 | 
			
		||||
		return TargetRef(new Dummy_Target(width, height, attachments, attachmentCount));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ShaderRef GraphicsBackend::create_shader(const ShaderData* data)
 | 
			
		||||
	ShaderRef Graphics::create_shader(const ShaderData* data)
 | 
			
		||||
	{
 | 
			
		||||
		return ShaderRef(new Dummy_Shader(data));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MeshRef GraphicsBackend::create_mesh()
 | 
			
		||||
	MeshRef Graphics::create_mesh()
 | 
			
		||||
	{
 | 
			
		||||
		return MeshRef(new Dummy_Mesh());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::render(const RenderPass& pass)
 | 
			
		||||
	void Graphics::render(const RenderPass& pass)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::clear_backbuffer(Color color)
 | 
			
		||||
	void Graphics::clear_backbuffer(Color color)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
#ifdef BLAH_GRAPHICS_OPENGL
 | 
			
		||||
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include "../internal/platform_backend.h"
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
@ -1091,21 +1091,21 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool GraphicsBackend::init()
 | 
			
		||||
	bool Graphics::init()
 | 
			
		||||
	{
 | 
			
		||||
		gl = State();
 | 
			
		||||
 | 
			
		||||
		// create gl context
 | 
			
		||||
		gl.context = PlatformBackend::gl_context_create();
 | 
			
		||||
		gl.context = Platform::gl_context_create();
 | 
			
		||||
		if (gl.context == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Failed to create OpenGL Context");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		PlatformBackend::gl_context_make_current(gl.context);
 | 
			
		||||
		Platform::gl_context_make_current(gl.context);
 | 
			
		||||
 | 
			
		||||
		// bind opengl functions
 | 
			
		||||
		#define GL_FUNC(name, ...) gl.name = (State::name ## Func)(PlatformBackend::gl_get_func("gl" #name));
 | 
			
		||||
		#define GL_FUNC(name, ...) gl.name = (State::name ## Func)(Platform::gl_get_func("gl" #name));
 | 
			
		||||
		GL_FUNCTIONS
 | 
			
		||||
		#undef GL_FUNC
 | 
			
		||||
 | 
			
		||||
@ -1143,27 +1143,27 @@ namespace Blah
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Renderer GraphicsBackend::renderer()
 | 
			
		||||
	Renderer Graphics::renderer()
 | 
			
		||||
	{
 | 
			
		||||
		return Renderer::OpenGL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::shutdown()
 | 
			
		||||
	void Graphics::shutdown()
 | 
			
		||||
	{
 | 
			
		||||
		PlatformBackend::gl_context_destroy(gl.context);
 | 
			
		||||
		Platform::gl_context_destroy(gl.context);
 | 
			
		||||
		gl.context = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const RendererFeatures& GraphicsBackend::features()
 | 
			
		||||
	const RendererFeatures& Graphics::features()
 | 
			
		||||
	{
 | 
			
		||||
		return gl.features;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::update() {}
 | 
			
		||||
	void GraphicsBackend::before_render() {}
 | 
			
		||||
	void GraphicsBackend::after_render() {}
 | 
			
		||||
	void Graphics::update() {}
 | 
			
		||||
	void Graphics::before_render() {}
 | 
			
		||||
	void Graphics::after_render() {}
 | 
			
		||||
 | 
			
		||||
	TextureRef GraphicsBackend::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	TextureRef Graphics::create_texture(int width, int height, TextureFormat format)
 | 
			
		||||
	{
 | 
			
		||||
		auto resource = new OpenGL_Texture(width, height, format);
 | 
			
		||||
 | 
			
		||||
@ -1176,7 +1176,7 @@ namespace Blah
 | 
			
		||||
		return TextureRef(resource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TargetRef GraphicsBackend::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount)
 | 
			
		||||
	TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount)
 | 
			
		||||
	{
 | 
			
		||||
		auto resource = new OpenGL_Target(width, height, attachments, attachmentCount);
 | 
			
		||||
 | 
			
		||||
@ -1189,7 +1189,7 @@ namespace Blah
 | 
			
		||||
		return TargetRef(resource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ShaderRef GraphicsBackend::create_shader(const ShaderData* data)
 | 
			
		||||
	ShaderRef Graphics::create_shader(const ShaderData* data)
 | 
			
		||||
	{
 | 
			
		||||
		auto resource = new OpenGL_Shader(data);
 | 
			
		||||
 | 
			
		||||
@ -1202,7 +1202,7 @@ namespace Blah
 | 
			
		||||
		return ShaderRef(resource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MeshRef GraphicsBackend::create_mesh()
 | 
			
		||||
	MeshRef Graphics::create_mesh()
 | 
			
		||||
	{
 | 
			
		||||
		auto resource = new OpenGL_Mesh();
 | 
			
		||||
 | 
			
		||||
@ -1215,7 +1215,7 @@ namespace Blah
 | 
			
		||||
		return MeshRef(resource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::render(const RenderPass& pass)
 | 
			
		||||
	void Graphics::render(const RenderPass& pass)
 | 
			
		||||
	{
 | 
			
		||||
		// Bind the Target
 | 
			
		||||
		Point size;
 | 
			
		||||
@ -1476,7 +1476,7 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GraphicsBackend::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
 | 
			
		||||
	void Graphics::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
 | 
			
		||||
	{
 | 
			
		||||
		int clear = 0;
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	namespace InputBackend
 | 
			
		||||
	namespace Input
 | 
			
		||||
	{
 | 
			
		||||
		// Initializes the Input State
 | 
			
		||||
		void init();
 | 
			
		||||
@ -7,12 +7,9 @@
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	struct Config;
 | 
			
		||||
	enum class FileMode;
 | 
			
		||||
 | 
			
		||||
	namespace PlatformBackend
 | 
			
		||||
	namespace Platform
 | 
			
		||||
	{
 | 
			
		||||
		typedef void* FileHandle;
 | 
			
		||||
 | 
			
		||||
		// Initialize the System
 | 
			
		||||
		bool init(const Config& config);
 | 
			
		||||
 | 
			
		||||
@ -1,755 +0,0 @@
 | 
			
		||||
#ifdef BLAH_PLATFORM_SDL2
 | 
			
		||||
 | 
			
		||||
#include "../internal/platform_backend.h"
 | 
			
		||||
#include "../internal/input_backend.h"
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include <blah/input.h>
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include <blah/filesystem.h>
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include <blah/time.h>
 | 
			
		||||
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
 | 
			
		||||
#if _WIN32
 | 
			
		||||
#include <SDL_syswm.h>
 | 
			
		||||
// on Windows we're using the C++ <filesystem> API for now
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <winuser.h>	// for SetProcessDPIAware
 | 
			
		||||
#include <filesystem>	// for File Reading/Writing
 | 
			
		||||
#include <shellapi.h>	// for file explore
 | 
			
		||||
namespace fs = std::filesystem;
 | 
			
		||||
#else
 | 
			
		||||
// on non-Windows we use POSIX standard file system stuff
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	SDL_Window* window = nullptr;
 | 
			
		||||
	SDL_Joystick* joysticks[Blah::Input::max_controllers];
 | 
			
		||||
	SDL_GameController* gamepads[Blah::Input::max_controllers];
 | 
			
		||||
	char* basePath = nullptr;
 | 
			
		||||
	char* userPath = nullptr;
 | 
			
		||||
	bool displayed = false;
 | 
			
		||||
 | 
			
		||||
	void sdl_log(void* userdata, int category, SDL_LogPriority priority, const char* message)
 | 
			
		||||
	{
 | 
			
		||||
		if (priority <= SDL_LOG_PRIORITY_INFO)
 | 
			
		||||
			Log::info(message);
 | 
			
		||||
		else if (priority <= SDL_LOG_PRIORITY_WARN)
 | 
			
		||||
			Log::warn(message);
 | 
			
		||||
		else
 | 
			
		||||
			Log::error(message);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int find_joystick_index(SDL_JoystickID instance_id)
 | 
			
		||||
	{
 | 
			
		||||
		for (int i = 0; i < Blah::Input::max_controllers; i++)
 | 
			
		||||
			if (joysticks[i] != nullptr && SDL_JoystickInstanceID(joysticks[i]) == instance_id)
 | 
			
		||||
				return i;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int find_gamepad_index(SDL_JoystickID instance_id)
 | 
			
		||||
	{
 | 
			
		||||
		for (int i = 0; i < Blah::Input::max_controllers; i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (gamepads[i] != nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				auto joystick = SDL_GameControllerGetJoystick(gamepads[i]);
 | 
			
		||||
				if (SDL_JoystickInstanceID(joystick) == instance_id)
 | 
			
		||||
					return i;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Custom File class
 | 
			
		||||
	class Blah_SDL2_File : public File
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
		SDL_RWops* m_handle;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		Blah_SDL2_File(SDL_RWops* handle)
 | 
			
		||||
		{
 | 
			
		||||
			m_handle = handle;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~Blah_SDL2_File()
 | 
			
		||||
		{
 | 
			
		||||
			if (m_handle)
 | 
			
		||||
				SDL_RWclose(m_handle);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t length() override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWsize(m_handle);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t position() override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWtell(m_handle);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t seek(size_t position) override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWseek(m_handle, position, RW_SEEK_SET);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t read(unsigned char* buffer, size_t length) override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWread(m_handle, buffer, sizeof(char), length);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t write(const unsigned char* buffer, size_t length) override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWwrite(m_handle, buffer, sizeof(char), length);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::init(const Config& config)
 | 
			
		||||
	{
 | 
			
		||||
		// Required to call this for Windows
 | 
			
		||||
		// I'm not sure why SDL2 doesn't do this on Windows automatically?
 | 
			
		||||
	#if _WIN32
 | 
			
		||||
		SetProcessDPIAware();
 | 
			
		||||
	#endif
 | 
			
		||||
 | 
			
		||||
		// TODO:
 | 
			
		||||
		// control this via some kind of config flag
 | 
			
		||||
		SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);
 | 
			
		||||
		SDL_LogSetOutputFunction(sdl_log, nullptr);
 | 
			
		||||
 | 
			
		||||
		// Get SDL version
 | 
			
		||||
		SDL_version version;
 | 
			
		||||
		SDL_GetVersion(&version);
 | 
			
		||||
		Log::info("SDL v%i.%i.%i", version.major, version.minor, version.patch);
 | 
			
		||||
 | 
			
		||||
		// initialize SDL
 | 
			
		||||
		if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Failed to initialize SDL2");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
 | 
			
		||||
 | 
			
		||||
		// enable OpenGL
 | 
			
		||||
		if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
		{
 | 
			
		||||
			flags |= SDL_WINDOW_OPENGL;
 | 
			
		||||
 | 
			
		||||
	#ifdef __EMSCRIPTEN__
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
 | 
			
		||||
	#else
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 | 
			
		||||
 | 
			
		||||
			// TODO:
 | 
			
		||||
			// This should be controlled via the gfx api somehow?
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
 | 
			
		||||
			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
 | 
			
		||||
	#endif
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// create the window
 | 
			
		||||
		window = SDL_CreateWindow(config.name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, config.width, config.height, flags);
 | 
			
		||||
		if (window == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Failed to create a Window");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Scale Window to monitor for High DPI displays
 | 
			
		||||
		// Other platforms do this automatically ... Windows we need to explitely do so
 | 
			
		||||
	#if _WIN32
 | 
			
		||||
		{
 | 
			
		||||
			// find the display index
 | 
			
		||||
			int display = SDL_GetWindowDisplayIndex(window);
 | 
			
		||||
			float ddpi, hdpi, vdpi;
 | 
			
		||||
			if (SDL_GetDisplayDPI(display, &ddpi, &hdpi, &vdpi) == 0)
 | 
			
		||||
			{
 | 
			
		||||
				// scale the window up basesd on the display DPI
 | 
			
		||||
				float hidpiRes = 96;
 | 
			
		||||
				float dpi = (ddpi / hidpiRes);
 | 
			
		||||
				if (dpi != 1)
 | 
			
		||||
				{
 | 
			
		||||
					SDL_DisplayMode mode;
 | 
			
		||||
					SDL_GetDesktopDisplayMode(display, &mode);
 | 
			
		||||
					SDL_SetWindowPosition(window, (int)(mode.w - config.width * dpi) / 2, (int)(mode.h - config.height * dpi) / 2);
 | 
			
		||||
					SDL_SetWindowSize(window, (int)(config.width * dpi), (int)(config.height * dpi));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	#endif
 | 
			
		||||
 | 
			
		||||
		// set window properties
 | 
			
		||||
		SDL_SetWindowResizable(window, SDL_TRUE);
 | 
			
		||||
		SDL_SetWindowMinimumSize(window, 256, 256);
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::ready()
 | 
			
		||||
	{
 | 
			
		||||
	#ifndef __EMSCRIPTEN__
 | 
			
		||||
		// enable V-Sync
 | 
			
		||||
		// TODO:
 | 
			
		||||
		// This should be a toggle or controllable in some way
 | 
			
		||||
		if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
			SDL_GL_SetSwapInterval(1);
 | 
			
		||||
	#endif
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::shutdown()
 | 
			
		||||
	{
 | 
			
		||||
		if (window != nullptr)
 | 
			
		||||
			SDL_DestroyWindow(window);
 | 
			
		||||
		window = nullptr;
 | 
			
		||||
		displayed = false;
 | 
			
		||||
 | 
			
		||||
		if (basePath != nullptr)
 | 
			
		||||
			SDL_free(basePath);
 | 
			
		||||
 | 
			
		||||
		if (userPath != nullptr)
 | 
			
		||||
			SDL_free(userPath);
 | 
			
		||||
 | 
			
		||||
		SDL_Quit();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u64 PlatformBackend::ticks()
 | 
			
		||||
	{
 | 
			
		||||
		auto counter = SDL_GetPerformanceCounter();
 | 
			
		||||
		auto per_second = (double)SDL_GetPerformanceFrequency();
 | 
			
		||||
		return (u64)(counter * (Time::ticks_per_second / per_second));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Macro defined by X11 conflicts with MouseButton enum
 | 
			
		||||
	#undef None
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::update(InputState& state)
 | 
			
		||||
	{
 | 
			
		||||
		// update the mouse every frame
 | 
			
		||||
		{
 | 
			
		||||
			int win_x, win_y, x, y;
 | 
			
		||||
 | 
			
		||||
			SDL_GetWindowPosition(window, &win_x, &win_y);
 | 
			
		||||
			SDL_GetGlobalMouseState(&x, &y);
 | 
			
		||||
 | 
			
		||||
			state.mouse.on_move(
 | 
			
		||||
				Vec2((float)(x - win_x), (float)(y - win_y)),
 | 
			
		||||
				Vec2((float)x, (float)y));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// poll normal events
 | 
			
		||||
		SDL_Event event;
 | 
			
		||||
		while (SDL_PollEvent(&event))
 | 
			
		||||
		{
 | 
			
		||||
			if (event.type == SDL_QUIT)
 | 
			
		||||
			{
 | 
			
		||||
				auto& config = App::config();
 | 
			
		||||
				if (config.on_exit_request != nullptr)
 | 
			
		||||
					config.on_exit_request();
 | 
			
		||||
			}
 | 
			
		||||
			// Mouse
 | 
			
		||||
			else if (event.type == SDL_MOUSEBUTTONDOWN)
 | 
			
		||||
			{
 | 
			
		||||
				MouseButton btn = MouseButton::None;
 | 
			
		||||
				if (event.button.button == SDL_BUTTON_LEFT)
 | 
			
		||||
					btn = MouseButton::Left;
 | 
			
		||||
				else if (event.button.button == SDL_BUTTON_RIGHT)
 | 
			
		||||
					btn = MouseButton::Right;
 | 
			
		||||
				else if (event.button.button == SDL_BUTTON_MIDDLE)
 | 
			
		||||
					btn = MouseButton::Middle;
 | 
			
		||||
 | 
			
		||||
				state.mouse.on_press(btn);
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_MOUSEBUTTONUP)
 | 
			
		||||
			{
 | 
			
		||||
				MouseButton btn = MouseButton::None;
 | 
			
		||||
				if (event.button.button == SDL_BUTTON_LEFT)
 | 
			
		||||
					btn = MouseButton::Left;
 | 
			
		||||
				else if (event.button.button == SDL_BUTTON_RIGHT)
 | 
			
		||||
					btn = MouseButton::Right;
 | 
			
		||||
				else if (event.button.button == SDL_BUTTON_MIDDLE)
 | 
			
		||||
					btn = MouseButton::Middle;
 | 
			
		||||
 | 
			
		||||
				state.mouse.on_release(btn);
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_MOUSEWHEEL)
 | 
			
		||||
			{
 | 
			
		||||
				state.mouse.wheel = Point(event.wheel.x, event.wheel.y);
 | 
			
		||||
			}
 | 
			
		||||
			// Keyboard
 | 
			
		||||
			else if (event.type == SDL_KEYDOWN)
 | 
			
		||||
			{
 | 
			
		||||
				if (event.key.repeat == 0)
 | 
			
		||||
					state.keyboard.on_press((Key)event.key.keysym.scancode);
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_KEYUP)
 | 
			
		||||
			{
 | 
			
		||||
				if (event.key.repeat == 0)
 | 
			
		||||
					state.keyboard.on_release((Key)event.key.keysym.scancode);
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_TEXTINPUT)
 | 
			
		||||
			{
 | 
			
		||||
				state.keyboard.text += event.text.text;
 | 
			
		||||
			}
 | 
			
		||||
			// Joystick Controller
 | 
			
		||||
			else if (event.type == SDL_JOYDEVICEADDED)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = event.jdevice.which;
 | 
			
		||||
 | 
			
		||||
				if (SDL_IsGameController(index) == SDL_FALSE && index >= 0 && index < Input::max_controllers)
 | 
			
		||||
				{
 | 
			
		||||
					auto ptr = joysticks[index] = SDL_JoystickOpen(index);
 | 
			
		||||
					auto name = SDL_JoystickName(ptr);
 | 
			
		||||
					auto button_count = SDL_JoystickNumButtons(ptr);
 | 
			
		||||
					auto axis_count = SDL_JoystickNumAxes(ptr);
 | 
			
		||||
					auto vendor = SDL_JoystickGetVendor(ptr);
 | 
			
		||||
					auto product = SDL_JoystickGetProduct(ptr);
 | 
			
		||||
					auto version = SDL_JoystickGetProductVersion(ptr);
 | 
			
		||||
 | 
			
		||||
					state.controllers[index].on_connect(name, 0, button_count, axis_count, vendor, product, version);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_JOYDEVICEREMOVED)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = find_joystick_index(event.jdevice.which);
 | 
			
		||||
				if (index >= 0)
 | 
			
		||||
				{
 | 
			
		||||
					if (SDL_IsGameController(index) == SDL_FALSE)
 | 
			
		||||
					{
 | 
			
		||||
						state.controllers[index].on_disconnect();
 | 
			
		||||
						SDL_JoystickClose(joysticks[index]);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_JOYBUTTONDOWN)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = find_joystick_index(event.jdevice.which);
 | 
			
		||||
				if (index >= 0)
 | 
			
		||||
				{
 | 
			
		||||
					if (SDL_IsGameController(index) == SDL_FALSE)
 | 
			
		||||
						state.controllers[index].on_press((Button)event.jbutton.button);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_JOYBUTTONUP)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = find_joystick_index(event.jdevice.which);
 | 
			
		||||
				if (index >= 0)
 | 
			
		||||
				{
 | 
			
		||||
					if (SDL_IsGameController(index) == SDL_FALSE)
 | 
			
		||||
						state.controllers[index].on_release((Button)event.jbutton.button);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_JOYAXISMOTION)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = find_joystick_index(event.jdevice.which);
 | 
			
		||||
				if (index >= 0)
 | 
			
		||||
				{
 | 
			
		||||
					if (SDL_IsGameController(index) == SDL_FALSE)
 | 
			
		||||
					{
 | 
			
		||||
						float value;
 | 
			
		||||
						if (event.jaxis.value >= 0)
 | 
			
		||||
							value = event.jaxis.value / 32767.0f;
 | 
			
		||||
						else
 | 
			
		||||
							value = event.jaxis.value / 32768.0f;
 | 
			
		||||
						state.controllers[index].on_axis((Axis)event.jaxis.axis, value);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			// Gamepad Controller
 | 
			
		||||
			else if (event.type == SDL_CONTROLLERDEVICEADDED)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = event.cdevice.which;
 | 
			
		||||
				if (index >= 0 && index < Input::max_controllers)
 | 
			
		||||
				{
 | 
			
		||||
					auto ptr = gamepads[index] = SDL_GameControllerOpen(index);
 | 
			
		||||
					auto name = SDL_GameControllerName(ptr);
 | 
			
		||||
					auto vendor = SDL_GameControllerGetVendor(ptr);
 | 
			
		||||
					auto product = SDL_GameControllerGetProduct(ptr);
 | 
			
		||||
					auto version = SDL_GameControllerGetProductVersion(ptr);
 | 
			
		||||
 | 
			
		||||
					state.controllers[index].on_connect(name, 1, 15, 6, vendor, product, version);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_CONTROLLERDEVICEREMOVED)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = find_gamepad_index(event.cdevice.which);
 | 
			
		||||
				if (index >= 0)
 | 
			
		||||
				{
 | 
			
		||||
					state.controllers[index].on_disconnect();
 | 
			
		||||
					SDL_GameControllerClose(gamepads[index]);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_CONTROLLERBUTTONDOWN)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = find_gamepad_index(event.cdevice.which);
 | 
			
		||||
				if (index >= 0)
 | 
			
		||||
				{
 | 
			
		||||
					Button button = Button::None;
 | 
			
		||||
					if (event.cbutton.button >= 0 && event.cbutton.button < 15)
 | 
			
		||||
						button = (Button)event.cbutton.button; // NOTE: These map directly to Engine Buttons enum!
 | 
			
		||||
 | 
			
		||||
					state.controllers[index].on_press(button);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_CONTROLLERBUTTONUP)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = find_gamepad_index(event.cdevice.which);
 | 
			
		||||
				if (index >= 0)
 | 
			
		||||
				{
 | 
			
		||||
					Button button = Button::None;
 | 
			
		||||
					if (event.cbutton.button >= 0 && event.cbutton.button < 15)
 | 
			
		||||
						button = (Button)event.cbutton.button; // NOTE: These map directly to Engine Buttons enum!
 | 
			
		||||
 | 
			
		||||
					state.controllers[index].on_release(button);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if (event.type == SDL_CONTROLLERAXISMOTION)
 | 
			
		||||
			{
 | 
			
		||||
				auto index = find_gamepad_index(event.cdevice.which);
 | 
			
		||||
				if (index >= 0)
 | 
			
		||||
				{
 | 
			
		||||
					Axis axis = Axis::None;
 | 
			
		||||
					if (event.caxis.axis >= 0 && event.caxis.axis < 6)
 | 
			
		||||
						axis = (Axis)event.caxis.axis; // NOTE: These map directly to Engine Axis enum!
 | 
			
		||||
 | 
			
		||||
					float value;
 | 
			
		||||
					if (event.caxis.value >= 0)
 | 
			
		||||
						value = event.caxis.value / 32767.0f;
 | 
			
		||||
					else
 | 
			
		||||
						value = event.caxis.value / 32768.0f;
 | 
			
		||||
 | 
			
		||||
					state.controllers[index].on_axis(axis, value);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::sleep(int milliseconds)
 | 
			
		||||
	{
 | 
			
		||||
		if (milliseconds >= 0)
 | 
			
		||||
			SDL_Delay((u32)milliseconds);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::present()
 | 
			
		||||
	{
 | 
			
		||||
		if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
		{
 | 
			
		||||
			SDL_GL_SwapWindow(window);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// display the window
 | 
			
		||||
		// this avoids a short black screen on macoS
 | 
			
		||||
		if (!displayed)
 | 
			
		||||
		{
 | 
			
		||||
			SDL_ShowWindow(window);
 | 
			
		||||
			displayed = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* PlatformBackend::get_title()
 | 
			
		||||
	{
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::set_title(const char* title)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_SetWindowTitle(window, title);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::get_position(int* x, int* y)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_GetWindowPosition(window, x, y);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::set_position(int x, int y)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_SetWindowPosition(window, x, y);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::set_fullscreen(bool enabled)
 | 
			
		||||
	{
 | 
			
		||||
		if (enabled)
 | 
			
		||||
			SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
 | 
			
		||||
		else
 | 
			
		||||
			SDL_SetWindowFullscreen(window, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::get_size(int* width, int* height)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_GetWindowSize(window, width, height);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::set_size(int width, int height)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_SetWindowSize(window, width, height);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::get_draw_size(int* width, int* height)
 | 
			
		||||
	{
 | 
			
		||||
		if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
		{
 | 
			
		||||
			SDL_GL_GetDrawableSize(window, width, height);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			SDL_GetWindowSize(window, width, height);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	float PlatformBackend::get_content_scale()
 | 
			
		||||
	{
 | 
			
		||||
		// TODO:
 | 
			
		||||
		// This is incorrect! but for some reason the scale
 | 
			
		||||
		// is HUGE if I use the Display DPI on macOS :/
 | 
			
		||||
	#if __APPLE__
 | 
			
		||||
		return 2.0f;
 | 
			
		||||
	#endif
 | 
			
		||||
 | 
			
		||||
	#if _WIN32
 | 
			
		||||
		float hidpiRes = 96;
 | 
			
		||||
	#else
 | 
			
		||||
		float hidpiRes = 72;
 | 
			
		||||
	#endif
 | 
			
		||||
 | 
			
		||||
		int index = SDL_GetWindowDisplayIndex(window);
 | 
			
		||||
		if (index < 0)
 | 
			
		||||
			Log::error(SDL_GetError());
 | 
			
		||||
 | 
			
		||||
		float ddpi, x, y;
 | 
			
		||||
		if (SDL_GetDisplayDPI(index, &ddpi, &x, &y) != 0)
 | 
			
		||||
			Log::error(SDL_GetError());
 | 
			
		||||
 | 
			
		||||
		return (ddpi / hidpiRes);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// FILE IO
 | 
			
		||||
 | 
			
		||||
	const char* PlatformBackend::app_path()
 | 
			
		||||
	{
 | 
			
		||||
		if (basePath == nullptr)
 | 
			
		||||
			basePath = SDL_GetBasePath();
 | 
			
		||||
		return basePath;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* PlatformBackend::user_path()
 | 
			
		||||
	{
 | 
			
		||||
		if (userPath == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			auto& config = App::config();
 | 
			
		||||
			userPath = SDL_GetPrefPath(nullptr, config.name);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return userPath;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Windows File System methods
 | 
			
		||||
	#if _WIN32
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::file_exists(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		return fs::is_regular_file(path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::file_delete(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		return fs::remove(path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::dir_create(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		std::error_code error;
 | 
			
		||||
		return fs::create_directories(path, error);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::dir_exists(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		return fs::is_directory(path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::dir_delete(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		BLAH_ASSERT(false, "not implemented");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::dir_enumerate(Vector<FilePath>& list, const char* path, bool recursive)
 | 
			
		||||
	{
 | 
			
		||||
		if (fs::is_directory(path))
 | 
			
		||||
		{
 | 
			
		||||
			if (recursive)
 | 
			
		||||
			{
 | 
			
		||||
				for (auto& p : fs::recursive_directory_iterator(path))
 | 
			
		||||
					list.emplace_back(p.path().string().c_str());
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				for (auto& p : fs::directory_iterator(path))
 | 
			
		||||
					list.emplace_back(p.path().string().c_str());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::dir_explore(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Non-Windows File System Methods
 | 
			
		||||
	#else
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::file_exists(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		struct stat buffer;
 | 
			
		||||
		return (stat(path, &buffer) == 0) && S_ISREG(buffer.st_mode);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::file_delete(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		BLAH_ASSERT(false, "not implemented");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::dir_create(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		char tmp[265];
 | 
			
		||||
		char* p = NULL;
 | 
			
		||||
		size_t len;
 | 
			
		||||
 | 
			
		||||
		snprintf(tmp, sizeof(tmp), "%s", path);
 | 
			
		||||
		len = strlen(tmp);
 | 
			
		||||
		if (tmp[len - 1] == '/')
 | 
			
		||||
			tmp[len - 1] = 0;
 | 
			
		||||
		for (p = tmp + 1; *p; p++)
 | 
			
		||||
			if (*p == '/') {
 | 
			
		||||
				*p = 0;
 | 
			
		||||
				mkdir(tmp, S_IRWXU);
 | 
			
		||||
				*p = '/';
 | 
			
		||||
			}
 | 
			
		||||
		return mkdir(tmp, S_IRWXU) == 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::dir_exists(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		struct stat buffer;
 | 
			
		||||
		return (stat(path, &buffer) == 0) && S_ISDIR(buffer.st_mode);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::dir_delete(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		BLAH_ASSERT(false, "not implemented");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::dir_enumerate(Vector<FilePath>& list, const char* path, bool recursive)
 | 
			
		||||
	{
 | 
			
		||||
		DIR* dirp = opendir(path);
 | 
			
		||||
		if (dirp != NULL)
 | 
			
		||||
		{
 | 
			
		||||
			struct dirent* dp;
 | 
			
		||||
			while ((dp = readdir(dirp)) != NULL)
 | 
			
		||||
			{
 | 
			
		||||
				if (dp->d_name[0] == '.')
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				FilePath subpath = FilePath(path);
 | 
			
		||||
				if (subpath.end()[-1] != '/') subpath = subpath.append("/");
 | 
			
		||||
				subpath = subpath.append(dp->d_name);
 | 
			
		||||
				list.push_back(subpath);
 | 
			
		||||
 | 
			
		||||
				if (recursive && dp->d_type == DT_DIR)
 | 
			
		||||
					dir_enumerate(list, subpath + "/", true);
 | 
			
		||||
			}
 | 
			
		||||
			closedir(dirp);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::dir_explore(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		BLAH_ASSERT(false, "'dir_explore' Not Implemented");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#endif
 | 
			
		||||
 | 
			
		||||
	FileRef PlatformBackend::file_open(const char* path, FileMode mode)
 | 
			
		||||
	{
 | 
			
		||||
		const char* sdl_mode = "";
 | 
			
		||||
 | 
			
		||||
		switch (mode)
 | 
			
		||||
		{
 | 
			
		||||
		case FileMode::OpenRead:
 | 
			
		||||
			sdl_mode = "rb";
 | 
			
		||||
			break;
 | 
			
		||||
		case FileMode::Open:
 | 
			
		||||
			sdl_mode = "r+b";
 | 
			
		||||
			break;
 | 
			
		||||
		case FileMode::CreateWrite:
 | 
			
		||||
			sdl_mode = "wb";
 | 
			
		||||
			break;
 | 
			
		||||
		case FileMode::Create:
 | 
			
		||||
			sdl_mode = "w+b";
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto ptr = SDL_RWFromFile(path, sdl_mode);
 | 
			
		||||
		if (!ptr)
 | 
			
		||||
			return FileRef();
 | 
			
		||||
 | 
			
		||||
		return FileRef(new Blah_SDL2_File(ptr));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void* PlatformBackend::gl_get_func(const char* name)
 | 
			
		||||
	{
 | 
			
		||||
		return SDL_GL_GetProcAddress(name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void* PlatformBackend::gl_context_create()
 | 
			
		||||
	{
 | 
			
		||||
		void* pointer = SDL_GL_CreateContext(window);
 | 
			
		||||
		if (pointer == nullptr)
 | 
			
		||||
			Log::error("SDL_GL_CreateContext failed: %s", SDL_GetError());
 | 
			
		||||
		return pointer;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::gl_context_make_current(void* context)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_GL_MakeCurrent(window, context);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::gl_context_destroy(void* context)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_GL_DeleteContext(context);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void* PlatformBackend::d3d11_get_hwnd()
 | 
			
		||||
	{
 | 
			
		||||
	#if _WIN32
 | 
			
		||||
		SDL_SysWMinfo info;
 | 
			
		||||
		SDL_VERSION(&info.version);
 | 
			
		||||
		SDL_GetWindowWMInfo(window, &info);
 | 
			
		||||
		return info.info.win.window;
 | 
			
		||||
	#else
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	#endif
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // BLAH_PLATFORM_SDL2
 | 
			
		||||
@ -1,858 +0,0 @@
 | 
			
		||||
#ifdef BLAH_PLATFORM_WIN32
 | 
			
		||||
 | 
			
		||||
// Note:
 | 
			
		||||
// This backend implementation is unfinished! 
 | 
			
		||||
// It's missing a few things, namely:
 | 
			
		||||
// - Controller Support
 | 
			
		||||
// (And error testing)
 | 
			
		||||
 | 
			
		||||
#include "../internal/platform_backend.h"
 | 
			
		||||
#include "../internal/input_backend.h"
 | 
			
		||||
#include "../internal/graphics_backend.h"
 | 
			
		||||
#include <blah/input.h>
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include <blah/filesystem.h>
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include <blah/time.h>
 | 
			
		||||
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <winuser.h>    // for SetProcessDPIAware
 | 
			
		||||
#include <filesystem>   // for File Reading/Writing
 | 
			
		||||
#include <shellapi.h>   // for file explore
 | 
			
		||||
#include <shlobj.h>     // for known folder
 | 
			
		||||
#include <chrono>       // for ticks method
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
namespace fs = std::filesystem;
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	// Primary Window
 | 
			
		||||
	HWND g_hwnd;
 | 
			
		||||
 | 
			
		||||
	// Working Directories
 | 
			
		||||
	FilePath g_working_directory;
 | 
			
		||||
	FilePath g_user_directory;
 | 
			
		||||
 | 
			
		||||
	// Timestamp for calculating ticks
 | 
			
		||||
	std::chrono::system_clock::duration g_start_time;
 | 
			
		||||
 | 
			
		||||
	// OpenGL Methods
 | 
			
		||||
	// These are only loaded if built using the OpenGL Backend
 | 
			
		||||
	HMODULE g_opengl_dll;
 | 
			
		||||
	void* (WINAPI* g_wglGetProcAddress) (const char* proc);
 | 
			
		||||
	HGLRC(WINAPI* g_wglCreateContext) (HDC hdc);
 | 
			
		||||
	BOOL(WINAPI* g_wglDeleteContext) (HGLRC hglrc);
 | 
			
		||||
	BOOL(WINAPI* g_wglMakeCurrent) (HDC hdc, HGLRC hglrc);
 | 
			
		||||
 | 
			
		||||
	// fullscreen state
 | 
			
		||||
	RECT g_windowed_position;
 | 
			
		||||
	bool g_fullscreen = false;
 | 
			
		||||
 | 
			
		||||
	// current input state
 | 
			
		||||
	InputState* g_input_state = nullptr;
 | 
			
		||||
 | 
			
		||||
	// Converts Windows scancode to Blah key
 | 
			
		||||
	Key scancode_to_key(WPARAM wParam, LPARAM lParam);
 | 
			
		||||
 | 
			
		||||
	// Main Windows Procedure callback
 | 
			
		||||
	LRESULT CALLBACK window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 | 
			
		||||
 | 
			
		||||
	// Custom File class
 | 
			
		||||
	class Blah_Win32_File : public File
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
		HANDLE m_handle;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		Blah_Win32_File(HANDLE handle)
 | 
			
		||||
		{
 | 
			
		||||
			m_handle = handle;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~Blah_Win32_File()
 | 
			
		||||
		{
 | 
			
		||||
			CloseHandle(m_handle);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t length() override
 | 
			
		||||
		{
 | 
			
		||||
			// Todo: cache this value? not sure how performant it is
 | 
			
		||||
 | 
			
		||||
			LARGE_INTEGER file_size;
 | 
			
		||||
			if (GetFileSizeEx(m_handle, &file_size))
 | 
			
		||||
				return file_size.QuadPart;
 | 
			
		||||
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t position() override
 | 
			
		||||
		{
 | 
			
		||||
			LARGE_INTEGER move;
 | 
			
		||||
			LARGE_INTEGER result;
 | 
			
		||||
 | 
			
		||||
			move.QuadPart = 0;
 | 
			
		||||
			result.QuadPart = 0;
 | 
			
		||||
 | 
			
		||||
			SetFilePointerEx(m_handle, move, &result, FILE_CURRENT);
 | 
			
		||||
 | 
			
		||||
			return result.QuadPart;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t seek(size_t position) override
 | 
			
		||||
		{
 | 
			
		||||
			LARGE_INTEGER move;
 | 
			
		||||
			LARGE_INTEGER result;
 | 
			
		||||
 | 
			
		||||
			move.QuadPart = position;
 | 
			
		||||
			result.QuadPart = 0;
 | 
			
		||||
 | 
			
		||||
			SetFilePointerEx(m_handle, move, &result, FILE_BEGIN);
 | 
			
		||||
 | 
			
		||||
			return result.QuadPart;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t read(unsigned char* buffer, size_t length) override
 | 
			
		||||
		{
 | 
			
		||||
			static const DWORD read_step = 65536;
 | 
			
		||||
 | 
			
		||||
			size_t read = 0;
 | 
			
		||||
 | 
			
		||||
			while (read < length)
 | 
			
		||||
			{
 | 
			
		||||
				DWORD to_read = read_step;
 | 
			
		||||
				if (to_read > length - read)
 | 
			
		||||
					to_read = (DWORD)(length - read);
 | 
			
		||||
 | 
			
		||||
				DWORD moved = 0;
 | 
			
		||||
				if (ReadFile(m_handle, buffer + read, to_read, &moved, NULL))
 | 
			
		||||
					read += moved;
 | 
			
		||||
 | 
			
		||||
				if (moved < to_read)
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return read;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t write(const unsigned char* buffer, size_t length) override
 | 
			
		||||
		{
 | 
			
		||||
			static const DWORD write_step = 65536;
 | 
			
		||||
 | 
			
		||||
			size_t written = 0;
 | 
			
		||||
 | 
			
		||||
			while (written < length)
 | 
			
		||||
			{
 | 
			
		||||
				DWORD to_write = write_step;
 | 
			
		||||
				if (to_write > length - written)
 | 
			
		||||
					to_write = (DWORD)(length - written);
 | 
			
		||||
 | 
			
		||||
				DWORD moved = 0;
 | 
			
		||||
				if (WriteFile(m_handle, buffer + written, to_write, &moved, NULL))
 | 
			
		||||
					written += moved;
 | 
			
		||||
 | 
			
		||||
				if (moved < to_write)
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return written;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::init(const Config& config)
 | 
			
		||||
	{
 | 
			
		||||
		// Required to call this for Windows
 | 
			
		||||
		SetProcessDPIAware();
 | 
			
		||||
 | 
			
		||||
		// Get the hInstance
 | 
			
		||||
		HINSTANCE hInstance = GetModuleHandle(NULL);
 | 
			
		||||
 | 
			
		||||
		// Create the Window Class
 | 
			
		||||
		WNDCLASS wc = {};
 | 
			
		||||
		wc.lpfnWndProc = DefWindowProc;
 | 
			
		||||
		wc.lpszClassName = "BLAH WINDOW";
 | 
			
		||||
		wc.hInstance = hInstance;
 | 
			
		||||
		wc.lpfnWndProc = window_procedure;
 | 
			
		||||
		wc.hCursor = LoadCursor(NULL, IDC_ARROW);
 | 
			
		||||
		wc.hIcon = NULL;
 | 
			
		||||
		wc.lpszMenuName = NULL;
 | 
			
		||||
		wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
 | 
			
		||||
		wc.cbClsExtra = 0;
 | 
			
		||||
		wc.cbWndExtra = 0;
 | 
			
		||||
 | 
			
		||||
		// Register the Window class
 | 
			
		||||
		RegisterClass(&wc);
 | 
			
		||||
 | 
			
		||||
		// Create the Window Instance
 | 
			
		||||
		g_hwnd = CreateWindow("BLAH WINDOW", config.name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 640, 480, NULL, NULL, hInstance, NULL);
 | 
			
		||||
 | 
			
		||||
		// Failed to create the Window
 | 
			
		||||
		if (g_hwnd == NULL)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("Window Creation Failed");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Create the OpenGL device info
 | 
			
		||||
		if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
		{
 | 
			
		||||
			// Load the DLL
 | 
			
		||||
			g_opengl_dll = LoadLibraryA("opengl32.dll");
 | 
			
		||||
			if (g_opengl_dll == NULL)
 | 
			
		||||
			{
 | 
			
		||||
				Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll");
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Get the Windows GL functions we need
 | 
			
		||||
			g_wglGetProcAddress = (void* (WINAPI*)(const char*))GetProcAddress(g_opengl_dll, "wglGetProcAddress");
 | 
			
		||||
			g_wglCreateContext = (HGLRC(WINAPI*) (HDC))GetProcAddress(g_opengl_dll, "wglCreateContext");
 | 
			
		||||
			g_wglDeleteContext = (BOOL(WINAPI*) (HGLRC))GetProcAddress(g_opengl_dll, "wglDeleteContext");
 | 
			
		||||
			g_wglMakeCurrent = (BOOL(WINAPI*) (HDC, HGLRC))GetProcAddress(g_opengl_dll, "wglMakeCurrent");
 | 
			
		||||
 | 
			
		||||
			// TODO:
 | 
			
		||||
			// Allow the user to apply (some of) these values before instantiation.
 | 
			
		||||
			// Also applies to the SDL2 Backend
 | 
			
		||||
 | 
			
		||||
			PIXELFORMATDESCRIPTOR pfd =
 | 
			
		||||
			{
 | 
			
		||||
				sizeof(PIXELFORMATDESCRIPTOR),   // size of this pfd  
 | 
			
		||||
				1,                     // version number  
 | 
			
		||||
				PFD_DRAW_TO_WINDOW |   // support window  
 | 
			
		||||
				PFD_SUPPORT_OPENGL |   // support OpenGL  
 | 
			
		||||
				PFD_DOUBLEBUFFER,      // double buffered  
 | 
			
		||||
				PFD_TYPE_RGBA,         // RGBA type  
 | 
			
		||||
				32,                    // 32-bit color depth  
 | 
			
		||||
				0, 0, 0, 0, 0, 0,      // color bits ignored  
 | 
			
		||||
				0,                     // no alpha buffer  
 | 
			
		||||
				0,                     // shift bit ignored  
 | 
			
		||||
				0,                     // no accumulation buffer  
 | 
			
		||||
				0, 0, 0, 0,            // accum bits ignored  
 | 
			
		||||
				24,                    // 24-bit z-buffer  
 | 
			
		||||
				8,                     // 8-bit stencil buffer  
 | 
			
		||||
				0,                     // no auxiliary buffer  
 | 
			
		||||
				PFD_MAIN_PLANE,        // main layer  
 | 
			
		||||
				0,                     // reserved  
 | 
			
		||||
				0, 0, 0                // layer masks ignored  
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			HDC hdc = GetDC(g_hwnd);
 | 
			
		||||
 | 
			
		||||
			// get the best available match of pixel format for the device context   
 | 
			
		||||
			int pixel_format = ChoosePixelFormat(hdc, &pfd);
 | 
			
		||||
 | 
			
		||||
			// make that the pixel format of the device context  
 | 
			
		||||
			SetPixelFormat(hdc, pixel_format, &pfd);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Reset our game timer
 | 
			
		||||
		g_start_time = std::chrono::system_clock::now().time_since_epoch();
 | 
			
		||||
 | 
			
		||||
		// Get Working Directory
 | 
			
		||||
		{
 | 
			
		||||
			TCHAR buffer[MAX_PATH];
 | 
			
		||||
			GetModuleFileName(NULL, buffer, MAX_PATH);
 | 
			
		||||
 | 
			
		||||
			auto normalized = Path::normalize(buffer);
 | 
			
		||||
			auto end = normalized.last_index_of('/');;
 | 
			
		||||
			if (end >= 0)
 | 
			
		||||
				g_working_directory = FilePath(normalized.begin(), normalized.begin() + end);
 | 
			
		||||
			else
 | 
			
		||||
				g_working_directory = normalized;
 | 
			
		||||
			g_working_directory.append("/");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Get Application User Directory
 | 
			
		||||
		{
 | 
			
		||||
			PWSTR path = NULL;
 | 
			
		||||
			if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, NULL, &path)))
 | 
			
		||||
			{
 | 
			
		||||
				auto end = path;
 | 
			
		||||
				while (*end != 0) end++;
 | 
			
		||||
 | 
			
		||||
				FilePath result;
 | 
			
		||||
				result.append_utf16((u16*)path, (u16*)end);
 | 
			
		||||
 | 
			
		||||
				g_user_directory = Path::join(Path::normalize(result), config.name) + "/";
 | 
			
		||||
			}
 | 
			
		||||
			CoTaskMemFree(path);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Not currently fullscreen
 | 
			
		||||
		g_fullscreen = false;
 | 
			
		||||
 | 
			
		||||
		// Finished Platform Setup
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::ready()
 | 
			
		||||
	{
 | 
			
		||||
		// Setup Window Size
 | 
			
		||||
		{
 | 
			
		||||
			auto scale = get_content_scale();
 | 
			
		||||
			int sw = (int)(App::config().width * scale);
 | 
			
		||||
			int sh = (int)(App::config().height * scale);
 | 
			
		||||
			set_size(sw, sh);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Display the game window
 | 
			
		||||
		ShowWindow(g_hwnd, SW_SHOW);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::shutdown()
 | 
			
		||||
	{
 | 
			
		||||
		DestroyWindow(g_hwnd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u64 PlatformBackend::ticks()
 | 
			
		||||
	{
 | 
			
		||||
		// Todo:
 | 
			
		||||
		// This should account for whatever Time::ticks_per_second is set to
 | 
			
		||||
 | 
			
		||||
		auto now = std::chrono::system_clock::now().time_since_epoch();
 | 
			
		||||
		return std::chrono::duration_cast<std::chrono::microseconds>(now - g_start_time).count();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LRESULT CALLBACK window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 | 
			
		||||
	{
 | 
			
		||||
		switch (msg)
 | 
			
		||||
		{
 | 
			
		||||
		case WM_CLOSE:
 | 
			
		||||
		{
 | 
			
		||||
			auto& config = App::config();
 | 
			
		||||
			if (config.on_exit_request != nullptr)
 | 
			
		||||
				config.on_exit_request();
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		case WM_DESTROY:
 | 
			
		||||
			PostQuitMessage(0);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
			// Mouse Input
 | 
			
		||||
		case WM_LBUTTONDOWN:
 | 
			
		||||
			g_input_state->mouse.on_press(MouseButton::Left);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case WM_LBUTTONUP:
 | 
			
		||||
			g_input_state->mouse.on_release(MouseButton::Left);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case WM_RBUTTONDOWN:
 | 
			
		||||
			g_input_state->mouse.on_press(MouseButton::Right);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case WM_RBUTTONUP:
 | 
			
		||||
			g_input_state->mouse.on_release(MouseButton::Right);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case WM_MBUTTONDOWN:
 | 
			
		||||
			g_input_state->mouse.on_press(MouseButton::Middle);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case WM_MBUTTONUP:
 | 
			
		||||
			g_input_state->mouse.on_release(MouseButton::Middle);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case WM_MOUSEMOVE:
 | 
			
		||||
			g_input_state->mouse.on_move(Vec2((float)((u16)lParam), (float)(lParam >> 16)), Vec2::zero);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case WM_MOUSEWHEEL:
 | 
			
		||||
			g_input_state->mouse.wheel = Point(0, GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
			// Text Input
 | 
			
		||||
		case WM_UNICHAR:
 | 
			
		||||
			if (wParam == UNICODE_NOCHAR)
 | 
			
		||||
				return 1;
 | 
			
		||||
		case WM_CHAR:
 | 
			
		||||
		{
 | 
			
		||||
			String result;
 | 
			
		||||
			result.append((u32)wParam);
 | 
			
		||||
			if (result.length() > 0)
 | 
			
		||||
				g_input_state->keyboard.text += result.cstr();
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Keyboard Input
 | 
			
		||||
		case WM_KEYDOWN:
 | 
			
		||||
		case WM_SYSKEYDOWN:
 | 
			
		||||
		{
 | 
			
		||||
			auto is_repeat = ((lParam & (1 << 30)) >> 30) == 1;
 | 
			
		||||
			if (!is_repeat)
 | 
			
		||||
			{
 | 
			
		||||
				auto key = scancode_to_key(wParam, lParam);
 | 
			
		||||
				if (key != Key::Unknown)
 | 
			
		||||
					g_input_state->keyboard.on_press(key);
 | 
			
		||||
			}
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		case WM_KEYUP:
 | 
			
		||||
		case WM_SYSKEYUP:
 | 
			
		||||
		{
 | 
			
		||||
			auto key = scancode_to_key(wParam, lParam);
 | 
			
		||||
			if (key != Key::Unknown)
 | 
			
		||||
				g_input_state->keyboard.on_release(key);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return DefWindowProc(hwnd, msg, wParam, lParam);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::update(InputState& state)
 | 
			
		||||
	{
 | 
			
		||||
		g_input_state = &state;
 | 
			
		||||
 | 
			
		||||
		// Catch & Dispatch Window Messages
 | 
			
		||||
		MSG msg;
 | 
			
		||||
		while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
 | 
			
		||||
		{
 | 
			
		||||
			TranslateMessage(&msg);
 | 
			
		||||
			DispatchMessage(&msg);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::sleep(int milliseconds)
 | 
			
		||||
	{
 | 
			
		||||
		if (milliseconds > 0)
 | 
			
		||||
			Sleep(milliseconds);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::present()
 | 
			
		||||
	{
 | 
			
		||||
		if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
		{
 | 
			
		||||
			HDC hdc = GetDC(g_hwnd);
 | 
			
		||||
			SwapBuffers(hdc);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* PlatformBackend::get_title()
 | 
			
		||||
	{
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::set_title(const char* title)
 | 
			
		||||
	{
 | 
			
		||||
		SetWindowText(g_hwnd, title);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::get_position(int* x, int* y)
 | 
			
		||||
	{
 | 
			
		||||
		RECT rect;
 | 
			
		||||
		if (GetWindowRect(g_hwnd, &rect))
 | 
			
		||||
		{
 | 
			
		||||
			*x = rect.left;
 | 
			
		||||
			*y = rect.top;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::set_position(int x, int y)
 | 
			
		||||
	{
 | 
			
		||||
		int w, h;
 | 
			
		||||
		get_size(&w, &h);
 | 
			
		||||
		SetWindowPos(g_hwnd, NULL, x, y, w, h, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::set_fullscreen(bool enabled)
 | 
			
		||||
	{
 | 
			
		||||
		if (g_fullscreen == enabled)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		g_fullscreen = enabled;
 | 
			
		||||
 | 
			
		||||
		if (g_fullscreen)
 | 
			
		||||
		{
 | 
			
		||||
			GetWindowRect(g_hwnd, &g_windowed_position);
 | 
			
		||||
 | 
			
		||||
			int w = GetSystemMetrics(SM_CXSCREEN);
 | 
			
		||||
			int h = GetSystemMetrics(SM_CYSCREEN);
 | 
			
		||||
			SetWindowLongPtr(g_hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
 | 
			
		||||
			SetWindowPos(g_hwnd, HWND_TOP, 0, 0, w, h, 0);
 | 
			
		||||
			ShowWindow(g_hwnd, SW_SHOW);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			SetWindowLongPtr(g_hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
 | 
			
		||||
			SetWindowPos(g_hwnd, HWND_TOP,
 | 
			
		||||
				g_windowed_position.left,
 | 
			
		||||
				g_windowed_position.top,
 | 
			
		||||
				g_windowed_position.right - g_windowed_position.left,
 | 
			
		||||
				g_windowed_position.bottom - g_windowed_position.top, 0);
 | 
			
		||||
			ShowWindow(g_hwnd, SW_SHOW);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::get_size(int* width, int* height)
 | 
			
		||||
	{
 | 
			
		||||
		RECT rect;
 | 
			
		||||
		if (GetClientRect(g_hwnd, &rect))
 | 
			
		||||
		{
 | 
			
		||||
			*width = rect.right - rect.left;
 | 
			
		||||
			*height = rect.bottom - rect.top;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::set_size(int width, int height)
 | 
			
		||||
	{
 | 
			
		||||
		RECT client_rect;
 | 
			
		||||
		RECT border_rect;
 | 
			
		||||
 | 
			
		||||
		GetClientRect(g_hwnd, &client_rect);
 | 
			
		||||
		GetWindowRect(g_hwnd, &border_rect);
 | 
			
		||||
 | 
			
		||||
		int border_width = (border_rect.right - border_rect.left) - (client_rect.right - client_rect.left);
 | 
			
		||||
		int border_height = (border_rect.bottom - border_rect.top) - (client_rect.bottom - client_rect.top);
 | 
			
		||||
 | 
			
		||||
		SetWindowPos(g_hwnd, NULL, border_rect.left, border_rect.top, width + border_width, height + border_height, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::get_draw_size(int* width, int* height)
 | 
			
		||||
	{
 | 
			
		||||
		RECT rect;
 | 
			
		||||
		if (GetClientRect(g_hwnd, &rect))
 | 
			
		||||
		{
 | 
			
		||||
			*width = rect.right - rect.left;
 | 
			
		||||
			*height = rect.bottom - rect.top;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	float PlatformBackend::get_content_scale()
 | 
			
		||||
	{
 | 
			
		||||
		// base value of Windows DPI
 | 
			
		||||
		// as seen here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow
 | 
			
		||||
		constexpr float base_raw_value = 96.0f;
 | 
			
		||||
 | 
			
		||||
		UINT raw_value = GetDpiForWindow(g_hwnd);
 | 
			
		||||
 | 
			
		||||
		return (raw_value / base_raw_value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* PlatformBackend::app_path()
 | 
			
		||||
	{
 | 
			
		||||
		return g_working_directory.cstr();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* PlatformBackend::user_path()
 | 
			
		||||
	{
 | 
			
		||||
		return g_user_directory.cstr();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::file_exists(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		return fs::is_regular_file(path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::file_delete(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		return fs::remove(path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::dir_create(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		std::error_code error;
 | 
			
		||||
		return fs::create_directories(path, error);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::dir_exists(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		return fs::is_directory(path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PlatformBackend::dir_delete(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		BLAH_ASSERT(false, "not implemented");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::dir_enumerate(Vector<FilePath>& list, const char* path, bool recursive)
 | 
			
		||||
	{
 | 
			
		||||
		if (fs::is_directory(path))
 | 
			
		||||
		{
 | 
			
		||||
			if (recursive)
 | 
			
		||||
			{
 | 
			
		||||
				for (auto& p : fs::recursive_directory_iterator(path))
 | 
			
		||||
					list.emplace_back(p.path().string().c_str());
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				for (auto& p : fs::directory_iterator(path))
 | 
			
		||||
					list.emplace_back(p.path().string().c_str());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::dir_explore(const char* path)
 | 
			
		||||
	{
 | 
			
		||||
		ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	FileRef PlatformBackend::file_open(const char* path, FileMode mode)
 | 
			
		||||
	{
 | 
			
		||||
		int access = 0;
 | 
			
		||||
		int creation = 0;
 | 
			
		||||
 | 
			
		||||
		switch (mode)
 | 
			
		||||
		{
 | 
			
		||||
		case FileMode::OpenRead:
 | 
			
		||||
			access = GENERIC_READ;
 | 
			
		||||
			creation = OPEN_EXISTING;
 | 
			
		||||
			break;
 | 
			
		||||
		case FileMode::Open:
 | 
			
		||||
			access = GENERIC_READ | GENERIC_WRITE;
 | 
			
		||||
			creation = OPEN_EXISTING;
 | 
			
		||||
			break;
 | 
			
		||||
		case FileMode::CreateWrite:
 | 
			
		||||
			access = GENERIC_WRITE;
 | 
			
		||||
			creation = CREATE_ALWAYS;
 | 
			
		||||
			break;
 | 
			
		||||
		case FileMode::Create:
 | 
			
		||||
			access = GENERIC_READ | GENERIC_WRITE;
 | 
			
		||||
			creation = CREATE_ALWAYS;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto result = CreateFile(path, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
 | 
			
		||||
 | 
			
		||||
		if (result == INVALID_HANDLE_VALUE)
 | 
			
		||||
			return FileRef();
 | 
			
		||||
 | 
			
		||||
		return FileRef(new Blah_Win32_File(result));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void* PlatformBackend::gl_get_func(const char* name)
 | 
			
		||||
	{
 | 
			
		||||
		// this check is taken from https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions
 | 
			
		||||
		// wglGetProcAddress doesn't always return valid pointers for some specific methods?
 | 
			
		||||
 | 
			
		||||
		void* p = (void*)g_wglGetProcAddress(name);
 | 
			
		||||
		if ((p == 0) ||
 | 
			
		||||
			(p == (void*)0x1) ||
 | 
			
		||||
			(p == (void*)0x2) ||
 | 
			
		||||
			(p == (void*)0x3) ||
 | 
			
		||||
			(p == (void*)-1))
 | 
			
		||||
		{
 | 
			
		||||
			p = (void*)GetProcAddress(g_opengl_dll, name);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return p;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void* PlatformBackend::gl_context_create()
 | 
			
		||||
	{
 | 
			
		||||
		HDC hdc = GetDC(g_hwnd);
 | 
			
		||||
		return g_wglCreateContext(hdc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::gl_context_make_current(void* context)
 | 
			
		||||
	{
 | 
			
		||||
		if (context != nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			HDC hdc = GetDC(g_hwnd);
 | 
			
		||||
			g_wglMakeCurrent(hdc, (HGLRC)context);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			g_wglMakeCurrent(NULL, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void PlatformBackend::gl_context_destroy(void* context)
 | 
			
		||||
	{
 | 
			
		||||
		g_wglDeleteContext((HGLRC)context);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void* PlatformBackend::d3d11_get_hwnd()
 | 
			
		||||
	{
 | 
			
		||||
		return g_hwnd;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Key scancode_to_key(WPARAM wParam, LPARAM lParam)
 | 
			
		||||
	{
 | 
			
		||||
		Key key = Key::Unknown;
 | 
			
		||||
 | 
			
		||||
		switch (wParam)
 | 
			
		||||
		{
 | 
			
		||||
		case VK_CANCEL: key = Key::Cancel; break;
 | 
			
		||||
		case VK_BACK: key = Key::Backspace; break;
 | 
			
		||||
		case VK_TAB: key = Key::Tab; break;
 | 
			
		||||
		case VK_CLEAR: key = Key::Clear; break;
 | 
			
		||||
		case VK_RETURN: key = Key::Enter; break;
 | 
			
		||||
		case VK_SHIFT: key = Key::LeftShift; break;
 | 
			
		||||
		case VK_CONTROL: key = Key::LeftControl; break;
 | 
			
		||||
		case VK_PAUSE: key = Key::Pause; break;
 | 
			
		||||
		case VK_CAPITAL: key = Key::Capslock; break;
 | 
			
		||||
		case VK_ESCAPE: key = Key::Escape; break;
 | 
			
		||||
		case VK_SPACE: key = Key::Space; break;
 | 
			
		||||
		case VK_PRIOR: key = Key::Prior; break;
 | 
			
		||||
		case VK_END: key = Key::End; break;
 | 
			
		||||
		case VK_HOME: key = Key::Home; break;
 | 
			
		||||
		case VK_LEFT: key = Key::Left; break;
 | 
			
		||||
		case VK_UP: key = Key::Up; break;
 | 
			
		||||
		case VK_RIGHT: key = Key::Right; break;
 | 
			
		||||
		case VK_DOWN: key = Key::Down; break;
 | 
			
		||||
		case VK_SELECT: key = Key::Select; break;
 | 
			
		||||
		case VK_PRINT: key = Key::PrintScreen; break;
 | 
			
		||||
		case VK_EXECUTE: key = Key::Execute; break;
 | 
			
		||||
		case VK_SNAPSHOT: key = Key::PrintScreen; break;
 | 
			
		||||
		case VK_INSERT: key = Key::Insert; break;
 | 
			
		||||
		case VK_DELETE: key = Key::Delete; break;
 | 
			
		||||
		case VK_HELP: key = Key::Help; break;
 | 
			
		||||
		case VK_LWIN: key = Key::LeftOS; break;
 | 
			
		||||
		case VK_RWIN: key = Key::RightOS; break;
 | 
			
		||||
		case VK_APPS: key = Key::Application; break;
 | 
			
		||||
		case VK_SLEEP: key = Key::Unknown; break;
 | 
			
		||||
		case VK_NUMPAD0: key = Key::Keypad0; break;
 | 
			
		||||
		case VK_NUMPAD1: key = Key::Keypad1; break;
 | 
			
		||||
		case VK_NUMPAD2: key = Key::Keypad2; break;
 | 
			
		||||
		case VK_NUMPAD3: key = Key::Keypad3; break;
 | 
			
		||||
		case VK_NUMPAD4: key = Key::Keypad4; break;
 | 
			
		||||
		case VK_NUMPAD5: key = Key::Keypad5; break;
 | 
			
		||||
		case VK_NUMPAD6: key = Key::Keypad6; break;
 | 
			
		||||
		case VK_NUMPAD7: key = Key::Keypad7; break;
 | 
			
		||||
		case VK_NUMPAD8: key = Key::Keypad8; break;
 | 
			
		||||
		case VK_NUMPAD9: key = Key::Keypad9; break;
 | 
			
		||||
		case VK_F1: key = Key::F1; break;
 | 
			
		||||
		case VK_F2: key = Key::F2; break;
 | 
			
		||||
		case VK_F3: key = Key::F3; break;
 | 
			
		||||
		case VK_F4: key = Key::F4; break;
 | 
			
		||||
		case VK_F5: key = Key::F5; break;
 | 
			
		||||
		case VK_F6: key = Key::F6; break;
 | 
			
		||||
		case VK_F7: key = Key::F7; break;
 | 
			
		||||
		case VK_F8: key = Key::F8; break;
 | 
			
		||||
		case VK_F9: key = Key::F9; break;
 | 
			
		||||
		case VK_F10: key = Key::F10; break;
 | 
			
		||||
		case VK_F11: key = Key::F11; break;
 | 
			
		||||
		case VK_F12: key = Key::F12; break;
 | 
			
		||||
		case VK_F13: key = Key::F13; break;
 | 
			
		||||
		case VK_F14: key = Key::F14; break;
 | 
			
		||||
		case VK_F15: key = Key::F15; break;
 | 
			
		||||
		case VK_F16: key = Key::F16; break;
 | 
			
		||||
		case VK_F17: key = Key::F17; break;
 | 
			
		||||
		case VK_F18: key = Key::F18; break;
 | 
			
		||||
		case VK_F19: key = Key::F19; break;
 | 
			
		||||
		case VK_F20: key = Key::F20; break;
 | 
			
		||||
		case VK_F21: key = Key::F21; break;
 | 
			
		||||
		case VK_F22: key = Key::F22; break;
 | 
			
		||||
		case VK_F23: key = Key::F23; break;
 | 
			
		||||
		case VK_F24: key = Key::F24; break;
 | 
			
		||||
		case VK_NUMLOCK: key = Key::Numlock; break;
 | 
			
		||||
		case VK_LSHIFT: key = Key::LeftShift; break;
 | 
			
		||||
		case VK_RSHIFT: key = Key::RightShift; break;
 | 
			
		||||
		case VK_LCONTROL: key = Key::LeftControl; break;
 | 
			
		||||
		case VK_RCONTROL: key = Key::RightControl; break;
 | 
			
		||||
		case VK_VOLUME_MUTE: key = Key::Mute; break;
 | 
			
		||||
		case VK_VOLUME_DOWN: key = Key::VolumeDown; break;
 | 
			
		||||
		case VK_VOLUME_UP: key = Key::VolumeUp; break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (key == Key::Unknown)
 | 
			
		||||
		{
 | 
			
		||||
			int scancode = (lParam >> 16) & 0xFF;
 | 
			
		||||
 | 
			
		||||
			switch (scancode)
 | 
			
		||||
			{
 | 
			
		||||
			case 1: key = Key::Escape; break;
 | 
			
		||||
			case 2: key = Key::D1; break;
 | 
			
		||||
			case 3: key = Key::D2; break;
 | 
			
		||||
			case 4: key = Key::D3; break;
 | 
			
		||||
			case 5: key = Key::D4; break;
 | 
			
		||||
			case 6: key = Key::D5; break;
 | 
			
		||||
			case 7: key = Key::D6; break;
 | 
			
		||||
			case 8: key = Key::D7; break;
 | 
			
		||||
			case 9: key = Key::D8; break;
 | 
			
		||||
			case 10: key = Key::D9; break;
 | 
			
		||||
			case 11: key = Key::D0; break;
 | 
			
		||||
			case 12: key = Key::Minus; break;
 | 
			
		||||
			case 13: key = Key::Equals; break;
 | 
			
		||||
			case 14: key = Key::Backspace; break;
 | 
			
		||||
			case 15: key = Key::Tab; break;
 | 
			
		||||
			case 16: key = Key::Q; break;
 | 
			
		||||
			case 17: key = Key::W; break;
 | 
			
		||||
			case 18: key = Key::E; break;
 | 
			
		||||
			case 19: key = Key::R; break;
 | 
			
		||||
			case 20: key = Key::T; break;
 | 
			
		||||
			case 21: key = Key::Y; break;
 | 
			
		||||
			case 22: key = Key::U; break;
 | 
			
		||||
			case 23: key = Key::I; break;
 | 
			
		||||
			case 24: key = Key::O; break;
 | 
			
		||||
			case 25: key = Key::P; break;
 | 
			
		||||
			case 26: key = Key::LeftBracket; break;
 | 
			
		||||
			case 27: key = Key::RightBracket; break;
 | 
			
		||||
			case 28: key = Key::Enter; break;
 | 
			
		||||
			case 29: key = Key::LeftControl; break;
 | 
			
		||||
			case 30: key = Key::A; break;
 | 
			
		||||
			case 31: key = Key::S; break;
 | 
			
		||||
			case 32: key = Key::D; break;
 | 
			
		||||
			case 33: key = Key::F; break;
 | 
			
		||||
			case 34: key = Key::G; break;
 | 
			
		||||
			case 35: key = Key::H; break;
 | 
			
		||||
			case 36: key = Key::J; break;
 | 
			
		||||
			case 37: key = Key::K; break;
 | 
			
		||||
			case 38: key = Key::L; break;
 | 
			
		||||
			case 39: key = Key::Semicolon; break;
 | 
			
		||||
			case 40: key = Key::Apostrophe; break;
 | 
			
		||||
			case 41: key = Key::Tilde; break;
 | 
			
		||||
			case 42: key = Key::LeftShift; break;
 | 
			
		||||
			case 43: key = Key::Backslash; break;
 | 
			
		||||
			case 44: key = Key::Z; break;
 | 
			
		||||
			case 45: key = Key::X; break;
 | 
			
		||||
			case 46: key = Key::C; break;
 | 
			
		||||
			case 47: key = Key::V; break;
 | 
			
		||||
			case 48: key = Key::B; break;
 | 
			
		||||
			case 49: key = Key::N; break;
 | 
			
		||||
			case 50: key = Key::M; break;
 | 
			
		||||
			case 51: key = Key::Comma; break;
 | 
			
		||||
			case 52: key = Key::Period; break;
 | 
			
		||||
			case 53: key = Key::Slash; break;
 | 
			
		||||
			case 54: key = Key::RightShift; break;
 | 
			
		||||
			case 55: key = Key::PrintScreen; break;
 | 
			
		||||
			case 56: key = Key::LeftAlt; break;
 | 
			
		||||
			case 57: key = Key::Space; break;
 | 
			
		||||
			case 58: key = Key::Capslock; break;
 | 
			
		||||
			case 59: key = Key::F1; break;
 | 
			
		||||
			case 60: key = Key::F2; break;
 | 
			
		||||
			case 61: key = Key::F3; break;
 | 
			
		||||
			case 62: key = Key::F4; break;
 | 
			
		||||
			case 63: key = Key::F5; break;
 | 
			
		||||
			case 64: key = Key::F6; break;
 | 
			
		||||
			case 65: key = Key::F7; break;
 | 
			
		||||
			case 66: key = Key::F8; break;
 | 
			
		||||
			case 67: key = Key::F9; break;
 | 
			
		||||
			case 68: key = Key::F10; break;
 | 
			
		||||
			case 71: key = Key::Home; break;
 | 
			
		||||
			case 72: key = Key::Up; break;
 | 
			
		||||
			case 73: key = Key::PageUp; break;
 | 
			
		||||
			case 74: key = Key::KeypadMinus; break;
 | 
			
		||||
			case 75: key = Key::Left; break;
 | 
			
		||||
			case 76: key = Key::Keypad5; break;
 | 
			
		||||
			case 77: key = Key::Right; break;
 | 
			
		||||
			case 78: key = Key::KeypadPlus; break;
 | 
			
		||||
			case 79: key = Key::End; break;
 | 
			
		||||
			case 80: key = Key::Down; break;
 | 
			
		||||
			case 81: key = Key::PageDown; break;
 | 
			
		||||
			case 82: key = Key::Insert; break;
 | 
			
		||||
			case 83: key = Key::Delete; break;
 | 
			
		||||
			case 87: key = Key::F11; break;
 | 
			
		||||
			case 88: key = Key::F12; break;
 | 
			
		||||
			case 89: key = Key::Pause; break;
 | 
			
		||||
			case 91: key = Key::LeftOS; break;
 | 
			
		||||
			case 92: key = Key::RightOS; break;
 | 
			
		||||
			case 93: key = Key::Application; break;
 | 
			
		||||
			case 100: key = Key::F13; break;
 | 
			
		||||
			case 101: key = Key::F14; break;
 | 
			
		||||
			case 102: key = Key::F15; break;
 | 
			
		||||
			case 103: key = Key::F16; break;
 | 
			
		||||
			case 104: key = Key::F17; break;
 | 
			
		||||
			case 105: key = Key::F18; break;
 | 
			
		||||
			case 106: key = Key::F19; break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return key;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // BLAH_PLATFORM_WIN32
 | 
			
		||||
							
								
								
									
										760
									
								
								src/internal/platform_sdl2.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										760
									
								
								src/internal/platform_sdl2.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,760 @@
 | 
			
		||||
#ifdef BLAH_PLATFORM_SDL2
 | 
			
		||||
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include "input.h"
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include <blah/input.h>
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include <blah/filesystem.h>
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include <blah/time.h>
 | 
			
		||||
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
 | 
			
		||||
#if _WIN32
 | 
			
		||||
#include <SDL_syswm.h>
 | 
			
		||||
// on Windows we're using the C++ <filesystem> API for now
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <winuser.h>	// for SetProcessDPIAware
 | 
			
		||||
#include <filesystem>	// for File Reading/Writing
 | 
			
		||||
#include <shellapi.h>	// for file explore
 | 
			
		||||
namespace fs = std::filesystem;
 | 
			
		||||
#else
 | 
			
		||||
// on non-Windows we use POSIX standard file system stuff
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	// Blah SDL2 Platform State
 | 
			
		||||
	struct SDL2Platform
 | 
			
		||||
	{
 | 
			
		||||
		SDL_Window* window = nullptr;
 | 
			
		||||
		SDL_Joystick* joysticks[Input::max_controllers];
 | 
			
		||||
		SDL_GameController* gamepads[Input::max_controllers];
 | 
			
		||||
		char* base_path = nullptr;
 | 
			
		||||
		char* user_path = nullptr;
 | 
			
		||||
		bool displayed = false;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Blah SDL2 File
 | 
			
		||||
	class SDL2File : public File
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
		SDL_RWops* m_handle;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		SDL2File(SDL_RWops* handle)
 | 
			
		||||
		{
 | 
			
		||||
			m_handle = handle;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~SDL2File()
 | 
			
		||||
		{
 | 
			
		||||
			if (m_handle)
 | 
			
		||||
				SDL_RWclose(m_handle);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t length() override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWsize(m_handle);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t position() override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWtell(m_handle);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t seek(size_t position) override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWseek(m_handle, position, RW_SEEK_SET);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t read(unsigned char* buffer, size_t length) override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWread(m_handle, buffer, sizeof(char), length);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t write(const unsigned char* buffer, size_t length) override
 | 
			
		||||
		{
 | 
			
		||||
			return SDL_RWwrite(m_handle, buffer, sizeof(char), length);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void sdl_log(void* userdata, int category, SDL_LogPriority priority, const char* message)
 | 
			
		||||
	{
 | 
			
		||||
		if (priority <= SDL_LOG_PRIORITY_INFO)
 | 
			
		||||
			Log::info(message);
 | 
			
		||||
		else if (priority <= SDL_LOG_PRIORITY_WARN)
 | 
			
		||||
			Log::warn(message);
 | 
			
		||||
		else
 | 
			
		||||
			Log::error(message);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int sdl_find_joystick_index(SDL_Joystick** joysticks, SDL_JoystickID instance_id)
 | 
			
		||||
	{
 | 
			
		||||
		for (int i = 0; i < Input::max_controllers; i++)
 | 
			
		||||
			if (joysticks[i] != nullptr && SDL_JoystickInstanceID(joysticks[i]) == instance_id)
 | 
			
		||||
				return i;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int sdl_find_gamepad_index(SDL_GameController** gamepads, SDL_JoystickID instance_id)
 | 
			
		||||
	{
 | 
			
		||||
		for (int i = 0; i < Input::max_controllers; i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (gamepads[i] != nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				auto joystick = SDL_GameControllerGetJoystick(gamepads[i]);
 | 
			
		||||
				if (SDL_JoystickInstanceID(joystick) == instance_id)
 | 
			
		||||
					return i;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
static Blah::SDL2Platform g_platform;
 | 
			
		||||
 | 
			
		||||
bool Platform::init(const Config& config)
 | 
			
		||||
{
 | 
			
		||||
	g_platform = SDL2Platform();
 | 
			
		||||
 | 
			
		||||
	// Required to call this for Windows
 | 
			
		||||
	// I'm not sure why SDL2 doesn't do this on Windows automatically?
 | 
			
		||||
#if _WIN32
 | 
			
		||||
	SetProcessDPIAware();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// TODO:
 | 
			
		||||
	// control this via some kind of config flag
 | 
			
		||||
	SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);
 | 
			
		||||
	SDL_LogSetOutputFunction(sdl_log, nullptr);
 | 
			
		||||
 | 
			
		||||
	// Get SDL version
 | 
			
		||||
	SDL_version version;
 | 
			
		||||
	SDL_GetVersion(&version);
 | 
			
		||||
	Log::info("SDL v%i.%i.%i", version.major, version.minor, version.patch);
 | 
			
		||||
 | 
			
		||||
	// initialize SDL
 | 
			
		||||
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Failed to initialize SDL2");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
 | 
			
		||||
 | 
			
		||||
	// enable OpenGL
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		flags |= SDL_WINDOW_OPENGL;
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
 | 
			
		||||
#else
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 | 
			
		||||
 | 
			
		||||
		// TODO:
 | 
			
		||||
		// This should be controlled via the gfx api somehow?
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
 | 
			
		||||
		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// create the window
 | 
			
		||||
	g_platform.window = SDL_CreateWindow(config.name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, config.width, config.height, flags);
 | 
			
		||||
	if (g_platform.window == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Failed to create a Window");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Scale Window to monitor for High DPI displays
 | 
			
		||||
	// Other platforms do this automatically ... Windows we need to explitely do so
 | 
			
		||||
#if _WIN32
 | 
			
		||||
	{
 | 
			
		||||
		// find the display index
 | 
			
		||||
		int display = SDL_GetWindowDisplayIndex(g_platform.window);
 | 
			
		||||
		float ddpi, hdpi, vdpi;
 | 
			
		||||
		if (SDL_GetDisplayDPI(display, &ddpi, &hdpi, &vdpi) == 0)
 | 
			
		||||
		{
 | 
			
		||||
			// scale the window up basesd on the display DPI
 | 
			
		||||
			float hidpiRes = 96;
 | 
			
		||||
			float dpi = (ddpi / hidpiRes);
 | 
			
		||||
			if (dpi != 1)
 | 
			
		||||
			{
 | 
			
		||||
				SDL_DisplayMode mode;
 | 
			
		||||
				SDL_GetDesktopDisplayMode(display, &mode);
 | 
			
		||||
				SDL_SetWindowPosition(g_platform.window, (int)(mode.w - config.width * dpi) / 2, (int)(mode.h - config.height * dpi) / 2);
 | 
			
		||||
				SDL_SetWindowSize(g_platform.window, (int)(config.width * dpi), (int)(config.height * dpi));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// set window properties
 | 
			
		||||
	SDL_SetWindowResizable(g_platform.window, SDL_TRUE);
 | 
			
		||||
	SDL_SetWindowMinimumSize(g_platform.window, 256, 256);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::ready()
 | 
			
		||||
{
 | 
			
		||||
#ifndef __EMSCRIPTEN__
 | 
			
		||||
	// enable V-Sync
 | 
			
		||||
	// TODO:
 | 
			
		||||
	// This should be a toggle or controllable in some way
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
		SDL_GL_SetSwapInterval(1);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::shutdown()
 | 
			
		||||
{
 | 
			
		||||
	if (g_platform.window != nullptr)
 | 
			
		||||
		SDL_DestroyWindow(g_platform.window);
 | 
			
		||||
	g_platform.window = nullptr;
 | 
			
		||||
	g_platform.displayed = false;
 | 
			
		||||
 | 
			
		||||
	if (g_platform.base_path != nullptr)
 | 
			
		||||
		SDL_free(g_platform.base_path);
 | 
			
		||||
 | 
			
		||||
	if (g_platform.user_path != nullptr)
 | 
			
		||||
		SDL_free(g_platform.user_path);
 | 
			
		||||
 | 
			
		||||
	SDL_Quit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u64 Platform::ticks()
 | 
			
		||||
{
 | 
			
		||||
	auto counter = SDL_GetPerformanceCounter();
 | 
			
		||||
	auto per_second = (double)SDL_GetPerformanceFrequency();
 | 
			
		||||
	return (u64)(counter * (Time::ticks_per_second / per_second));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Macro defined by X11 conflicts with MouseButton enum
 | 
			
		||||
#undef None
 | 
			
		||||
 | 
			
		||||
void Platform::update(InputState& state)
 | 
			
		||||
{
 | 
			
		||||
	// update the mouse every frame
 | 
			
		||||
	{
 | 
			
		||||
		int win_x, win_y, x, y;
 | 
			
		||||
 | 
			
		||||
		SDL_GetWindowPosition(g_platform.window, &win_x, &win_y);
 | 
			
		||||
		SDL_GetGlobalMouseState(&x, &y);
 | 
			
		||||
 | 
			
		||||
		state.mouse.on_move(
 | 
			
		||||
			Vec2((float)(x - win_x), (float)(y - win_y)),
 | 
			
		||||
			Vec2((float)x, (float)y));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// poll normal events
 | 
			
		||||
	SDL_Event event;
 | 
			
		||||
	while (SDL_PollEvent(&event))
 | 
			
		||||
	{
 | 
			
		||||
		if (event.type == SDL_QUIT)
 | 
			
		||||
		{
 | 
			
		||||
			auto& config = App::config();
 | 
			
		||||
			if (config.on_exit_request != nullptr)
 | 
			
		||||
				config.on_exit_request();
 | 
			
		||||
		}
 | 
			
		||||
		// Mouse
 | 
			
		||||
		else if (event.type == SDL_MOUSEBUTTONDOWN)
 | 
			
		||||
		{
 | 
			
		||||
			MouseButton btn = MouseButton::None;
 | 
			
		||||
			if (event.button.button == SDL_BUTTON_LEFT)
 | 
			
		||||
				btn = MouseButton::Left;
 | 
			
		||||
			else if (event.button.button == SDL_BUTTON_RIGHT)
 | 
			
		||||
				btn = MouseButton::Right;
 | 
			
		||||
			else if (event.button.button == SDL_BUTTON_MIDDLE)
 | 
			
		||||
				btn = MouseButton::Middle;
 | 
			
		||||
 | 
			
		||||
			state.mouse.on_press(btn);
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_MOUSEBUTTONUP)
 | 
			
		||||
		{
 | 
			
		||||
			MouseButton btn = MouseButton::None;
 | 
			
		||||
			if (event.button.button == SDL_BUTTON_LEFT)
 | 
			
		||||
				btn = MouseButton::Left;
 | 
			
		||||
			else if (event.button.button == SDL_BUTTON_RIGHT)
 | 
			
		||||
				btn = MouseButton::Right;
 | 
			
		||||
			else if (event.button.button == SDL_BUTTON_MIDDLE)
 | 
			
		||||
				btn = MouseButton::Middle;
 | 
			
		||||
 | 
			
		||||
			state.mouse.on_release(btn);
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_MOUSEWHEEL)
 | 
			
		||||
		{
 | 
			
		||||
			state.mouse.wheel = Point(event.wheel.x, event.wheel.y);
 | 
			
		||||
		}
 | 
			
		||||
		// Keyboard
 | 
			
		||||
		else if (event.type == SDL_KEYDOWN)
 | 
			
		||||
		{
 | 
			
		||||
			if (event.key.repeat == 0)
 | 
			
		||||
				state.keyboard.on_press((Key)event.key.keysym.scancode);
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_KEYUP)
 | 
			
		||||
		{
 | 
			
		||||
			if (event.key.repeat == 0)
 | 
			
		||||
				state.keyboard.on_release((Key)event.key.keysym.scancode);
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_TEXTINPUT)
 | 
			
		||||
		{
 | 
			
		||||
			state.keyboard.text += event.text.text;
 | 
			
		||||
		}
 | 
			
		||||
		// Joystick Controller
 | 
			
		||||
		else if (event.type == SDL_JOYDEVICEADDED)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = event.jdevice.which;
 | 
			
		||||
 | 
			
		||||
			if (SDL_IsGameController(index) == SDL_FALSE && index >= 0 && index < Input::max_controllers)
 | 
			
		||||
			{
 | 
			
		||||
				auto ptr = g_platform.joysticks[index] = SDL_JoystickOpen(index);
 | 
			
		||||
				auto name = SDL_JoystickName(ptr);
 | 
			
		||||
				auto button_count = SDL_JoystickNumButtons(ptr);
 | 
			
		||||
				auto axis_count = SDL_JoystickNumAxes(ptr);
 | 
			
		||||
				auto vendor = SDL_JoystickGetVendor(ptr);
 | 
			
		||||
				auto product = SDL_JoystickGetProduct(ptr);
 | 
			
		||||
				auto version = SDL_JoystickGetProductVersion(ptr);
 | 
			
		||||
 | 
			
		||||
				state.controllers[index].on_connect(name, 0, button_count, axis_count, vendor, product, version);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_JOYDEVICEREMOVED)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which);
 | 
			
		||||
			if (index >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				if (SDL_IsGameController(index) == SDL_FALSE)
 | 
			
		||||
				{
 | 
			
		||||
					state.controllers[index].on_disconnect();
 | 
			
		||||
					SDL_JoystickClose(g_platform.joysticks[index]);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_JOYBUTTONDOWN)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which);
 | 
			
		||||
			if (index >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				if (SDL_IsGameController(index) == SDL_FALSE)
 | 
			
		||||
					state.controllers[index].on_press((Button)event.jbutton.button);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_JOYBUTTONUP)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which);
 | 
			
		||||
			if (index >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				if (SDL_IsGameController(index) == SDL_FALSE)
 | 
			
		||||
					state.controllers[index].on_release((Button)event.jbutton.button);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_JOYAXISMOTION)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = sdl_find_joystick_index(g_platform.joysticks, event.jdevice.which);
 | 
			
		||||
			if (index >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				if (SDL_IsGameController(index) == SDL_FALSE)
 | 
			
		||||
				{
 | 
			
		||||
					float value;
 | 
			
		||||
					if (event.jaxis.value >= 0)
 | 
			
		||||
						value = event.jaxis.value / 32767.0f;
 | 
			
		||||
					else
 | 
			
		||||
						value = event.jaxis.value / 32768.0f;
 | 
			
		||||
					state.controllers[index].on_axis((Axis)event.jaxis.axis, value);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Gamepad Controller
 | 
			
		||||
		else if (event.type == SDL_CONTROLLERDEVICEADDED)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = event.cdevice.which;
 | 
			
		||||
			if (index >= 0 && index < Input::max_controllers)
 | 
			
		||||
			{
 | 
			
		||||
				auto ptr = g_platform.gamepads[index] = SDL_GameControllerOpen(index);
 | 
			
		||||
				auto name = SDL_GameControllerName(ptr);
 | 
			
		||||
				auto vendor = SDL_GameControllerGetVendor(ptr);
 | 
			
		||||
				auto product = SDL_GameControllerGetProduct(ptr);
 | 
			
		||||
				auto version = SDL_GameControllerGetProductVersion(ptr);
 | 
			
		||||
 | 
			
		||||
				state.controllers[index].on_connect(name, 1, 15, 6, vendor, product, version);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_CONTROLLERDEVICEREMOVED)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which);
 | 
			
		||||
			if (index >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				state.controllers[index].on_disconnect();
 | 
			
		||||
				SDL_GameControllerClose(g_platform.gamepads[index]);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_CONTROLLERBUTTONDOWN)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which);
 | 
			
		||||
			if (index >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				Button button = Button::None;
 | 
			
		||||
				if (event.cbutton.button >= 0 && event.cbutton.button < 15)
 | 
			
		||||
					button = (Button)event.cbutton.button; // NOTE: These map directly to Engine Buttons enum!
 | 
			
		||||
 | 
			
		||||
				state.controllers[index].on_press(button);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_CONTROLLERBUTTONUP)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which);
 | 
			
		||||
			if (index >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				Button button = Button::None;
 | 
			
		||||
				if (event.cbutton.button >= 0 && event.cbutton.button < 15)
 | 
			
		||||
					button = (Button)event.cbutton.button; // NOTE: These map directly to Engine Buttons enum!
 | 
			
		||||
 | 
			
		||||
				state.controllers[index].on_release(button);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (event.type == SDL_CONTROLLERAXISMOTION)
 | 
			
		||||
		{
 | 
			
		||||
			auto index = sdl_find_gamepad_index(g_platform.gamepads, event.cdevice.which);
 | 
			
		||||
			if (index >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				Axis axis = Axis::None;
 | 
			
		||||
				if (event.caxis.axis >= 0 && event.caxis.axis < 6)
 | 
			
		||||
					axis = (Axis)event.caxis.axis; // NOTE: These map directly to Engine Axis enum!
 | 
			
		||||
 | 
			
		||||
				float value;
 | 
			
		||||
				if (event.caxis.value >= 0)
 | 
			
		||||
					value = event.caxis.value / 32767.0f;
 | 
			
		||||
				else
 | 
			
		||||
					value = event.caxis.value / 32768.0f;
 | 
			
		||||
 | 
			
		||||
				state.controllers[index].on_axis(axis, value);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::sleep(int milliseconds)
 | 
			
		||||
{
 | 
			
		||||
	if (milliseconds >= 0)
 | 
			
		||||
		SDL_Delay((u32)milliseconds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::present()
 | 
			
		||||
{
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_GL_SwapWindow(g_platform.window);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// display the window
 | 
			
		||||
	// this avoids a short black screen on macoS
 | 
			
		||||
	if (!g_platform.displayed)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_ShowWindow(g_platform.window);
 | 
			
		||||
		g_platform.displayed = true;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* Platform::get_title()
 | 
			
		||||
{
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_title(const char* title)
 | 
			
		||||
{
 | 
			
		||||
	SDL_SetWindowTitle(g_platform.window, title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::get_position(int* x, int* y)
 | 
			
		||||
{
 | 
			
		||||
	SDL_GetWindowPosition(g_platform.window, x, y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_position(int x, int y)
 | 
			
		||||
{
 | 
			
		||||
	SDL_SetWindowPosition(g_platform.window, x, y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_fullscreen(bool enabled)
 | 
			
		||||
{
 | 
			
		||||
	if (enabled)
 | 
			
		||||
		SDL_SetWindowFullscreen(g_platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
 | 
			
		||||
	else
 | 
			
		||||
		SDL_SetWindowFullscreen(g_platform.window, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::get_size(int* width, int* height)
 | 
			
		||||
{
 | 
			
		||||
	SDL_GetWindowSize(g_platform.window, width, height);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_size(int width, int height)
 | 
			
		||||
{
 | 
			
		||||
	SDL_SetWindowSize(g_platform.window, width, height);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::get_draw_size(int* width, int* height)
 | 
			
		||||
{
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		SDL_GL_GetDrawableSize(g_platform.window, width, height);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		SDL_GetWindowSize(g_platform.window, width, height);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float Platform::get_content_scale()
 | 
			
		||||
{
 | 
			
		||||
	// TODO:
 | 
			
		||||
	// This is incorrect! but for some reason the scale
 | 
			
		||||
	// is HUGE if I use the Display DPI on macOS :/
 | 
			
		||||
#if __APPLE__
 | 
			
		||||
	return 2.0f;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if _WIN32
 | 
			
		||||
	float hidpiRes = 96;
 | 
			
		||||
#else
 | 
			
		||||
	float hidpiRes = 72;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	int index = SDL_GetWindowDisplayIndex(g_platform.window);
 | 
			
		||||
	if (index < 0)
 | 
			
		||||
		Log::error(SDL_GetError());
 | 
			
		||||
 | 
			
		||||
	float ddpi, x, y;
 | 
			
		||||
	if (SDL_GetDisplayDPI(index, &ddpi, &x, &y) != 0)
 | 
			
		||||
		Log::error(SDL_GetError());
 | 
			
		||||
 | 
			
		||||
	return (ddpi / hidpiRes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FILE IO
 | 
			
		||||
 | 
			
		||||
const char* Platform::app_path()
 | 
			
		||||
{
 | 
			
		||||
	if (g_platform.base_path == nullptr)
 | 
			
		||||
		g_platform.base_path = SDL_GetBasePath();
 | 
			
		||||
	return g_platform.base_path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* Platform::user_path()
 | 
			
		||||
{
 | 
			
		||||
	if (g_platform.user_path == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		auto& config = App::config();
 | 
			
		||||
		g_platform.user_path = SDL_GetPrefPath(nullptr, config.name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return g_platform.user_path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FileRef Platform::file_open(const char* path, FileMode mode)
 | 
			
		||||
{
 | 
			
		||||
	const char* sdl_mode = "";
 | 
			
		||||
 | 
			
		||||
	switch (mode)
 | 
			
		||||
	{
 | 
			
		||||
	case FileMode::OpenRead:
 | 
			
		||||
		sdl_mode = "rb";
 | 
			
		||||
		break;
 | 
			
		||||
	case FileMode::Open:
 | 
			
		||||
		sdl_mode = "r+b";
 | 
			
		||||
		break;
 | 
			
		||||
	case FileMode::CreateWrite:
 | 
			
		||||
		sdl_mode = "wb";
 | 
			
		||||
		break;
 | 
			
		||||
	case FileMode::Create:
 | 
			
		||||
		sdl_mode = "w+b";
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto ptr = SDL_RWFromFile(path, sdl_mode);
 | 
			
		||||
	if (!ptr)
 | 
			
		||||
		return FileRef();
 | 
			
		||||
 | 
			
		||||
	return FileRef(new SDL2File(ptr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Windows File System methods
 | 
			
		||||
#if _WIN32
 | 
			
		||||
 | 
			
		||||
bool Platform::file_exists(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return fs::is_regular_file(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::file_delete(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return fs::remove(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::dir_create(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return fs::create_directories(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::dir_exists(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return fs::is_directory(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::dir_delete(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return fs::remove_all(path) > 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::dir_enumerate(Vector<FilePath>& list, const char* path, bool recursive)
 | 
			
		||||
{
 | 
			
		||||
	if (fs::is_directory(path))
 | 
			
		||||
	{
 | 
			
		||||
		if (recursive)
 | 
			
		||||
		{
 | 
			
		||||
			for (auto& p : fs::recursive_directory_iterator(path))
 | 
			
		||||
				list.emplace_back(p.path().string().c_str());
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			for (auto& p : fs::directory_iterator(path))
 | 
			
		||||
				list.emplace_back(p.path().string().c_str());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::dir_explore(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Non-Windows File System Methods
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
bool Platform::file_exists(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	struct stat buffer;
 | 
			
		||||
	return (stat(path, &buffer) == 0) && S_ISREG(buffer.st_mode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::file_delete(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return unlink(path) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::dir_create(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	char tmp[265];
 | 
			
		||||
	char* p = NULL;
 | 
			
		||||
	size_t len;
 | 
			
		||||
 | 
			
		||||
	snprintf(tmp, sizeof(tmp), "%s", path);
 | 
			
		||||
	len = strlen(tmp);
 | 
			
		||||
	if (tmp[len - 1] == '/')
 | 
			
		||||
		tmp[len - 1] = 0;
 | 
			
		||||
	for (p = tmp + 1; *p; p++)
 | 
			
		||||
		if (*p == '/') {
 | 
			
		||||
			*p = 0;
 | 
			
		||||
			mkdir(tmp, S_IRWXU);
 | 
			
		||||
			*p = '/';
 | 
			
		||||
		}
 | 
			
		||||
	return mkdir(tmp, S_IRWXU) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::dir_exists(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	struct stat buffer;
 | 
			
		||||
	return (stat(path, &buffer) == 0) && S_ISDIR(buffer.st_mode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::dir_delete(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT(false, "not implemented");
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::dir_enumerate(Vector<FilePath>& list, const char* path, bool recursive)
 | 
			
		||||
{
 | 
			
		||||
	DIR* dirp = opendir(path);
 | 
			
		||||
	if (dirp != NULL)
 | 
			
		||||
	{
 | 
			
		||||
		struct dirent* dp;
 | 
			
		||||
		while ((dp = readdir(dirp)) != NULL)
 | 
			
		||||
		{
 | 
			
		||||
			if (dp->d_name[0] == '.')
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			FilePath subpath = FilePath(path);
 | 
			
		||||
			if (subpath.end()[-1] != '/') subpath = subpath.append("/");
 | 
			
		||||
			subpath = subpath.append(dp->d_name);
 | 
			
		||||
			list.push_back(subpath);
 | 
			
		||||
 | 
			
		||||
			if (recursive && dp->d_type == DT_DIR)
 | 
			
		||||
				dir_enumerate(list, subpath + "/", true);
 | 
			
		||||
		}
 | 
			
		||||
		closedir(dirp);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::dir_explore(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	BLAH_ASSERT(false, "'dir_explore' Not Implemented");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void* Platform::gl_get_func(const char* name)
 | 
			
		||||
{
 | 
			
		||||
	return SDL_GL_GetProcAddress(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* Platform::gl_context_create()
 | 
			
		||||
{
 | 
			
		||||
	void* pointer = SDL_GL_CreateContext(g_platform.window);
 | 
			
		||||
	if (pointer == nullptr)
 | 
			
		||||
		Log::error("SDL_GL_CreateContext failed: %s", SDL_GetError());
 | 
			
		||||
	return pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::gl_context_make_current(void* context)
 | 
			
		||||
{
 | 
			
		||||
	SDL_GL_MakeCurrent(g_platform.window, context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::gl_context_destroy(void* context)
 | 
			
		||||
{
 | 
			
		||||
	SDL_GL_DeleteContext(context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* Platform::d3d11_get_hwnd()
 | 
			
		||||
{
 | 
			
		||||
#if _WIN32
 | 
			
		||||
	SDL_SysWMinfo info;
 | 
			
		||||
	SDL_VERSION(&info.version);
 | 
			
		||||
	SDL_GetWindowWMInfo(g_platform.window, &info);
 | 
			
		||||
	return info.info.win.window;
 | 
			
		||||
#else
 | 
			
		||||
	return nullptr;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // BLAH_PLATFORM_SDL2
 | 
			
		||||
							
								
								
									
										856
									
								
								src/internal/platform_win32.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										856
									
								
								src/internal/platform_win32.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,856 @@
 | 
			
		||||
#ifdef BLAH_PLATFORM_WIN32
 | 
			
		||||
 | 
			
		||||
// Note:
 | 
			
		||||
// This backend implementation is unfinished! 
 | 
			
		||||
// It's missing a few things, namely:
 | 
			
		||||
// - Controller Support
 | 
			
		||||
// (And error testing)
 | 
			
		||||
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include "input.h"
 | 
			
		||||
#include "graphics.h"
 | 
			
		||||
#include <blah/input.h>
 | 
			
		||||
#include <blah/app.h>
 | 
			
		||||
#include <blah/filesystem.h>
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include <blah/time.h>
 | 
			
		||||
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <winuser.h>    // for SetProcessDPIAware
 | 
			
		||||
#include <filesystem>   // for File Reading/Writing
 | 
			
		||||
#include <shellapi.h>   // for file explore
 | 
			
		||||
#include <shlobj.h>     // for known folder
 | 
			
		||||
#include <chrono>       // for ticks method
 | 
			
		||||
 | 
			
		||||
namespace Blah
 | 
			
		||||
{
 | 
			
		||||
	using Duration = std::chrono::system_clock::duration;
 | 
			
		||||
 | 
			
		||||
	// Win32 Platform State
 | 
			
		||||
	struct Win32Platform
 | 
			
		||||
	{
 | 
			
		||||
		// Main State
 | 
			
		||||
		HWND         hwnd;
 | 
			
		||||
		FilePath     working_directory;
 | 
			
		||||
		FilePath     user_directory;
 | 
			
		||||
		Duration     start_time;
 | 
			
		||||
		RECT         windowed_position;
 | 
			
		||||
		bool         fullscreen = false;
 | 
			
		||||
		InputState*  input_state = nullptr;
 | 
			
		||||
 | 
			
		||||
		// OpenGL Methods
 | 
			
		||||
		// These are only loaded if built using the OpenGL Backend
 | 
			
		||||
		HMODULE  opengl_dll;
 | 
			
		||||
		void*    (WINAPI* wglGetProcAddress)  (const char* proc);
 | 
			
		||||
		HGLRC    (WINAPI* wglCreateContext)   (HDC hdc);
 | 
			
		||||
		BOOL     (WINAPI* wglDeleteContext)   (HGLRC hglrc);
 | 
			
		||||
		BOOL     (WINAPI* wglMakeCurrent)     (HDC hdc, HGLRC hglrc);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Win32 File Class
 | 
			
		||||
	class Win32File : public File
 | 
			
		||||
	{
 | 
			
		||||
	private:
 | 
			
		||||
		HANDLE m_handle;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		Win32File(HANDLE handle)
 | 
			
		||||
		{
 | 
			
		||||
			m_handle = handle;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~Win32File()
 | 
			
		||||
		{
 | 
			
		||||
			CloseHandle(m_handle);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t length() override
 | 
			
		||||
		{
 | 
			
		||||
			// Todo: cache this value? not sure how performant it is
 | 
			
		||||
 | 
			
		||||
			LARGE_INTEGER file_size;
 | 
			
		||||
			if (GetFileSizeEx(m_handle, &file_size))
 | 
			
		||||
				return file_size.QuadPart;
 | 
			
		||||
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t position() override
 | 
			
		||||
		{
 | 
			
		||||
			LARGE_INTEGER move;
 | 
			
		||||
			LARGE_INTEGER result;
 | 
			
		||||
 | 
			
		||||
			move.QuadPart = 0;
 | 
			
		||||
			result.QuadPart = 0;
 | 
			
		||||
 | 
			
		||||
			SetFilePointerEx(m_handle, move, &result, FILE_CURRENT);
 | 
			
		||||
 | 
			
		||||
			return result.QuadPart;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t seek(size_t position) override
 | 
			
		||||
		{
 | 
			
		||||
			LARGE_INTEGER move;
 | 
			
		||||
			LARGE_INTEGER result;
 | 
			
		||||
 | 
			
		||||
			move.QuadPart = position;
 | 
			
		||||
			result.QuadPart = 0;
 | 
			
		||||
 | 
			
		||||
			SetFilePointerEx(m_handle, move, &result, FILE_BEGIN);
 | 
			
		||||
 | 
			
		||||
			return result.QuadPart;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t read(unsigned char* buffer, size_t length) override
 | 
			
		||||
		{
 | 
			
		||||
			static const DWORD read_step = 65536;
 | 
			
		||||
 | 
			
		||||
			size_t read = 0;
 | 
			
		||||
 | 
			
		||||
			while (read < length)
 | 
			
		||||
			{
 | 
			
		||||
				DWORD to_read = read_step;
 | 
			
		||||
				if (to_read > length - read)
 | 
			
		||||
					to_read = (DWORD)(length - read);
 | 
			
		||||
 | 
			
		||||
				DWORD moved = 0;
 | 
			
		||||
				if (ReadFile(m_handle, buffer + read, to_read, &moved, NULL))
 | 
			
		||||
					read += moved;
 | 
			
		||||
 | 
			
		||||
				if (moved < to_read)
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return read;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t write(const unsigned char* buffer, size_t length) override
 | 
			
		||||
		{
 | 
			
		||||
			static const DWORD write_step = 65536;
 | 
			
		||||
 | 
			
		||||
			size_t written = 0;
 | 
			
		||||
 | 
			
		||||
			while (written < length)
 | 
			
		||||
			{
 | 
			
		||||
				DWORD to_write = write_step;
 | 
			
		||||
				if (to_write > length - written)
 | 
			
		||||
					to_write = (DWORD)(length - written);
 | 
			
		||||
 | 
			
		||||
				DWORD moved = 0;
 | 
			
		||||
				if (WriteFile(m_handle, buffer + written, to_write, &moved, NULL))
 | 
			
		||||
					written += moved;
 | 
			
		||||
 | 
			
		||||
				if (moved < to_write)
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return written;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Converts Windows scancode to Blah key
 | 
			
		||||
	Key win32_scancode_to_key(WPARAM wParam, LPARAM lParam);
 | 
			
		||||
 | 
			
		||||
	// Main Windows Procedure callback
 | 
			
		||||
	LRESULT CALLBACK win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
namespace fs = std::filesystem;
 | 
			
		||||
 | 
			
		||||
static Blah::Win32Platform g_platform;
 | 
			
		||||
 | 
			
		||||
bool Platform::init(const Config& config)
 | 
			
		||||
{
 | 
			
		||||
	// clear platform
 | 
			
		||||
	g_platform = Win32Platform();
 | 
			
		||||
 | 
			
		||||
	// Required to call this for Windows
 | 
			
		||||
	SetProcessDPIAware();
 | 
			
		||||
 | 
			
		||||
	// Get the hInstance
 | 
			
		||||
	HINSTANCE hInstance = GetModuleHandle(NULL);
 | 
			
		||||
 | 
			
		||||
	// Create the Window Class
 | 
			
		||||
	WNDCLASS wc = {};
 | 
			
		||||
	wc.lpfnWndProc = DefWindowProc;
 | 
			
		||||
	wc.lpszClassName = "BLAH WINDOW";
 | 
			
		||||
	wc.hInstance = hInstance;
 | 
			
		||||
	wc.lpfnWndProc = win32_window_procedure;
 | 
			
		||||
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
 | 
			
		||||
	wc.hIcon = NULL;
 | 
			
		||||
	wc.lpszMenuName = NULL;
 | 
			
		||||
	wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
 | 
			
		||||
	wc.cbClsExtra = 0;
 | 
			
		||||
	wc.cbWndExtra = 0;
 | 
			
		||||
 | 
			
		||||
	// Register the Window class
 | 
			
		||||
	RegisterClass(&wc);
 | 
			
		||||
 | 
			
		||||
	// Create the Window Instance
 | 
			
		||||
	g_platform.hwnd = CreateWindow("BLAH WINDOW", config.name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 640, 480, NULL, NULL, hInstance, NULL);
 | 
			
		||||
 | 
			
		||||
	// Failed to create the Window
 | 
			
		||||
	if (g_platform.hwnd == NULL)
 | 
			
		||||
	{
 | 
			
		||||
		Log::error("Window Creation Failed");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create the OpenGL device info
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		// Load the DLL
 | 
			
		||||
		g_platform.opengl_dll = LoadLibraryA("opengl32.dll");
 | 
			
		||||
		if (g_platform.opengl_dll == NULL)
 | 
			
		||||
		{
 | 
			
		||||
			Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Get the Windows GL functions we need
 | 
			
		||||
		g_platform.wglGetProcAddress = (void* (WINAPI*)(const char*))GetProcAddress(g_platform.opengl_dll, "wglGetProcAddress");
 | 
			
		||||
		g_platform.wglCreateContext = (HGLRC(WINAPI*) (HDC))GetProcAddress(g_platform.opengl_dll, "wglCreateContext");
 | 
			
		||||
		g_platform.wglDeleteContext = (BOOL(WINAPI*) (HGLRC))GetProcAddress(g_platform.opengl_dll, "wglDeleteContext");
 | 
			
		||||
		g_platform.wglMakeCurrent = (BOOL(WINAPI*) (HDC, HGLRC))GetProcAddress(g_platform.opengl_dll, "wglMakeCurrent");
 | 
			
		||||
 | 
			
		||||
		// TODO:
 | 
			
		||||
		// Allow the user to apply (some of) these values before instantiation.
 | 
			
		||||
		// Also applies to the SDL2 Backend
 | 
			
		||||
 | 
			
		||||
		PIXELFORMATDESCRIPTOR pfd =
 | 
			
		||||
		{
 | 
			
		||||
			sizeof(PIXELFORMATDESCRIPTOR),   // size of this pfd  
 | 
			
		||||
			1,                     // version number  
 | 
			
		||||
			PFD_DRAW_TO_WINDOW |   // support window  
 | 
			
		||||
			PFD_SUPPORT_OPENGL |   // support OpenGL  
 | 
			
		||||
			PFD_DOUBLEBUFFER,      // double buffered  
 | 
			
		||||
			PFD_TYPE_RGBA,         // RGBA type  
 | 
			
		||||
			32,                    // 32-bit color depth  
 | 
			
		||||
			0, 0, 0, 0, 0, 0,      // color bits ignored  
 | 
			
		||||
			0,                     // no alpha buffer  
 | 
			
		||||
			0,                     // shift bit ignored  
 | 
			
		||||
			0,                     // no accumulation buffer  
 | 
			
		||||
			0, 0, 0, 0,            // accum bits ignored  
 | 
			
		||||
			24,                    // 24-bit z-buffer  
 | 
			
		||||
			8,                     // 8-bit stencil buffer  
 | 
			
		||||
			0,                     // no auxiliary buffer  
 | 
			
		||||
			PFD_MAIN_PLANE,        // main layer  
 | 
			
		||||
			0,                     // reserved  
 | 
			
		||||
			0, 0, 0                // layer masks ignored  
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		HDC hdc = GetDC(g_platform.hwnd);
 | 
			
		||||
 | 
			
		||||
		// get the best available match of pixel format for the device context   
 | 
			
		||||
		int pixel_format = ChoosePixelFormat(hdc, &pfd);
 | 
			
		||||
 | 
			
		||||
		// make that the pixel format of the device context  
 | 
			
		||||
		SetPixelFormat(hdc, pixel_format, &pfd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Reset our game timer
 | 
			
		||||
	g_platform.start_time = std::chrono::system_clock::now().time_since_epoch();
 | 
			
		||||
 | 
			
		||||
	// Get Working Directory
 | 
			
		||||
	{
 | 
			
		||||
		TCHAR buffer[MAX_PATH];
 | 
			
		||||
		GetModuleFileName(NULL, buffer, MAX_PATH);
 | 
			
		||||
 | 
			
		||||
		auto normalized = Path::normalize(buffer);
 | 
			
		||||
		auto end = normalized.last_index_of('/');;
 | 
			
		||||
		if (end >= 0)
 | 
			
		||||
			g_platform.working_directory = FilePath(normalized.begin(), normalized.begin() + end);
 | 
			
		||||
		else
 | 
			
		||||
			g_platform.working_directory = normalized;
 | 
			
		||||
		g_platform.working_directory.append("/");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get Application User Directory
 | 
			
		||||
	{
 | 
			
		||||
		PWSTR path = NULL;
 | 
			
		||||
		if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, NULL, &path)))
 | 
			
		||||
		{
 | 
			
		||||
			auto end = path;
 | 
			
		||||
			while (*end != 0) end++;
 | 
			
		||||
 | 
			
		||||
			FilePath result;
 | 
			
		||||
			result.append_utf16((u16*)path, (u16*)end);
 | 
			
		||||
 | 
			
		||||
			g_platform.user_directory = Path::join(Path::normalize(result), config.name) + "/";
 | 
			
		||||
		}
 | 
			
		||||
		CoTaskMemFree(path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Not currently fullscreen
 | 
			
		||||
	g_platform.fullscreen = false;
 | 
			
		||||
 | 
			
		||||
	// Finished Platform Setup
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::ready()
 | 
			
		||||
{
 | 
			
		||||
	// Setup Window Size
 | 
			
		||||
	{
 | 
			
		||||
		auto scale = get_content_scale();
 | 
			
		||||
		int sw = (int)(App::config().width * scale);
 | 
			
		||||
		int sh = (int)(App::config().height * scale);
 | 
			
		||||
		set_size(sw, sh);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Display the game window
 | 
			
		||||
	ShowWindow(g_platform.hwnd, SW_SHOW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::shutdown()
 | 
			
		||||
{
 | 
			
		||||
	DestroyWindow(g_platform.hwnd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u64 Platform::ticks()
 | 
			
		||||
{
 | 
			
		||||
	// Todo:
 | 
			
		||||
	// This should account for whatever Time::ticks_per_second is set to
 | 
			
		||||
 | 
			
		||||
	auto now = std::chrono::system_clock::now().time_since_epoch();
 | 
			
		||||
	return std::chrono::duration_cast<std::chrono::microseconds>(now - g_platform.start_time).count();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LRESULT CALLBACK Blah::win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 | 
			
		||||
{
 | 
			
		||||
	switch (msg)
 | 
			
		||||
	{
 | 
			
		||||
	case WM_CLOSE:
 | 
			
		||||
	{
 | 
			
		||||
		auto& config = App::config();
 | 
			
		||||
		if (config.on_exit_request != nullptr)
 | 
			
		||||
			config.on_exit_request();
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	case WM_DESTROY:
 | 
			
		||||
		PostQuitMessage(0);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	// Mouse Input
 | 
			
		||||
	case WM_LBUTTONDOWN:
 | 
			
		||||
		g_platform.input_state->mouse.on_press(MouseButton::Left);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case WM_LBUTTONUP:
 | 
			
		||||
		g_platform.input_state->mouse.on_release(MouseButton::Left);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case WM_RBUTTONDOWN:
 | 
			
		||||
		g_platform.input_state->mouse.on_press(MouseButton::Right);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case WM_RBUTTONUP:
 | 
			
		||||
		g_platform.input_state->mouse.on_release(MouseButton::Right);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case WM_MBUTTONDOWN:
 | 
			
		||||
		g_platform.input_state->mouse.on_press(MouseButton::Middle);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case WM_MBUTTONUP:
 | 
			
		||||
		g_platform.input_state->mouse.on_release(MouseButton::Middle);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case WM_MOUSEMOVE:
 | 
			
		||||
		g_platform.input_state->mouse.on_move(Vec2((float)((u16)lParam), (float)(lParam >> 16)), Vec2::zero);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case WM_MOUSEWHEEL:
 | 
			
		||||
		g_platform.input_state->mouse.wheel = Point(0, GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	// Text Input
 | 
			
		||||
	case WM_UNICHAR:
 | 
			
		||||
		if (wParam == UNICODE_NOCHAR)
 | 
			
		||||
			return 1;
 | 
			
		||||
	case WM_CHAR:
 | 
			
		||||
	{
 | 
			
		||||
		String result;
 | 
			
		||||
		result.append((u32)wParam);
 | 
			
		||||
		if (result.length() > 0)
 | 
			
		||||
			g_platform.input_state->keyboard.text += result.cstr();
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Keyboard Input
 | 
			
		||||
	case WM_KEYDOWN:
 | 
			
		||||
	case WM_SYSKEYDOWN:
 | 
			
		||||
	{
 | 
			
		||||
		auto is_repeat = ((lParam & (1 << 30)) >> 30) == 1;
 | 
			
		||||
		if (!is_repeat)
 | 
			
		||||
		{
 | 
			
		||||
			auto key = Blah::win32_scancode_to_key(wParam, lParam);
 | 
			
		||||
			if (key != Key::Unknown)
 | 
			
		||||
				g_platform.input_state->keyboard.on_press(key);
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	case WM_KEYUP:
 | 
			
		||||
	case WM_SYSKEYUP:
 | 
			
		||||
	{
 | 
			
		||||
		auto key = Blah::win32_scancode_to_key(wParam, lParam);
 | 
			
		||||
		if (key != Key::Unknown)
 | 
			
		||||
			g_platform.input_state->keyboard.on_release(key);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return DefWindowProc(hwnd, msg, wParam, lParam);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::update(InputState& state)
 | 
			
		||||
{
 | 
			
		||||
	// store reference to input state
 | 
			
		||||
	g_platform.input_state = &state;
 | 
			
		||||
 | 
			
		||||
	// Catch & Dispatch Window Messages
 | 
			
		||||
	MSG msg;
 | 
			
		||||
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
 | 
			
		||||
	{
 | 
			
		||||
		TranslateMessage(&msg);
 | 
			
		||||
		DispatchMessage(&msg);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::sleep(int milliseconds)
 | 
			
		||||
{
 | 
			
		||||
	if (milliseconds > 0)
 | 
			
		||||
		Sleep(milliseconds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::present()
 | 
			
		||||
{
 | 
			
		||||
	if (App::renderer() == Renderer::OpenGL)
 | 
			
		||||
	{
 | 
			
		||||
		HDC hdc = GetDC(g_platform.hwnd);
 | 
			
		||||
		SwapBuffers(hdc);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* Platform::get_title()
 | 
			
		||||
{
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_title(const char* title)
 | 
			
		||||
{
 | 
			
		||||
	SetWindowText(g_platform.hwnd, title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::get_position(int* x, int* y)
 | 
			
		||||
{
 | 
			
		||||
	RECT rect;
 | 
			
		||||
	if (GetWindowRect(g_platform.hwnd, &rect))
 | 
			
		||||
	{
 | 
			
		||||
		*x = rect.left;
 | 
			
		||||
		*y = rect.top;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_position(int x, int y)
 | 
			
		||||
{
 | 
			
		||||
	int w, h;
 | 
			
		||||
	get_size(&w, &h);
 | 
			
		||||
	SetWindowPos(g_platform.hwnd, NULL, x, y, w, h, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_fullscreen(bool enabled)
 | 
			
		||||
{
 | 
			
		||||
	if (g_platform.fullscreen == enabled)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_platform.fullscreen = enabled;
 | 
			
		||||
 | 
			
		||||
	if (g_platform.fullscreen)
 | 
			
		||||
	{
 | 
			
		||||
		GetWindowRect(g_platform.hwnd, &g_platform.windowed_position);
 | 
			
		||||
 | 
			
		||||
		int w = GetSystemMetrics(SM_CXSCREEN);
 | 
			
		||||
		int h = GetSystemMetrics(SM_CYSCREEN);
 | 
			
		||||
		SetWindowLongPtr(g_platform.hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
 | 
			
		||||
		SetWindowPos(g_platform.hwnd, HWND_TOP, 0, 0, w, h, 0);
 | 
			
		||||
		ShowWindow(g_platform.hwnd, SW_SHOW);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		SetWindowLongPtr(g_platform.hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
 | 
			
		||||
		SetWindowPos(g_platform.hwnd, HWND_TOP,
 | 
			
		||||
			g_platform.windowed_position.left,
 | 
			
		||||
			g_platform.windowed_position.top,
 | 
			
		||||
			g_platform.windowed_position.right - g_platform.windowed_position.left,
 | 
			
		||||
			g_platform.windowed_position.bottom - g_platform.windowed_position.top, 0);
 | 
			
		||||
		ShowWindow(g_platform.hwnd, SW_SHOW);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::get_size(int* width, int* height)
 | 
			
		||||
{
 | 
			
		||||
	RECT rect;
 | 
			
		||||
	if (GetClientRect(g_platform.hwnd, &rect))
 | 
			
		||||
	{
 | 
			
		||||
		*width = rect.right - rect.left;
 | 
			
		||||
		*height = rect.bottom - rect.top;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::set_size(int width, int height)
 | 
			
		||||
{
 | 
			
		||||
	RECT client_rect;
 | 
			
		||||
	RECT border_rect;
 | 
			
		||||
 | 
			
		||||
	GetClientRect(g_platform.hwnd, &client_rect);
 | 
			
		||||
	GetWindowRect(g_platform.hwnd, &border_rect);
 | 
			
		||||
 | 
			
		||||
	int border_width = (border_rect.right - border_rect.left) - (client_rect.right - client_rect.left);
 | 
			
		||||
	int border_height = (border_rect.bottom - border_rect.top) - (client_rect.bottom - client_rect.top);
 | 
			
		||||
 | 
			
		||||
	SetWindowPos(g_platform.hwnd, NULL, border_rect.left, border_rect.top, width + border_width, height + border_height, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::get_draw_size(int* width, int* height)
 | 
			
		||||
{
 | 
			
		||||
	RECT rect;
 | 
			
		||||
	if (GetClientRect(g_platform.hwnd, &rect))
 | 
			
		||||
	{
 | 
			
		||||
		*width = rect.right - rect.left;
 | 
			
		||||
		*height = rect.bottom - rect.top;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float Platform::get_content_scale()
 | 
			
		||||
{
 | 
			
		||||
	// base value of Windows DPI
 | 
			
		||||
	// as seen here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow
 | 
			
		||||
	constexpr float base_raw_value = 96.0f;
 | 
			
		||||
 | 
			
		||||
	UINT raw_value = GetDpiForWindow(g_platform.hwnd);
 | 
			
		||||
 | 
			
		||||
	return (raw_value / base_raw_value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* Platform::app_path()
 | 
			
		||||
{
 | 
			
		||||
	return g_platform.working_directory.cstr();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* Platform::user_path()
 | 
			
		||||
{
 | 
			
		||||
	return g_platform.user_directory.cstr();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::file_exists(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return fs::is_regular_file(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::file_delete(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return fs::remove(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::dir_create(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	std::error_code error;
 | 
			
		||||
	return fs::create_directories(path, error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::dir_exists(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return fs::is_directory(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Platform::dir_delete(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return fs::remove_all(path) > 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::dir_enumerate(Vector<FilePath>& list, const char* path, bool recursive)
 | 
			
		||||
{
 | 
			
		||||
	if (fs::is_directory(path))
 | 
			
		||||
	{
 | 
			
		||||
		if (recursive)
 | 
			
		||||
		{
 | 
			
		||||
			for (auto& p : fs::recursive_directory_iterator(path))
 | 
			
		||||
				list.emplace_back(p.path().string().c_str());
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			for (auto& p : fs::directory_iterator(path))
 | 
			
		||||
				list.emplace_back(p.path().string().c_str());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::dir_explore(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FileRef Platform::file_open(const char* path, FileMode mode)
 | 
			
		||||
{
 | 
			
		||||
	int access = 0;
 | 
			
		||||
	int creation = 0;
 | 
			
		||||
 | 
			
		||||
	switch (mode)
 | 
			
		||||
	{
 | 
			
		||||
	case FileMode::OpenRead:
 | 
			
		||||
		access = GENERIC_READ;
 | 
			
		||||
		creation = OPEN_EXISTING;
 | 
			
		||||
		break;
 | 
			
		||||
	case FileMode::Open:
 | 
			
		||||
		access = GENERIC_READ | GENERIC_WRITE;
 | 
			
		||||
		creation = OPEN_EXISTING;
 | 
			
		||||
		break;
 | 
			
		||||
	case FileMode::CreateWrite:
 | 
			
		||||
		access = GENERIC_WRITE;
 | 
			
		||||
		creation = CREATE_ALWAYS;
 | 
			
		||||
		break;
 | 
			
		||||
	case FileMode::Create:
 | 
			
		||||
		access = GENERIC_READ | GENERIC_WRITE;
 | 
			
		||||
		creation = CREATE_ALWAYS;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto result = CreateFile(path, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
 | 
			
		||||
 | 
			
		||||
	if (result == INVALID_HANDLE_VALUE)
 | 
			
		||||
		return FileRef();
 | 
			
		||||
 | 
			
		||||
	return FileRef(new Win32File(result));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* Platform::gl_get_func(const char* name)
 | 
			
		||||
{
 | 
			
		||||
	// this check is taken from https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions
 | 
			
		||||
	// wglGetProcAddress doesn't always return valid pointers for some specific methods?
 | 
			
		||||
 | 
			
		||||
	void* p = (void*)g_platform.wglGetProcAddress(name);
 | 
			
		||||
	if ((p == 0) ||
 | 
			
		||||
		(p == (void*)0x1) ||
 | 
			
		||||
		(p == (void*)0x2) ||
 | 
			
		||||
		(p == (void*)0x3) ||
 | 
			
		||||
		(p == (void*)-1))
 | 
			
		||||
	{
 | 
			
		||||
		p = (void*)GetProcAddress(g_platform.opengl_dll, name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* Platform::gl_context_create()
 | 
			
		||||
{
 | 
			
		||||
	HDC hdc = GetDC(g_platform.hwnd);
 | 
			
		||||
	return g_platform.wglCreateContext(hdc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::gl_context_make_current(void* context)
 | 
			
		||||
{
 | 
			
		||||
	if (context != nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		HDC hdc = GetDC(g_platform.hwnd);
 | 
			
		||||
		g_platform.wglMakeCurrent(hdc, (HGLRC)context);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		g_platform.wglMakeCurrent(NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Platform::gl_context_destroy(void* context)
 | 
			
		||||
{
 | 
			
		||||
	g_platform.wglDeleteContext((HGLRC)context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* Platform::d3d11_get_hwnd()
 | 
			
		||||
{
 | 
			
		||||
	return g_platform.hwnd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Key Blah::win32_scancode_to_key(WPARAM wParam, LPARAM lParam)
 | 
			
		||||
{
 | 
			
		||||
	// scancodes
 | 
			
		||||
	switch ((lParam >> 16) & 0xFF)
 | 
			
		||||
	{
 | 
			
		||||
	case 1:   return Key::Escape;
 | 
			
		||||
	case 2:   return Key::D1;
 | 
			
		||||
	case 3:   return Key::D2;
 | 
			
		||||
	case 4:   return Key::D3;
 | 
			
		||||
	case 5:   return Key::D4;
 | 
			
		||||
	case 6:   return Key::D5;
 | 
			
		||||
	case 7:   return Key::D6;
 | 
			
		||||
	case 8:   return Key::D7;
 | 
			
		||||
	case 9:   return Key::D8;
 | 
			
		||||
	case 10:  return Key::D9;
 | 
			
		||||
	case 11:  return Key::D0;
 | 
			
		||||
	case 12:  return Key::Minus;
 | 
			
		||||
	case 13:  return Key::Equals;
 | 
			
		||||
	case 14:  return Key::Backspace;
 | 
			
		||||
	case 15:  return Key::Tab;
 | 
			
		||||
	case 16:  return Key::Q;
 | 
			
		||||
	case 17:  return Key::W;
 | 
			
		||||
	case 18:  return Key::E;
 | 
			
		||||
	case 19:  return Key::R;
 | 
			
		||||
	case 20:  return Key::T;
 | 
			
		||||
	case 21:  return Key::Y;
 | 
			
		||||
	case 22:  return Key::U;
 | 
			
		||||
	case 23:  return Key::I;
 | 
			
		||||
	case 24:  return Key::O;
 | 
			
		||||
	case 25:  return Key::P;
 | 
			
		||||
	case 26:  return Key::LeftBracket;
 | 
			
		||||
	case 27:  return Key::RightBracket;
 | 
			
		||||
	case 28:  return Key::Enter;
 | 
			
		||||
	case 29:  return Key::LeftControl;
 | 
			
		||||
	case 30:  return Key::A;
 | 
			
		||||
	case 31:  return Key::S;
 | 
			
		||||
	case 32:  return Key::D;
 | 
			
		||||
	case 33:  return Key::F;
 | 
			
		||||
	case 34:  return Key::G;
 | 
			
		||||
	case 35:  return Key::H;
 | 
			
		||||
	case 36:  return Key::J;
 | 
			
		||||
	case 37:  return Key::K;
 | 
			
		||||
	case 38:  return Key::L;
 | 
			
		||||
	case 39:  return Key::Semicolon;
 | 
			
		||||
	case 40:  return Key::Apostrophe;
 | 
			
		||||
	case 41:  return Key::Tilde;
 | 
			
		||||
	case 42:  return Key::LeftShift;
 | 
			
		||||
	case 43:  return Key::Backslash;
 | 
			
		||||
	case 44:  return Key::Z;
 | 
			
		||||
	case 45:  return Key::X;
 | 
			
		||||
	case 46:  return Key::C;
 | 
			
		||||
	case 47:  return Key::V;
 | 
			
		||||
	case 48:  return Key::B;
 | 
			
		||||
	case 49:  return Key::N;
 | 
			
		||||
	case 50:  return Key::M;
 | 
			
		||||
	case 51:  return Key::Comma;
 | 
			
		||||
	case 52:  return Key::Period;
 | 
			
		||||
	case 53:  return Key::Slash;
 | 
			
		||||
	case 54:  return Key::RightShift;
 | 
			
		||||
	case 55:  return Key::PrintScreen;
 | 
			
		||||
	case 56:  return Key::LeftAlt;
 | 
			
		||||
	case 57:  return Key::Space;
 | 
			
		||||
	case 58:  return Key::Capslock;
 | 
			
		||||
	case 59:  return Key::F1;
 | 
			
		||||
	case 60:  return Key::F2;
 | 
			
		||||
	case 61:  return Key::F3;
 | 
			
		||||
	case 62:  return Key::F4;
 | 
			
		||||
	case 63:  return Key::F5;
 | 
			
		||||
	case 64:  return Key::F6;
 | 
			
		||||
	case 65:  return Key::F7;
 | 
			
		||||
	case 66:  return Key::F8;
 | 
			
		||||
	case 67:  return Key::F9;
 | 
			
		||||
	case 68:  return Key::F10;
 | 
			
		||||
	case 71:  return Key::Home;
 | 
			
		||||
	case 72:  return Key::Up;
 | 
			
		||||
	case 73:  return Key::PageUp;
 | 
			
		||||
	case 74:  return Key::KeypadMinus;
 | 
			
		||||
	case 75:  return Key::Left;
 | 
			
		||||
	case 76:  return Key::Keypad5;
 | 
			
		||||
	case 77:  return Key::Right;
 | 
			
		||||
	case 78:  return Key::KeypadPlus;
 | 
			
		||||
	case 79:  return Key::End;
 | 
			
		||||
	case 80:  return Key::Down;
 | 
			
		||||
	case 81:  return Key::PageDown;
 | 
			
		||||
	case 82:  return Key::Insert;
 | 
			
		||||
	case 83:  return Key::Delete;
 | 
			
		||||
	case 87:  return Key::F11;
 | 
			
		||||
	case 88:  return Key::F12;
 | 
			
		||||
	case 89:  return Key::Pause;
 | 
			
		||||
	case 91:  return Key::LeftOS;
 | 
			
		||||
	case 92:  return Key::RightOS;
 | 
			
		||||
	case 93:  return Key::Application;
 | 
			
		||||
	case 100: return Key::F13;
 | 
			
		||||
	case 101: return Key::F14;
 | 
			
		||||
	case 102: return Key::F15;
 | 
			
		||||
	case 103: return Key::F16;
 | 
			
		||||
	case 104: return Key::F17;
 | 
			
		||||
	case 105: return Key::F18;
 | 
			
		||||
	case 106: return Key::F19;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// virtual keys
 | 
			
		||||
	switch (wParam)
 | 
			
		||||
	{
 | 
			
		||||
	case VK_CANCEL:      return Key::Cancel;
 | 
			
		||||
	case VK_BACK:        return Key::Backspace;
 | 
			
		||||
	case VK_TAB:         return Key::Tab;
 | 
			
		||||
	case VK_CLEAR:       return Key::Clear;
 | 
			
		||||
	case VK_RETURN:      return Key::Enter;
 | 
			
		||||
	case VK_SHIFT:       return Key::LeftShift;
 | 
			
		||||
	case VK_CONTROL:     return Key::LeftControl;
 | 
			
		||||
	case VK_PAUSE:       return Key::Pause;
 | 
			
		||||
	case VK_CAPITAL:     return Key::Capslock;
 | 
			
		||||
	case VK_ESCAPE:      return Key::Escape;
 | 
			
		||||
	case VK_SPACE:       return Key::Space;
 | 
			
		||||
	case VK_PRIOR:       return Key::Prior;
 | 
			
		||||
	case VK_END:         return Key::End;
 | 
			
		||||
	case VK_HOME:        return Key::Home;
 | 
			
		||||
	case VK_LEFT:        return Key::Left;
 | 
			
		||||
	case VK_UP:          return Key::Up;
 | 
			
		||||
	case VK_RIGHT:       return Key::Right;
 | 
			
		||||
	case VK_DOWN:        return Key::Down;
 | 
			
		||||
	case VK_SELECT:      return Key::Select;
 | 
			
		||||
	case VK_PRINT:       return Key::PrintScreen;
 | 
			
		||||
	case VK_EXECUTE:     return Key::Execute;
 | 
			
		||||
	case VK_SNAPSHOT:    return Key::PrintScreen;
 | 
			
		||||
	case VK_INSERT:      return Key::Insert;
 | 
			
		||||
	case VK_DELETE:      return Key::Delete;
 | 
			
		||||
	case VK_HELP:        return Key::Help;
 | 
			
		||||
	case VK_LWIN:        return Key::LeftOS;
 | 
			
		||||
	case VK_RWIN:        return Key::RightOS;
 | 
			
		||||
	case VK_APPS:        return Key::Application;
 | 
			
		||||
	case VK_SLEEP:       return Key::Unknown;
 | 
			
		||||
	case VK_NUMPAD0:     return Key::Keypad0;
 | 
			
		||||
	case VK_NUMPAD1:     return Key::Keypad1;
 | 
			
		||||
	case VK_NUMPAD2:     return Key::Keypad2;
 | 
			
		||||
	case VK_NUMPAD3:     return Key::Keypad3;
 | 
			
		||||
	case VK_NUMPAD4:     return Key::Keypad4;
 | 
			
		||||
	case VK_NUMPAD5:     return Key::Keypad5;
 | 
			
		||||
	case VK_NUMPAD6:     return Key::Keypad6;
 | 
			
		||||
	case VK_NUMPAD7:     return Key::Keypad7;
 | 
			
		||||
	case VK_NUMPAD8:     return Key::Keypad8;
 | 
			
		||||
	case VK_NUMPAD9:     return Key::Keypad9;
 | 
			
		||||
	case VK_F1:          return Key::F1;
 | 
			
		||||
	case VK_F2:          return Key::F2;
 | 
			
		||||
	case VK_F3:          return Key::F3;
 | 
			
		||||
	case VK_F4:          return Key::F4;
 | 
			
		||||
	case VK_F5:          return Key::F5;
 | 
			
		||||
	case VK_F6:          return Key::F6;
 | 
			
		||||
	case VK_F7:          return Key::F7;
 | 
			
		||||
	case VK_F8:          return Key::F8;
 | 
			
		||||
	case VK_F9:          return Key::F9;
 | 
			
		||||
	case VK_F10:         return Key::F10;
 | 
			
		||||
	case VK_F11:         return Key::F11;
 | 
			
		||||
	case VK_F12:         return Key::F12;
 | 
			
		||||
	case VK_F13:         return Key::F13;
 | 
			
		||||
	case VK_F14:         return Key::F14;
 | 
			
		||||
	case VK_F15:         return Key::F15;
 | 
			
		||||
	case VK_F16:         return Key::F16;
 | 
			
		||||
	case VK_F17:         return Key::F17;
 | 
			
		||||
	case VK_F18:         return Key::F18;
 | 
			
		||||
	case VK_F19:         return Key::F19;
 | 
			
		||||
	case VK_F20:         return Key::F20;
 | 
			
		||||
	case VK_F21:         return Key::F21;
 | 
			
		||||
	case VK_F22:         return Key::F22;
 | 
			
		||||
	case VK_F23:         return Key::F23;
 | 
			
		||||
	case VK_F24:         return Key::F24;
 | 
			
		||||
	case VK_NUMLOCK:     return Key::Numlock;
 | 
			
		||||
	case VK_LSHIFT:      return Key::LeftShift;
 | 
			
		||||
	case VK_RSHIFT:      return Key::RightShift;
 | 
			
		||||
	case VK_LCONTROL:    return Key::LeftControl;
 | 
			
		||||
	case VK_RCONTROL:    return Key::RightControl;
 | 
			
		||||
	case VK_VOLUME_MUTE: return Key::Mute;
 | 
			
		||||
	case VK_VOLUME_DOWN: return Key::VolumeDown;
 | 
			
		||||
	case VK_VOLUME_UP:   return Key::VolumeUp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Key::Unknown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // BLAH_PLATFORM_WIN32
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
#include <blah/streams/filestream.h>
 | 
			
		||||
#include <blah/common.h>
 | 
			
		||||
#include "../internal/platform_backend.h"
 | 
			
		||||
#include "../internal/platform.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
using namespace Blah;
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user