mirror of
https://github.com/NoelFB/blah.git
synced 2024-11-25 16:18:57 +08:00
removing "backend" from internal namespaces
This commit is contained in:
parent
d7cef352a5
commit
9eca790f9b
|
@ -43,11 +43,11 @@ add_library(blah
|
||||||
src/streams/memorystream.cpp
|
src/streams/memorystream.cpp
|
||||||
src/streams/stream.cpp
|
src/streams/stream.cpp
|
||||||
|
|
||||||
src/internal/graphics_backend_gl.cpp
|
src/internal/graphics_gl.cpp
|
||||||
src/internal/graphics_backend_d3d11.cpp
|
src/internal/graphics_d3d11.cpp
|
||||||
src/internal/graphics_backend_dummy.cpp
|
src/internal/graphics_dummy.cpp
|
||||||
src/internal/platform_backend_sdl2.cpp
|
src/internal/platform_sdl2.cpp
|
||||||
src/internal/platform_backend_win32.cpp
|
src/internal/platform_win32.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(blah
|
target_include_directories(blah
|
||||||
|
|
70
src/app.cpp
70
src/app.cpp
|
@ -3,9 +3,9 @@
|
||||||
#include <blah/time.h>
|
#include <blah/time.h>
|
||||||
#include <blah/numerics/point.h>
|
#include <blah/numerics/point.h>
|
||||||
#include <blah/graphics/target.h>
|
#include <blah/graphics/target.h>
|
||||||
#include "internal/platform_backend.h"
|
#include "internal/platform.h"
|
||||||
#include "internal/graphics_backend.h"
|
#include "internal/graphics.h"
|
||||||
#include "internal/input_backend.h"
|
#include "internal/input.h"
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
|
@ -44,7 +44,7 @@ namespace
|
||||||
// TODO: allow a non-fixed step update?
|
// TODO: allow a non-fixed step update?
|
||||||
{
|
{
|
||||||
u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
|
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;
|
u64 time_diff = time_curr - time_last;
|
||||||
time_last = time_curr;
|
time_last = time_curr;
|
||||||
time_accumulator += time_diff;
|
time_accumulator += time_diff;
|
||||||
|
@ -53,9 +53,9 @@ namespace
|
||||||
while (time_accumulator < time_target)
|
while (time_accumulator < time_target)
|
||||||
{
|
{
|
||||||
int milliseconds = (int)(time_target - time_accumulator) / (Time::ticks_per_second / 1000);
|
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_diff = time_curr - time_last;
|
||||||
time_last = time_curr;
|
time_last = time_curr;
|
||||||
time_accumulator += time_diff;
|
time_accumulator += time_diff;
|
||||||
|
@ -88,10 +88,10 @@ namespace
|
||||||
Time::previous_seconds = Time::seconds;
|
Time::previous_seconds = Time::seconds;
|
||||||
Time::seconds += Time::delta;
|
Time::seconds += Time::delta;
|
||||||
|
|
||||||
InputBackend::update_state();
|
Input::update_state();
|
||||||
PlatformBackend::update(Input::state);
|
Platform::update(Input::state);
|
||||||
InputBackend::update_bindings();
|
Input::update_bindings();
|
||||||
GraphicsBackend::update();
|
Graphics::update();
|
||||||
|
|
||||||
if (app_config.on_update != nullptr)
|
if (app_config.on_update != nullptr)
|
||||||
app_config.on_update();
|
app_config.on_update();
|
||||||
|
@ -100,13 +100,13 @@ namespace
|
||||||
|
|
||||||
// render
|
// render
|
||||||
{
|
{
|
||||||
GraphicsBackend::before_render();
|
Graphics::before_render();
|
||||||
|
|
||||||
if (app_config.on_render != nullptr)
|
if (app_config.on_render != nullptr)
|
||||||
app_config.on_render();
|
app_config.on_render();
|
||||||
|
|
||||||
GraphicsBackend::after_render();
|
Graphics::after_render();
|
||||||
PlatformBackend::present();
|
Platform::present();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,31 +126,31 @@ bool App::run(const Config* c)
|
||||||
app_is_exiting = false;
|
app_is_exiting = false;
|
||||||
|
|
||||||
// initialize the system
|
// initialize the system
|
||||||
if (!PlatformBackend::init(app_config))
|
if (!Platform::init(app_config))
|
||||||
{
|
{
|
||||||
Log::error("Failed to initialize Platform module");
|
Log::error("Failed to initialize Platform module");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize graphics
|
// initialize graphics
|
||||||
if (!GraphicsBackend::init())
|
if (!Graphics::init())
|
||||||
{
|
{
|
||||||
Log::error("Failed to initialize Graphics module");
|
Log::error("Failed to initialize Graphics module");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// input
|
// input
|
||||||
InputBackend::init();
|
Input::init();
|
||||||
|
|
||||||
// startup
|
// startup
|
||||||
if (app_config.on_startup != nullptr)
|
if (app_config.on_startup != nullptr)
|
||||||
app_config.on_startup();
|
app_config.on_startup();
|
||||||
|
|
||||||
time_last = PlatformBackend::ticks();
|
time_last = Platform::ticks();
|
||||||
time_accumulator = 0;
|
time_accumulator = 0;
|
||||||
|
|
||||||
// display window
|
// display window
|
||||||
PlatformBackend::ready();
|
Platform::ready();
|
||||||
|
|
||||||
// Begin main loop
|
// Begin main loop
|
||||||
// Emscripten requires the main loop be separated into its own call
|
// 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)
|
if (app_config.on_shutdown != nullptr)
|
||||||
app_config.on_shutdown();
|
app_config.on_shutdown();
|
||||||
|
|
||||||
GraphicsBackend::shutdown();
|
Graphics::shutdown();
|
||||||
PlatformBackend::shutdown();
|
Platform::shutdown();
|
||||||
|
|
||||||
// clear static state
|
// clear static state
|
||||||
app_is_running = false;
|
app_is_running = false;
|
||||||
|
@ -195,46 +195,46 @@ const Config& App::config()
|
||||||
|
|
||||||
const char* App::path()
|
const char* App::path()
|
||||||
{
|
{
|
||||||
return PlatformBackend::app_path();
|
return Platform::app_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* App::user_path()
|
const char* App::user_path()
|
||||||
{
|
{
|
||||||
return PlatformBackend::user_path();
|
return Platform::user_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* App::get_title()
|
const char* App::get_title()
|
||||||
{
|
{
|
||||||
return PlatformBackend::get_title();
|
return Platform::get_title();
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::set_title(const char* title)
|
void App::set_title(const char* title)
|
||||||
{
|
{
|
||||||
PlatformBackend::set_title(title);
|
Platform::set_title(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
Point App::get_position()
|
Point App::get_position()
|
||||||
{
|
{
|
||||||
Point result;
|
Point result;
|
||||||
PlatformBackend::get_position(&result.x, &result.y);
|
Platform::get_position(&result.x, &result.y);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::set_position(Point point)
|
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 App::get_size()
|
||||||
{
|
{
|
||||||
Point result;
|
Point result;
|
||||||
PlatformBackend::get_size(&result.x, &result.y);
|
Platform::get_size(&result.x, &result.y);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::set_size(Point point)
|
void App::set_size(Point point)
|
||||||
{
|
{
|
||||||
PlatformBackend::set_size(point.x, point.y);
|
Platform::set_size(point.x, point.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
int App::width()
|
int App::width()
|
||||||
|
@ -250,35 +250,35 @@ int App::height()
|
||||||
int App::draw_width()
|
int App::draw_width()
|
||||||
{
|
{
|
||||||
int w, h;
|
int w, h;
|
||||||
PlatformBackend::get_draw_size(&w, &h);
|
Platform::get_draw_size(&w, &h);
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
int App::draw_height()
|
int App::draw_height()
|
||||||
{
|
{
|
||||||
int w, h;
|
int w, h;
|
||||||
PlatformBackend::get_draw_size(&w, &h);
|
Platform::get_draw_size(&w, &h);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
float App::content_scale()
|
float App::content_scale()
|
||||||
{
|
{
|
||||||
return PlatformBackend::get_content_scale();
|
return Platform::get_content_scale();
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::fullscreen(bool enabled)
|
void App::fullscreen(bool enabled)
|
||||||
{
|
{
|
||||||
PlatformBackend::set_fullscreen(enabled);
|
Platform::set_fullscreen(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer App::renderer()
|
Renderer App::renderer()
|
||||||
{
|
{
|
||||||
return GraphicsBackend::renderer();
|
return Graphics::renderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
const RendererFeatures& Blah::App::renderer_features()
|
const RendererFeatures& Blah::App::renderer_features()
|
||||||
{
|
{
|
||||||
return GraphicsBackend::features();
|
return Graphics::features();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -313,7 +313,7 @@ namespace
|
||||||
|
|
||||||
void clear(Color color, float depth, u8 stencil, ClearMask mask) override
|
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/filesystem.h>
|
||||||
#include <blah/streams/filestream.h>
|
#include <blah/streams/filestream.h>
|
||||||
#include "internal/platform_backend.h"
|
#include "internal/platform.h"
|
||||||
|
|
||||||
using namespace Blah;
|
using namespace Blah;
|
||||||
|
|
||||||
FileRef File::open(const FilePath& path, FileMode mode)
|
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)
|
bool File::exists(const FilePath& path)
|
||||||
{
|
{
|
||||||
return PlatformBackend::file_exists(path.cstr());
|
return Platform::file_exists(path.cstr());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool File::destroy(const FilePath& path)
|
bool File::destroy(const FilePath& path)
|
||||||
{
|
{
|
||||||
return PlatformBackend::file_delete(path.cstr());
|
return Platform::file_delete(path.cstr());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Directory::create(const FilePath& path)
|
bool Directory::create(const FilePath& path)
|
||||||
{
|
{
|
||||||
return PlatformBackend::dir_create(path.cstr());
|
return Platform::dir_create(path.cstr());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Directory::exists(const FilePath& path)
|
bool Directory::exists(const FilePath& path)
|
||||||
{
|
{
|
||||||
return PlatformBackend::dir_exists(path.cstr());
|
return Platform::dir_exists(path.cstr());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Directory::destroy(const FilePath& path)
|
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)
|
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;
|
Vector<FilePath> list;
|
||||||
|
|
||||||
// get files
|
// get files
|
||||||
PlatformBackend::dir_enumerate(list, path.cstr(), recursive);
|
Platform::dir_enumerate(list, path.cstr(), recursive);
|
||||||
|
|
||||||
// normalize path names
|
// normalize path names
|
||||||
for (auto& it : list)
|
for (auto& it : list)
|
||||||
|
@ -50,7 +50,7 @@ Vector<FilePath> Directory::enumerate(const FilePath& path, bool recursive)
|
||||||
|
|
||||||
void Directory::explore(const FilePath& path)
|
void Directory::explore(const FilePath& path)
|
||||||
{
|
{
|
||||||
PlatformBackend::dir_explore(path);
|
Platform::dir_explore(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePath Path::get_file_name(const FilePath& path)
|
FilePath Path::get_file_name(const FilePath& path)
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#include <blah/graphics/mesh.h>
|
#include <blah/graphics/mesh.h>
|
||||||
#include "../internal/graphics_backend.h"
|
#include "../internal/graphics.h"
|
||||||
|
|
||||||
using namespace Blah;
|
using namespace Blah;
|
||||||
|
|
||||||
|
|
||||||
MeshRef Mesh::create()
|
MeshRef Mesh::create()
|
||||||
{
|
{
|
||||||
return GraphicsBackend::create_mesh();
|
return Graphics::create_mesh();
|
||||||
}
|
}
|
||||||
|
|
||||||
VertexFormat::VertexFormat(std::initializer_list<VertexAttribute> attributes, int stride)
|
VertexFormat::VertexFormat(std::initializer_list<VertexAttribute> attributes, int stride)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include <blah/graphics/renderpass.h>
|
#include <blah/graphics/renderpass.h>
|
||||||
#include <blah/common.h>
|
#include <blah/common.h>
|
||||||
#include "../internal/graphics_backend.h"
|
#include "../internal/graphics.h"
|
||||||
|
|
||||||
using namespace Blah;
|
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));
|
pass.scissor = pass.scissor.overlap_rect(Rect(0, 0, draw_size.x, draw_size.y));
|
||||||
|
|
||||||
// perform render
|
// perform render
|
||||||
GraphicsBackend::render(pass);
|
Graphics::render(pass);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include <blah/graphics/shader.h>
|
#include <blah/graphics/shader.h>
|
||||||
#include <blah/app.h>
|
#include <blah/app.h>
|
||||||
#include "../internal/graphics_backend.h"
|
#include "../internal/graphics.h"
|
||||||
|
|
||||||
using namespace Blah;
|
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.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");
|
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
|
// validate the shader
|
||||||
if (shader)
|
if (shader)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include <blah/graphics/target.h>
|
#include <blah/graphics/target.h>
|
||||||
#include "../internal/graphics_backend.h"
|
#include "../internal/graphics.h"
|
||||||
|
|
||||||
using namespace Blah;
|
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(depth_count <= 1, "Target can only have 1 Depth/Stencil Texture");
|
||||||
BLAH_ASSERT(color_count <= Attachments::capacity - 1, "Exceeded maximum Color texture count");
|
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)
|
TextureRef& Target::texture(int index)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include <blah/images/image.h>
|
#include <blah/images/image.h>
|
||||||
#include <blah/streams/stream.h>
|
#include <blah/streams/stream.h>
|
||||||
#include <blah/common.h>
|
#include <blah/common.h>
|
||||||
#include "../internal/graphics_backend.h"
|
#include "../internal/graphics.h"
|
||||||
|
|
||||||
using namespace Blah;
|
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(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");
|
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)
|
if (tex && data != nullptr)
|
||||||
tex->set_data(data);
|
tex->set_data(data);
|
||||||
|
|
|
@ -5,19 +5,10 @@
|
||||||
|
|
||||||
using namespace Blah;
|
using namespace Blah;
|
||||||
|
|
||||||
#ifdef __CLANG__
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wunused-function"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define STBTT_STATIC
|
#define STBTT_STATIC
|
||||||
#define STB_TRUETYPE_IMPLEMENTATION
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
#include "../third_party/stb_truetype.h"
|
#include "../third_party/stb_truetype.h"
|
||||||
|
|
||||||
#ifdef __CLANG__
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
String get_font_name(stbtt_fontinfo* font, int nameId)
|
String get_font_name(stbtt_fontinfo* font, int nameId)
|
||||||
|
|
|
@ -15,18 +15,18 @@ using namespace Blah;
|
||||||
|
|
||||||
namespace
|
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);
|
i64 read = ((Stream*)user)->read(data, size);
|
||||||
return (int)read;
|
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);
|
((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 position = ((Stream*)user)->position();
|
||||||
i64 length = ((Stream*)user)->length();
|
i64 length = ((Stream*)user)->length();
|
||||||
|
@ -37,7 +37,7 @@ namespace
|
||||||
return 0;
|
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);
|
((Stream*)context)->write((char*)data, size);
|
||||||
}
|
}
|
||||||
|
@ -142,34 +142,30 @@ Image::~Image()
|
||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::from_stream(Stream& stream)
|
bool Image::from_stream(Stream& stream)
|
||||||
{
|
{
|
||||||
dispose();
|
dispose();
|
||||||
|
|
||||||
if (!stream.is_readable())
|
if (!stream.is_readable())
|
||||||
{
|
return false;
|
||||||
BLAH_ASSERT(false, "Unable to load image as the Stream was not readable");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stbi_io_callbacks callbacks;
|
stbi_io_callbacks callbacks;
|
||||||
callbacks.eof = Blah_STBI_Eof;
|
callbacks.eof = blaH_stbi_eof;
|
||||||
callbacks.read = Blah_STBI_Read;
|
callbacks.read = blah_stbi_read;
|
||||||
callbacks.skip = Blah_STBI_Skip;
|
callbacks.skip = blah_stbi_skip;
|
||||||
|
|
||||||
int x, y, comps;
|
int x, y, comps;
|
||||||
u8* data = stbi_load_from_callbacks(&callbacks, &stream, &x, &y, &comps, 4);
|
u8* data = stbi_load_from_callbacks(&callbacks, &stream, &x, &y, &comps, 4);
|
||||||
|
|
||||||
if (data == nullptr)
|
if (data == nullptr)
|
||||||
{
|
return false;
|
||||||
BLAH_ASSERT(false, "Unable to load image as the Stream's data was not a valid image");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_stbi_ownership = true;
|
m_stbi_ownership = true;
|
||||||
pixels = (Color*)data;
|
pixels = (Color*)data;
|
||||||
width = x;
|
width = x;
|
||||||
height = y;
|
height = y;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::dispose()
|
void Image::dispose()
|
||||||
|
@ -223,14 +219,8 @@ bool Image::save_png(Stream& stream) const
|
||||||
stbi_write_force_png_filter = 0;
|
stbi_write_force_png_filter = 0;
|
||||||
stbi_write_png_compression_level = 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;
|
return true;
|
||||||
else
|
|
||||||
Log::error("stbi_write_png_to_func failed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log::error("Cannot save Image, the Stream is not writable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -260,14 +250,8 @@ bool Image::save_jpg(Stream& stream, int quality) const
|
||||||
|
|
||||||
if (stream.is_writable())
|
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;
|
return true;
|
||||||
else
|
|
||||||
Log::error("stbi_write_jpg_to_func failed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log::error("Cannot save Image, the Stream is not writable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <blah/common.h>
|
#include <blah/common.h>
|
||||||
#include <blah/numerics/point.h>
|
#include <blah/numerics/point.h>
|
||||||
#include <blah/numerics/calc.h>
|
#include <blah/numerics/calc.h>
|
||||||
#include "internal/input_backend.h"
|
#include "internal/input.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
using namespace Blah;
|
using namespace Blah;
|
||||||
|
@ -21,7 +21,7 @@ namespace
|
||||||
InputState Blah::Input::state;
|
InputState Blah::Input::state;
|
||||||
InputState Blah::Input::last_state;
|
InputState Blah::Input::last_state;
|
||||||
|
|
||||||
void InputBackend::init()
|
void Input::init()
|
||||||
{
|
{
|
||||||
g_empty_controller.name = "Disconnected";
|
g_empty_controller.name = "Disconnected";
|
||||||
for (int i = 0; i < Input::max_controllers; i++)
|
for (int i = 0; i < Input::max_controllers; i++)
|
||||||
|
@ -34,7 +34,7 @@ void InputBackend::init()
|
||||||
g_sticks.dispose();
|
g_sticks.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputBackend::update_state()
|
void Input::update_state()
|
||||||
{
|
{
|
||||||
// cycle states
|
// cycle states
|
||||||
Input::last_state = Input::state;
|
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++)
|
for (int i = 0; i < g_buttons.size(); i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace Blah
|
||||||
{
|
{
|
||||||
// Graphics backend API used for rendering.
|
// Graphics backend API used for rendering.
|
||||||
// All rendering ends up going through here.
|
// All rendering ends up going through here.
|
||||||
namespace GraphicsBackend
|
namespace Graphics
|
||||||
{
|
{
|
||||||
// Initializes the graphics backend
|
// Initializes the graphics backend
|
||||||
bool init();
|
bool init();
|
|
@ -3,8 +3,8 @@
|
||||||
// TODO:
|
// TODO:
|
||||||
// Note the D3D11 Implementation is still a work-in-progress
|
// Note the D3D11 Implementation is still a work-in-progress
|
||||||
|
|
||||||
#include "../internal/graphics_backend.h"
|
#include "graphics.h"
|
||||||
#include "../internal/platform_backend.h"
|
#include "platform.h"
|
||||||
#include <blah/common.h>
|
#include <blah/common.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -673,7 +673,7 @@ namespace Blah
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool GraphicsBackend::init()
|
bool Graphics::init()
|
||||||
{
|
{
|
||||||
state = D3D11();
|
state = D3D11();
|
||||||
state.last_size = Point(App::draw_width(), App::draw_height());
|
state.last_size = Point(App::draw_width(), App::draw_height());
|
||||||
|
@ -687,7 +687,7 @@ namespace Blah
|
||||||
desc.SampleDesc.Quality = 0;
|
desc.SampleDesc.Quality = 0;
|
||||||
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||||
desc.BufferCount = 1;
|
desc.BufferCount = 1;
|
||||||
desc.OutputWindow = (HWND)PlatformBackend::d3d11_get_hwnd();
|
desc.OutputWindow = (HWND)Platform::d3d11_get_hwnd();
|
||||||
desc.Windowed = true;
|
desc.Windowed = true;
|
||||||
|
|
||||||
// Creation Flags
|
// Creation Flags
|
||||||
|
@ -755,12 +755,12 @@ namespace Blah
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer GraphicsBackend::renderer()
|
Renderer Graphics::renderer()
|
||||||
{
|
{
|
||||||
return Renderer::D3D11;
|
return Renderer::D3D11;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::shutdown()
|
void Graphics::shutdown()
|
||||||
{
|
{
|
||||||
// release cached objects
|
// release cached objects
|
||||||
for (auto& it : state.blend_cache)
|
for (auto& it : state.blend_cache)
|
||||||
|
@ -787,16 +787,16 @@ namespace Blah
|
||||||
state = D3D11();
|
state = D3D11();
|
||||||
}
|
}
|
||||||
|
|
||||||
const RendererFeatures& GraphicsBackend::features()
|
const RendererFeatures& Graphics::features()
|
||||||
{
|
{
|
||||||
return state.features;
|
return state.features;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::update()
|
void Graphics::update()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::before_render()
|
void Graphics::before_render()
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
|
@ -824,13 +824,13 @@ namespace Blah
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::after_render()
|
void Graphics::after_render()
|
||||||
{
|
{
|
||||||
auto hr = state.swap_chain->Present(1, 0);
|
auto hr = state.swap_chain->Present(1, 0);
|
||||||
BLAH_ASSERT(SUCCEEDED(hr), "Failed to Present swap chain");
|
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);
|
auto result = new D3D11_Texture(width, height, format, false);
|
||||||
|
|
||||||
|
@ -841,12 +841,12 @@ namespace Blah
|
||||||
return TextureRef();
|
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));
|
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);
|
auto result = new D3D11_Shader(data);
|
||||||
if (result->valid)
|
if (result->valid)
|
||||||
|
@ -856,12 +856,12 @@ namespace Blah
|
||||||
return ShaderRef();
|
return ShaderRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshRef GraphicsBackend::create_mesh()
|
MeshRef Graphics::create_mesh()
|
||||||
{
|
{
|
||||||
return MeshRef(new D3D11_Mesh());
|
return MeshRef(new D3D11_Mesh());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::render(const RenderPass& pass)
|
void Graphics::render(const RenderPass& pass)
|
||||||
{
|
{
|
||||||
auto ctx = state.context;
|
auto ctx = state.context;
|
||||||
auto mesh = (D3D11_Mesh*)pass.mesh.get();
|
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)
|
if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color)
|
||||||
{
|
{
|
|
@ -1,7 +1,7 @@
|
||||||
#if !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11))
|
#if !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11))
|
||||||
|
|
||||||
#include "../internal/graphics_backend.h"
|
#include "graphics.h"
|
||||||
#include "../internal/platform_backend.h"
|
#include "platform.h"
|
||||||
#include <blah/common.h>
|
#include <blah/common.h>
|
||||||
|
|
||||||
namespace Blah
|
namespace Blah
|
||||||
|
@ -155,58 +155,58 @@ namespace Blah
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool GraphicsBackend::init()
|
bool Graphics::init()
|
||||||
{
|
{
|
||||||
Log::info("Dummy Renderer");
|
Log::info("Dummy Renderer");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer GraphicsBackend::renderer()
|
Renderer Graphics::renderer()
|
||||||
{
|
{
|
||||||
return Renderer::None;
|
return Renderer::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::shutdown()
|
void Graphics::shutdown()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RendererFeatures& GraphicsBackend::features()
|
const RendererFeatures& Graphics::features()
|
||||||
{
|
{
|
||||||
static const RendererFeatures features{ false, true, 4096 };
|
static const RendererFeatures features{ false, true, 4096 };
|
||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::update() {}
|
void Graphics::update() {}
|
||||||
void GraphicsBackend::before_render() {}
|
void Graphics::before_render() {}
|
||||||
void GraphicsBackend::after_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));
|
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));
|
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));
|
return ShaderRef(new Dummy_Shader(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshRef GraphicsBackend::create_mesh()
|
MeshRef Graphics::create_mesh()
|
||||||
{
|
{
|
||||||
return MeshRef(new Dummy_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
|
#ifdef BLAH_GRAPHICS_OPENGL
|
||||||
|
|
||||||
#include "../internal/graphics_backend.h"
|
#include "graphics.h"
|
||||||
#include "../internal/platform_backend.h"
|
#include "platform.h"
|
||||||
#include <blah/common.h>
|
#include <blah/common.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -1091,21 +1091,21 @@ namespace Blah
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool GraphicsBackend::init()
|
bool Graphics::init()
|
||||||
{
|
{
|
||||||
gl = State();
|
gl = State();
|
||||||
|
|
||||||
// create gl context
|
// create gl context
|
||||||
gl.context = PlatformBackend::gl_context_create();
|
gl.context = Platform::gl_context_create();
|
||||||
if (gl.context == nullptr)
|
if (gl.context == nullptr)
|
||||||
{
|
{
|
||||||
Log::error("Failed to create OpenGL Context");
|
Log::error("Failed to create OpenGL Context");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
PlatformBackend::gl_context_make_current(gl.context);
|
Platform::gl_context_make_current(gl.context);
|
||||||
|
|
||||||
// bind opengl functions
|
// 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
|
GL_FUNCTIONS
|
||||||
#undef GL_FUNC
|
#undef GL_FUNC
|
||||||
|
|
||||||
|
@ -1143,27 +1143,27 @@ namespace Blah
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer GraphicsBackend::renderer()
|
Renderer Graphics::renderer()
|
||||||
{
|
{
|
||||||
return Renderer::OpenGL;
|
return Renderer::OpenGL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::shutdown()
|
void Graphics::shutdown()
|
||||||
{
|
{
|
||||||
PlatformBackend::gl_context_destroy(gl.context);
|
Platform::gl_context_destroy(gl.context);
|
||||||
gl.context = nullptr;
|
gl.context = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RendererFeatures& GraphicsBackend::features()
|
const RendererFeatures& Graphics::features()
|
||||||
{
|
{
|
||||||
return gl.features;
|
return gl.features;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::update() {}
|
void Graphics::update() {}
|
||||||
void GraphicsBackend::before_render() {}
|
void Graphics::before_render() {}
|
||||||
void GraphicsBackend::after_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);
|
auto resource = new OpenGL_Texture(width, height, format);
|
||||||
|
|
||||||
|
@ -1176,7 +1176,7 @@ namespace Blah
|
||||||
return TextureRef(resource);
|
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);
|
auto resource = new OpenGL_Target(width, height, attachments, attachmentCount);
|
||||||
|
|
||||||
|
@ -1189,7 +1189,7 @@ namespace Blah
|
||||||
return TargetRef(resource);
|
return TargetRef(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderRef GraphicsBackend::create_shader(const ShaderData* data)
|
ShaderRef Graphics::create_shader(const ShaderData* data)
|
||||||
{
|
{
|
||||||
auto resource = new OpenGL_Shader(data);
|
auto resource = new OpenGL_Shader(data);
|
||||||
|
|
||||||
|
@ -1202,7 +1202,7 @@ namespace Blah
|
||||||
return ShaderRef(resource);
|
return ShaderRef(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshRef GraphicsBackend::create_mesh()
|
MeshRef Graphics::create_mesh()
|
||||||
{
|
{
|
||||||
auto resource = new OpenGL_Mesh();
|
auto resource = new OpenGL_Mesh();
|
||||||
|
|
||||||
|
@ -1215,7 +1215,7 @@ namespace Blah
|
||||||
return MeshRef(resource);
|
return MeshRef(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsBackend::render(const RenderPass& pass)
|
void Graphics::render(const RenderPass& pass)
|
||||||
{
|
{
|
||||||
// Bind the Target
|
// Bind the Target
|
||||||
Point size;
|
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;
|
int clear = 0;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace Blah
|
namespace Blah
|
||||||
{
|
{
|
||||||
namespace InputBackend
|
namespace Input
|
||||||
{
|
{
|
||||||
// Initializes the Input State
|
// Initializes the Input State
|
||||||
void init();
|
void init();
|
|
@ -7,12 +7,9 @@
|
||||||
namespace Blah
|
namespace Blah
|
||||||
{
|
{
|
||||||
struct Config;
|
struct Config;
|
||||||
enum class FileMode;
|
|
||||||
|
|
||||||
namespace PlatformBackend
|
namespace Platform
|
||||||
{
|
{
|
||||||
typedef void* FileHandle;
|
|
||||||
|
|
||||||
// Initialize the System
|
// Initialize the System
|
||||||
bool init(const Config& config);
|
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/streams/filestream.h>
|
||||||
#include <blah/common.h>
|
#include <blah/common.h>
|
||||||
#include "../internal/platform_backend.h"
|
#include "../internal/platform.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
using namespace Blah;
|
using namespace Blah;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user