449 lines
12 KiB
C++
449 lines
12 KiB
C++
#include "pch.h"
|
|
#include "platform_windows/windows_platform_services.h"
|
|
|
|
#include "log.h"
|
|
#include "renderer_gl/opengl_capabilities.h"
|
|
|
|
#include <deque>
|
|
#include <map>
|
|
|
|
extern HWND hWnd;
|
|
extern std::deque<std::packaged_task<void()>> main_tasklist;
|
|
extern std::mutex main_task_mutex;
|
|
|
|
void destroy_window();
|
|
void async_lock();
|
|
void async_unlock();
|
|
void win32_async_swap();
|
|
void win32_renderdoc_frame_start();
|
|
void win32_renderdoc_frame_end();
|
|
void win32_update_fps(int frames);
|
|
void win32_update_stylus(float dt);
|
|
|
|
namespace {
|
|
|
|
static CONSOLE_SCREEN_BUFFER_INFO render_debug_console_info;
|
|
|
|
[[nodiscard]] GLenum debug_severity_notification() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::debug_severity_notification());
|
|
}
|
|
|
|
[[nodiscard]] GLenum debug_severity_low() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::debug_severity_low());
|
|
}
|
|
|
|
[[nodiscard]] GLenum debug_severity_medium() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::debug_severity_medium());
|
|
}
|
|
|
|
[[nodiscard]] GLenum debug_severity_high() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::debug_severity_high());
|
|
}
|
|
|
|
[[nodiscard]] GLenum debug_output_state() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::debug_output_state());
|
|
}
|
|
|
|
[[nodiscard]] GLenum debug_output_synchronous_state() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::debug_output_synchronous_state());
|
|
}
|
|
|
|
void handle_gl_callback(
|
|
GLenum source,
|
|
GLenum type,
|
|
GLuint id,
|
|
GLenum severity,
|
|
GLsizei length,
|
|
const GLchar* message,
|
|
const void* userParam)
|
|
{
|
|
(void)source;
|
|
(void)type;
|
|
(void)id;
|
|
(void)userParam;
|
|
|
|
static std::map<GLenum, WORD> colors = {
|
|
{ debug_severity_notification(), static_cast<WORD>(8) },
|
|
{ debug_severity_low(), static_cast<WORD>(8) },
|
|
{ debug_severity_medium(), static_cast<WORD>(FOREGROUND_GREEN | FOREGROUND_INTENSITY) },
|
|
{ debug_severity_high(), static_cast<WORD>(FOREGROUND_RED | FOREGROUND_INTENSITY) },
|
|
};
|
|
if (severity == debug_severity_high()
|
|
|| severity == debug_severity_medium()
|
|
|| severity == debug_severity_low())
|
|
{
|
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colors[severity]);
|
|
LOG("OPENGL: %.*s", length, message);
|
|
FlushConsoleInputBuffer(GetStdHandle(STD_OUTPUT_HANDLE));
|
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), render_debug_console_info.wAttributes);
|
|
#ifdef _DEBUG
|
|
if (severity == debug_severity_high())
|
|
__debugbreak();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void show_cursor(bool visible)
|
|
{
|
|
std::lock_guard<std::mutex> lock(main_task_mutex);
|
|
main_tasklist.emplace_back([=] {
|
|
if (visible)
|
|
while (ShowCursor(true) < 0);
|
|
else
|
|
while (ShowCursor(false) >= 0);
|
|
});
|
|
}
|
|
|
|
std::string clipboard_text()
|
|
{
|
|
std::string ret;
|
|
if (OpenClipboard(hWnd))
|
|
{
|
|
if (HANDLE h = GetClipboardData(CF_TEXT))
|
|
{
|
|
if (char* s = static_cast<char*>(GlobalLock(h)))
|
|
{
|
|
ret = s;
|
|
GlobalUnlock(h);
|
|
}
|
|
}
|
|
CloseClipboard();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool set_clipboard_text(const std::string& s)
|
|
{
|
|
bool success = false;
|
|
if (OpenClipboard(hWnd))
|
|
{
|
|
// owned by SetClipboardData
|
|
if (HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, s.size() + 1))
|
|
{
|
|
if (char* p = static_cast<char*>(GlobalLock(h)))
|
|
{
|
|
std::copy(s.begin(), s.end(), p);
|
|
p[s.size()] = 0;
|
|
GlobalUnlock(h);
|
|
success = true;
|
|
}
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_TEXT, h);
|
|
}
|
|
CloseClipboard();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
std::string open_file(const char* filter)
|
|
{
|
|
OPENFILENAMEA ofn;
|
|
char fileName[MAX_PATH] = "";
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = hWnd;
|
|
ofn.lpstrFilter = filter;
|
|
ofn.lpstrFile = fileName;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
|
|
ofn.lpstrDefExt = "";
|
|
ofn.lpstrInitialDir = "";
|
|
if (GetOpenFileNameA(&ofn) != NULL)
|
|
return fileName;
|
|
return "";
|
|
}
|
|
|
|
std::string save_file(const char* filter)
|
|
{
|
|
OPENFILENAMEA ofn;
|
|
char fileName[MAX_PATH] = "";
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = hWnd;
|
|
ofn.lpstrFilter = filter;
|
|
ofn.lpstrFile = fileName;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT;
|
|
ofn.lpstrDefExt = "";
|
|
ofn.lpstrInitialDir = "";
|
|
if (GetSaveFileNameA(&ofn) != NULL)
|
|
return fileName;
|
|
return "";
|
|
}
|
|
|
|
std::string open_directory()
|
|
{
|
|
BROWSEINFOA bi;
|
|
char Buffer[MAX_PATH];
|
|
ZeroMemory(Buffer, MAX_PATH);
|
|
ZeroMemory(&bi, sizeof(bi));
|
|
bi.hwndOwner = hWnd;
|
|
bi.pszDisplayName = Buffer;
|
|
bi.lpszTitle = "Title";
|
|
bi.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS | BIF_SHAREABLE;
|
|
LPCITEMIDLIST pFolder = SHBrowseForFolderA(&bi);
|
|
if (pFolder == NULL)
|
|
return "";
|
|
if (!SHGetPathFromIDListA(pFolder, Buffer))
|
|
return "";
|
|
return Buffer;
|
|
}
|
|
|
|
void invoke_selected_path(
|
|
const std::string& path,
|
|
const pp::platform::PickedPathCallback& callback)
|
|
{
|
|
if (!path.empty())
|
|
callback(path);
|
|
}
|
|
|
|
void ensure_directory(const std::string& path)
|
|
{
|
|
if (!PathFileExistsA(path.c_str()))
|
|
CreateDirectoryA(path.c_str(), NULL);
|
|
}
|
|
|
|
std::string build_supported_files_filter(const std::vector<std::string>& types)
|
|
{
|
|
std::string filter = "Supported Files (";
|
|
bool first_type = true;
|
|
for (const auto& t : types)
|
|
{
|
|
filter.append(std::string(first_type ? "" : " ,") + "*." + t);
|
|
first_type = false;
|
|
}
|
|
filter.append(")");
|
|
filter.push_back(0);
|
|
first_type = true;
|
|
for (const auto& t : types)
|
|
{
|
|
filter.append(std::string(first_type ? "" : ";") + "*." + t);
|
|
first_type = false;
|
|
}
|
|
filter.push_back(0);
|
|
return filter;
|
|
}
|
|
|
|
class WindowsPlatformServices final : public pp::platform::PlatformServices {
|
|
public:
|
|
[[nodiscard]] pp::platform::PlatformStoragePaths prepare_storage_paths() override
|
|
{
|
|
std::string data_path;
|
|
CHAR my_documents[MAX_PATH];
|
|
HRESULT result = SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, my_documents);
|
|
if (SUCCEEDED(result))
|
|
{
|
|
data_path = std::string(my_documents) + "\\PanoPainter";
|
|
ensure_directory(data_path);
|
|
}
|
|
else
|
|
{
|
|
CHAR path[MAX_PATH];
|
|
GetCurrentDirectoryA(sizeof(path), path);
|
|
data_path = path;
|
|
}
|
|
|
|
ensure_directory(data_path + "\\frames");
|
|
ensure_directory(data_path + "\\brushes");
|
|
ensure_directory(data_path + "\\brushes\\thumbs");
|
|
ensure_directory(data_path + "\\patterns");
|
|
ensure_directory(data_path + "\\patterns\\thumbs");
|
|
ensure_directory(data_path + "\\settings");
|
|
|
|
return {
|
|
data_path,
|
|
data_path,
|
|
data_path + "\\frames",
|
|
{},
|
|
};
|
|
}
|
|
|
|
void log_stacktrace() override
|
|
{
|
|
}
|
|
|
|
void trigger_crash_test() override
|
|
{
|
|
__debugbreak();
|
|
}
|
|
|
|
[[nodiscard]] std::string clipboard_text() override
|
|
{
|
|
return ::clipboard_text();
|
|
}
|
|
|
|
[[nodiscard]] bool set_clipboard_text(std::string_view text) override
|
|
{
|
|
return ::set_clipboard_text(std::string(text));
|
|
}
|
|
|
|
void set_cursor_visible(bool visible) override
|
|
{
|
|
show_cursor(visible);
|
|
}
|
|
|
|
void set_virtual_keyboard_visible(bool visible) override
|
|
{
|
|
(void)visible;
|
|
}
|
|
|
|
void attach_ui_thread() override
|
|
{
|
|
}
|
|
|
|
void detach_ui_thread() override
|
|
{
|
|
}
|
|
|
|
void acquire_render_context() override
|
|
{
|
|
async_lock();
|
|
glBindFramebuffer(
|
|
static_cast<GLenum>(pp::renderer::gl::framebuffer_target()),
|
|
static_cast<GLuint>(pp::renderer::gl::default_framebuffer_id()));
|
|
}
|
|
|
|
void release_render_context() override
|
|
{
|
|
async_unlock();
|
|
}
|
|
|
|
void present_render_context() override
|
|
{
|
|
win32_async_swap();
|
|
}
|
|
|
|
void bind_default_render_target() override
|
|
{
|
|
glBindFramebuffer(
|
|
static_cast<GLenum>(pp::renderer::gl::framebuffer_target()),
|
|
pp::renderer::gl::default_framebuffer_id());
|
|
}
|
|
|
|
void bind_main_render_target() override
|
|
{
|
|
bind_default_render_target();
|
|
}
|
|
|
|
void apply_render_platform_hints() override
|
|
{
|
|
glEnable(static_cast<GLenum>(pp::renderer::gl::program_point_size_state()));
|
|
glEnable(static_cast<GLenum>(pp::renderer::gl::line_smooth_state()));
|
|
}
|
|
|
|
void install_render_debug_callback() override
|
|
{
|
|
if (!glDebugMessageCallback)
|
|
return;
|
|
|
|
// colors: http://stackoverflow.com/questions/4053837/colorizing-text-in-the-console-with-c
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &render_debug_console_info);
|
|
glDebugMessageCallback(handle_gl_callback, nullptr);
|
|
glEnable(debug_output_state());
|
|
glEnable(debug_output_synchronous_state());
|
|
}
|
|
|
|
void begin_render_capture_frame() override
|
|
{
|
|
win32_renderdoc_frame_start();
|
|
}
|
|
|
|
void end_render_capture_frame() override
|
|
{
|
|
win32_renderdoc_frame_end();
|
|
}
|
|
|
|
[[nodiscard]] bool deletes_recorded_files_on_clear() override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void clear_recorded_files(std::string_view recording_path) override
|
|
{
|
|
(void)recording_path;
|
|
}
|
|
|
|
[[nodiscard]] bool enables_live_asset_reloading() override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void update_platform_frame(float delta_time_seconds) override
|
|
{
|
|
win32_update_stylus(delta_time_seconds);
|
|
}
|
|
|
|
void report_rendered_frames(int frames) override
|
|
{
|
|
win32_update_fps(frames);
|
|
}
|
|
|
|
void display_file(std::string_view path) override
|
|
{
|
|
(void)path;
|
|
}
|
|
|
|
void share_file(std::string_view path) override
|
|
{
|
|
(void)path;
|
|
}
|
|
|
|
void request_app_close() override
|
|
{
|
|
destroy_window();
|
|
}
|
|
|
|
void pick_image(pp::platform::PickedPathCallback callback) override
|
|
{
|
|
const std::string path = open_file("Image Files (*.jpg, *.png)\0*.jpg;*.png");
|
|
invoke_selected_path(path, callback);
|
|
}
|
|
|
|
void pick_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
|
|
{
|
|
const std::string filter = build_supported_files_filter(file_types);
|
|
const std::string path = open_file(filter.c_str());
|
|
invoke_selected_path(path, callback);
|
|
}
|
|
|
|
void pick_save_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
|
|
{
|
|
const std::string filter = build_supported_files_filter(file_types);
|
|
const std::string path = save_file(filter.c_str());
|
|
invoke_selected_path(path, callback);
|
|
}
|
|
|
|
void pick_directory(pp::platform::PickedPathCallback callback) override
|
|
{
|
|
const std::string path = open_directory();
|
|
invoke_selected_path(path, callback);
|
|
}
|
|
|
|
void save_prepared_file(
|
|
std::string_view path,
|
|
std::string_view suggested_name,
|
|
pp::platform::PreparedFileCallback callback) override
|
|
{
|
|
(void)suggested_name;
|
|
callback(std::string(path), false);
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
namespace pp::platform::windows {
|
|
|
|
PlatformServices& platform_services()
|
|
{
|
|
static WindowsPlatformServices services;
|
|
return services;
|
|
}
|
|
|
|
}
|