Files
panopainter/src/platform_windows/windows_platform_services.cpp

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;
}
}