Retain Win32 entry state and trim canvas/platform seams

This commit is contained in:
2026-06-16 08:12:37 +02:00
parent 2948e907bc
commit 76ca2eea1a
7 changed files with 235 additions and 150 deletions

View File

@@ -18,6 +18,23 @@ agent or engineer to remove them without reconstructing context from chat.
## Reductions ## Reductions
- 2026-06-16: `DEBT-0003` was narrowed again. `src/main.cpp` now keeps Win32
window handles, GL/task mutex state, stylus timers, splash-dialog state, and
VR worker state behind one retained local state object instead of separate
file-scope globals; retained entrypoint orchestration, direct `App::I`
mutation, and broader platform/runtime coupling remain.
- 2026-06-16: `DEBT-0017` was narrowed again. Retained GLFW window hooks and
the non-Linux fallback storage-path return in
`src/platform_legacy/legacy_platform_services.cpp` now route through local
retained-state helpers instead of direct method-body `App::I` reads; the
retained platform fallback adapter and broader platform-to-app singleton
reach remain.
- 2026-06-16: `DEBT-0036` was narrowed again. `NodeCanvas` heightmap draw
callback setup now routes through
`make_legacy_canvas_draw_merge_heightmap_draw(...)` in
`src/legacy_canvas_draw_merge_services.h` instead of keeping that callback
body inline in `NodeCanvas::draw()`; broader canvas draw orchestration and
retained GL resource ownership remain.
- 2026-06-16: `DEBT-0003` was narrowed again. The Windows main-loop run-state - 2026-06-16: `DEBT-0003` was narrowed again. The Windows main-loop run-state
and VR worker coordination flags in `src/main.cpp` now use `std::atomic` and VR worker coordination flags in `src/main.cpp` now use `std::atomic`
instead of unsynchronized globals; retained Win32 entrypoint ownership, instead of unsynchronized globals; retained Win32 entrypoint ownership,

View File

@@ -104,7 +104,9 @@ Current architecture mismatches that must be treated as real blockers:
while Linux/Web GLFW render-context acquire/present and Linux app-close now while Linux/Web GLFW render-context acquire/present and Linux app-close now
route through retained local GLFW callback hooks, and retained Apple ObjC route through retained local GLFW callback hooks, and retained Apple ObjC
handles plus storage paths now sit behind one local `platform_legacy` handles plus storage paths now sit behind one local `platform_legacy`
helper instead of being re-read through `App::I` in each touched path. helper instead of being re-read through `App::I` in each touched path, with
the retained GLFW window hooks and fallback storage-path return now also
using local retained-state helpers instead of direct method-body reads.
- `src/platform_legacy/legacy_platform_services.*` is still part of the live - `src/platform_legacy/legacy_platform_services.*` is still part of the live
app shell. app shell.
- `pp_panopainter_ui` still depends on `pp_legacy_app`. - `pp_panopainter_ui` still depends on `pp_legacy_app`.
@@ -115,7 +117,8 @@ Current architecture mismatches that must be treated as real blockers:
checkerboard background setup now route through retained draw-merge helpers, checkerboard background setup now route through retained draw-merge helpers,
with the cache-to-screen checkerboard-plane callback setup also reduced and with the cache-to-screen checkerboard-plane callback setup also reduced and
the merged-path per-plane merged-texture draw plus the smoothing-mask face the merged-path per-plane merged-texture draw plus the smoothing-mask face
shader/draw pass now routed through the same retained helper family. shader/draw pass plus heightmap callback setup now routed through the same
retained helper family.
- `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files - `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files
rather than thin composition/binding surfaces. rather than thin composition/binding surfaces.
- `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use - `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
@@ -128,7 +131,9 @@ Current architecture mismatches that must be treated as real blockers:
also use owned `std::jthread` lifecycle, `LogRemote` now uses the same also use owned `std::jthread` lifecycle, `LogRemote` now uses the same
ownership model, the Windows VR device now has explicit `std::unique_ptr` ownership model, the Windows VR device now has explicit `std::unique_ptr`
ownership instead of raw global lifetime, and the Windows main-loop/VR ownership instead of raw global lifetime, and the Windows main-loop/VR
coordination flags now use `std::atomic` instead of unsynchronized globals. coordination flags now use `std::atomic` instead of unsynchronized globals,
while the main Win32 entrypoint now groups window/GL/task/VR state behind a
retained local state object instead of separate process-wide globals.
- Modern C++23 usage exists in extracted components, especially `std::span`, - Modern C++23 usage exists in extracted components, especially `std::span`,
explicit result/status objects, and a few concepts, but the live app still explicit result/status objects, and a few concepts, but the live app still
does not consistently express ownership, thread affinity, or renderer does not consistently express ownership, thread affinity, or renderer

View File

@@ -143,6 +143,9 @@ Current slice:
draw, heightmap draw, and current-mode draw now also route through draw, heightmap draw, and current-mode draw now also route through
`execute_legacy_canvas_draw_merge_post_draw(...)`, but broader canvas draw `execute_legacy_canvas_draw_merge_post_draw(...)`, but broader canvas draw
orchestration is still inline. orchestration is still inline.
- `NodeCanvas` heightmap draw callback setup now also routes through
`make_legacy_canvas_draw_merge_heightmap_draw(...)`, but the node still owns
current-mode traversal and broader post-draw orchestration.
- `NodeCanvas` smoothing-mask face shader setup plus per-face draw execution - `NodeCanvas` smoothing-mask face shader setup plus per-face draw execution
now also route through now also route through
`execute_legacy_canvas_draw_merge_smask_faces(...)`, but the node still owns `execute_legacy_canvas_draw_merge_smask_faces(...)`, but the node still owns
@@ -341,6 +344,9 @@ Current slice:
explicit stop requests instead of raw `std::thread` explicit stop requests instead of raw `std::thread`
- Windows main-loop run-state and VR worker coordination flags in `main.cpp` - Windows main-loop run-state and VR worker coordination flags in `main.cpp`
now use `std::atomic` ownership instead of unsynchronized globals now use `std::atomic` ownership instead of unsynchronized globals
- `main.cpp` Win32 window handles, GL task/mutex state, splash-dialog state,
stylus timers, and VR worker state now sit behind one retained local state
object instead of separate file-scope globals
- retained `App` composition, task call sites, and platform/runtime entrypoint - retained `App` composition, task call sites, and platform/runtime entrypoint
coupling are still not fully reduced behind the runtime contract coupling are still not fully reduced behind the runtime contract
@@ -616,6 +622,9 @@ Current slice:
- Linux/Web GLFW render-context acquire/present hooks and Linux app-close now - Linux/Web GLFW render-context acquire/present hooks and Linux app-close now
also route through retained local GLFW callback hooks instead of direct also route through retained local GLFW callback hooks instead of direct
method-body `App::I` access in `LegacyPlatformServices` method-body `App::I` access in `LegacyPlatformServices`
- retained GLFW window hooks and the non-Linux fallback storage-path return now
also route through retained local state helpers instead of reading `App::I`
directly in those method bodies
- retained Apple callback injection and broader `platform_legacy` singleton - retained Apple callback injection and broader `platform_legacy` singleton
reach are still open reach are still open

View File

@@ -239,6 +239,17 @@ struct LegacyCanvasDrawMergePostDrawExecution {
std::function<void()> draw_current_modes; std::function<void()> draw_current_modes;
}; };
template <typename GridT>
[[nodiscard]] inline auto make_legacy_canvas_draw_merge_heightmap_draw(
GridT* grid,
const glm::mat4& proj,
const glm::mat4& camera)
{
return [grid, proj, camera] {
grid->draw_heightmap(proj, camera, false);
};
}
struct LegacyCanvasDrawMergeSmaskFacesExecution { struct LegacyCanvasDrawMergeSmaskFacesExecution {
std::function<void()> set_active_texture_unit; std::function<void()> set_active_texture_unit;
std::function<void()> enable_blend; std::function<void()> enable_blend;

View File

@@ -38,31 +38,43 @@
#define WM_USER_WAKEUP (WM_USER + 2) #define WM_USER_WAKEUP (WM_USER + 2)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
HINSTANCE hInst; struct RetainedState
HWND hWnd; {
HDC hDC; HINSTANCE hInst{};
HGLRC hRC; HWND hWnd{};
const wchar_t* className; HDC hDC{};
bool keys[256]; HGLRC hRC{};
std::mutex gl_mutex; const wchar_t* className{};
std::mutex async_mutex; bool keys[256]{};
std::thread::id gl_thread; std::mutex gl_mutex;
std::map<kKey, int> vkey_map; std::mutex async_mutex;
static wchar_t window_title[512]; std::thread::id gl_thread{};
std::map<kKey, int> vkey_map;
wchar_t window_title[512]{};
std::jthread hmd_renderer;
int gl_count = 0;
std::deque<std::packaged_task<void()>> main_tasklist;
std::mutex main_task_mutex;
float timer_stylus = 0;
float timer_ink_touch = 0;
float timer_ink_pen = 0;
bool sandboxed = false;
std::mutex hmd_render_mutex;
std::condition_variable hmd_render_cv;
std::unique_ptr<Vive> vive;
HWND splash_dialog{};
};
RetainedState& retained_state()
{
static RetainedState state;
return state;
}
std::jthread hmd_renderer;
std::atomic<int> vr_frames{0}; std::atomic<int> vr_frames{0};
std::atomic<int> running{-1}; std::atomic<int> running{-1};
std::atomic_bool vr_running{false}; std::atomic_bool vr_running{false};
int gl_count = 0;
std::deque<std::packaged_task<void()>> main_tasklist;
std::mutex main_task_mutex;
float timer_stylus = 0;
float timer_ink_touch = 0;
float timer_ink_pen = 0;
bool sandboxed = false;
#ifdef USE_RENDERDOC #ifdef USE_RENDERDOC
RENDERDOC_API_1_4_0* rdoc_api = NULL; RENDERDOC_API_1_4_0* rdoc_api = NULL;
bool win32_renderdoc_init() bool win32_renderdoc_init()
@@ -149,8 +161,9 @@ std::string GetLastErrorAsString()
void destroy_window() void destroy_window()
{ {
std::lock_guard<std::mutex> lock(main_task_mutex); auto& state = retained_state();
main_tasklist.emplace_back([=] { std::lock_guard<std::mutex> lock(state.main_task_mutex);
state.main_tasklist.emplace_back([hWnd = state.hWnd] {
PostMessage(hWnd, WM_USER_CLOSE, 0, 0); PostMessage(hWnd, WM_USER_CLOSE, 0, 0);
}); });
} }
@@ -158,73 +171,77 @@ void destroy_window()
void async_lock() void async_lock()
{ {
//std::lock_guard<std::mutex> _lock(async_mutex); //std::lock_guard<std::mutex> _lock(async_mutex);
if (gl_count == 0 || gl_thread != std::this_thread::get_id()) auto& state = retained_state();
if (state.gl_count == 0 || state.gl_thread != std::this_thread::get_id())
{ {
gl_mutex.lock(); state.gl_mutex.lock();
bool ret = wglMakeCurrent(hDC, hRC); bool ret = wglMakeCurrent(state.hDC, state.hRC);
if (ret == false) if (ret == false)
LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str()); LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str());
gl_thread = std::this_thread::get_id(); state.gl_thread = std::this_thread::get_id();
//LOG("lock"); //LOG("lock");
} }
gl_count++; state.gl_count++;
//assert(ret == true); //assert(ret == true);
} }
bool async_lock_try() bool async_lock_try()
{ {
//std::lock_guard<std::mutex> _lock(async_mutex); //std::lock_guard<std::mutex> _lock(async_mutex);
if (gl_count == 0 || gl_thread != std::this_thread::get_id()) auto& state = retained_state();
if (state.gl_count == 0 || state.gl_thread != std::this_thread::get_id())
{ {
if (!gl_mutex.try_lock()) if (!state.gl_mutex.try_lock())
return false; return false;
bool ret = wglMakeCurrent(hDC, hRC); bool ret = wglMakeCurrent(state.hDC, state.hRC);
if (ret == false) if (ret == false)
LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str()); LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str());
gl_thread = std::this_thread::get_id(); state.gl_thread = std::this_thread::get_id();
//LOG("lock"); //LOG("lock");
} }
gl_count++; state.gl_count++;
//assert(ret == true); //assert(ret == true);
return true; return true;
} }
void win32_async_swap() void win32_async_swap()
{ {
SwapBuffers(hDC); SwapBuffers(retained_state().hDC);
//LOG("swap"); //LOG("swap");
} }
void async_unlock() void async_unlock()
{ {
//std::lock_guard<std::mutex> _lock(async_mutex); //std::lock_guard<std::mutex> _lock(async_mutex);
gl_count--; auto& state = retained_state();
if (gl_count == 0) state.gl_count--;
if (state.gl_count == 0)
{ {
//LOG("unlock"); //LOG("unlock");
wglMakeCurrent(0, 0); wglMakeCurrent(0, 0);
gl_mutex.unlock(); state.gl_mutex.unlock();
} }
} }
void win32_update_stylus(float dt) void win32_update_stylus(float dt)
{ {
timer_stylus += dt; auto& state = retained_state();
timer_ink_touch += dt; state.timer_stylus += dt;
timer_ink_pen += dt; state.timer_ink_touch += dt;
state.timer_ink_pen += dt;
if (timer_stylus > 0.1 && (WacomTablet::I.m_stylus || WacomTablet::I.m_eraser)) if (state.timer_stylus > 0.1 && (WacomTablet::I.m_stylus || WacomTablet::I.m_eraser))
{ {
WacomTablet::I.m_stylus = false; WacomTablet::I.m_stylus = false;
WacomTablet::I.m_eraser = false; WacomTablet::I.m_eraser = false;
App::I->redraw = true; App::I->redraw = true;
} }
if (timer_ink_pen > 0.1 && WacomTablet::I.m_ink_pen) if (state.timer_ink_pen > 0.1 && WacomTablet::I.m_ink_pen)
{ {
WacomTablet::I.m_ink_pen = false; WacomTablet::I.m_ink_pen = false;
App::I->redraw = true; App::I->redraw = true;
} }
if (timer_ink_touch > 0.1 && WacomTablet::I.m_ink_touch) if (state.timer_ink_touch > 0.1 && WacomTablet::I.m_ink_touch)
{ {
WacomTablet::I.m_ink_touch = false; WacomTablet::I.m_ink_touch = false;
App::I->redraw = true; App::I->redraw = true;
@@ -235,18 +252,19 @@ void win32_update_fps(int frames)
{ {
static wchar_t title_fps[512]; static wchar_t title_fps[512];
const int vr_fps = vr_frames.load(std::memory_order_relaxed); const int vr_fps = vr_frames.load(std::memory_order_relaxed);
auto& state = retained_state();
if (App::I->vr_active) if (App::I->vr_active)
swprintf_s(title_fps, L"%s - %d fps - %d vr fps", window_title, frames, vr_fps); swprintf_s(title_fps, L"%s - %d fps - %d vr fps", state.window_title, frames, vr_fps);
else else
swprintf_s(title_fps, L"%s - %d fps", window_title, frames); swprintf_s(title_fps, L"%s - %d fps", state.window_title, frames);
{ {
std::lock_guard<std::mutex> lock(main_task_mutex); std::lock_guard<std::mutex> lock(state.main_task_mutex);
main_tasklist.emplace_back([] { state.main_tasklist.emplace_back([hWnd = state.hWnd] {
SetWindowText(hWnd, title_fps); SetWindowText(hWnd, title_fps);
}); });
} }
PostMessage(hWnd, WM_USER_WAKEUP, 0, 0); PostMessage(state.hWnd, WM_USER_WAKEUP, 0, 0);
} }
int read_WMI_info() int read_WMI_info()
@@ -403,7 +421,7 @@ int read_WMI_info()
if (get_int(clsObj, L"CodeIntegrityPolicyEnforcementStatus") > 0) if (get_int(clsObj, L"CodeIntegrityPolicyEnforcementStatus") > 0)
{ {
LOG("SANDBOX DETECTED"); LOG("SANDBOX DETECTED");
sandboxed = true; retained_state().sandboxed = true;
} }
SAFEARRAY *psaNames = NULL; SAFEARRAY *psaNames = NULL;
@@ -491,7 +509,7 @@ static void SetupExceptionHandler()
GetFullPathNameA(path.c_str(), MAX_PATH, abspath, NULL); GetFullPathNameA(path.c_str(), MAX_PATH, abspath, NULL);
static char message[4096]; static char message[4096];
snprintf(message, sizeof(message), "File recovered in: %s", abspath); snprintf(message, sizeof(message), "File recovered in: %s", abspath);
MessageBoxA(hWnd, message, "File Recovery", MB_OK | MB_ICONWARNING); MessageBoxA(retained_state().hWnd, message, "File Recovery", MB_OK | MB_ICONWARNING);
} }
LogRemote::I.file_close(); LogRemote::I.file_close();
}, 0); }, 0);
@@ -500,50 +518,49 @@ static void SetupExceptionHandler()
// create a reverse map from kKey to VK_XXX // create a reverse map from kKey to VK_XXX
void init_vk_map() void init_vk_map()
{ {
auto& state = retained_state();
for (int k = 1; k < 256; k++) // ignore kKey::Unknown = 0 for (int k = 1; k < 256; k++) // ignore kKey::Unknown = 0
{ {
for (int vk = 0; vk < 256; vk++) for (int vk = 0; vk < 256; vk++)
{ {
if (k == (int)convert_key(vk)) if (k == (int)convert_key(vk))
{ {
if (vkey_map.find((kKey)k) == vkey_map.end()) if (state.vkey_map.find((kKey)k) == state.vkey_map.end())
{ {
vkey_map.insert({ (kKey)k, vk }); state.vkey_map.insert({ (kKey)k, vk });
} }
else else
{ {
LOG("KEY MAP COLLISION %d and %d maps to %d", LOG("KEY MAP COLLISION %d and %d maps to %d",
(int)vk, (int)vkey_map[(kKey)k], (int)k); (int)vk, (int)state.vkey_map[(kKey)k], (int)k);
} }
} }
} }
} }
} }
std::mutex hmd_render_mutex;
std::condition_variable hmd_render_cv;
std::unique_ptr<Vive> vive;
bool win32_vr_start() bool win32_vr_start()
{ {
if (sandboxed) auto& state = retained_state();
if (state.sandboxed)
return false; return false;
vive = std::make_unique<Vive>(); state.vive = std::make_unique<Vive>();
vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) { App::I->vr_draw(proj, view, pose); }; state.vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) { App::I->vr_draw(proj, view, pose); };
if (!vive->Initialize()) if (!state.vive->Initialize())
{ {
vive.reset(); state.vive.reset();
LOG("VR: failed to initialize vive"); LOG("VR: failed to initialize vive");
return false; return false;
} }
if (hmd_renderer.joinable()) if (state.hmd_renderer.joinable())
{ {
hmd_renderer.request_stop(); state.hmd_renderer.request_stop();
hmd_renderer.join(); state.hmd_renderer.join();
} }
hmd_renderer = std::jthread([&](std::stop_token stop_token) { state.hmd_renderer = std::jthread([&state](std::stop_token stop_token) {
if (!vive) if (!state.vive)
return; return;
BT_SetTerminate(); BT_SetTerminate();
@@ -551,9 +568,9 @@ bool win32_vr_start()
App::I->has_vr = true; App::I->has_vr = true;
vr_running.store(true, std::memory_order_relaxed); vr_running.store(true, std::memory_order_relaxed);
vive->on_analog_button = std::bind(&App::vr_analog, App::I, std::placeholders::_1, state.vive->on_analog_button = std::bind(&App::vr_analog, App::I, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
vive->on_button = std::bind(&App::vr_digital, App::I, std::placeholders::_1, state.vive->on_button = std::bind(&App::vr_digital, App::I, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
App::I->render_task([] { App::I->render_task([] {
@@ -564,9 +581,9 @@ bool win32_vr_start()
auto t0 = GetTickCount64(); auto t0 = GetTickCount64();
float one_sec_timer = 0; float one_sec_timer = 0;
int frames = 0; int frames = 0;
while (!stop_token.stop_requested() && vr_running.load(std::memory_order_relaxed) && running.load(std::memory_order_relaxed) == 1 && vive->Valid()) while (!stop_token.stop_requested() && vr_running.load(std::memory_order_relaxed) && running.load(std::memory_order_relaxed) == 1 && state.vive->Valid())
{ {
std::unique_lock<std::mutex> lock(hmd_render_mutex); std::unique_lock<std::mutex> lock(state.hmd_render_mutex);
auto t1 = GetTickCount64(); auto t1 = GetTickCount64();
float dt = (float)(t1 - t0) / 1000.0f; float dt = (float)(t1 - t0) / 1000.0f;
@@ -579,28 +596,28 @@ bool win32_vr_start()
} }
frames++; frames++;
vive->Update(); state.vive->Update();
App::I->vr_active = vive->m_active; App::I->vr_active = state.vive->m_active;
App::I->vr_controllers[0] = vive->m_controllers[0]; App::I->vr_controllers[0] = state.vive->m_controllers[0];
App::I->vr_head = vive->m_pose; App::I->vr_head = state.vive->m_pose;
App::I->vr_update(dt); App::I->vr_update(dt);
if (vr_running.load(std::memory_order_relaxed) && vive->m_active) if (vr_running.load(std::memory_order_relaxed) && state.vive->m_active)
{ {
App::I->render_task([] { App::I->render_task([&state] {
vive->Draw(); state.vive->Draw();
}); });
} }
const int framerate = (1.f / target_tick_rate) * 1000; const int framerate = (1.f / target_tick_rate) * 1000;
const int diff = framerate - (t1 - t0); const int diff = framerate - (t1 - t0);
//hmd_render_cv.wait_for(lock, std::chrono::milliseconds(diff)); //state.hmd_render_cv.wait_for(lock, std::chrono::milliseconds(diff));
t0 = t1; t0 = t1;
} }
App::I->vr_active = false; App::I->vr_active = false;
App::I->has_vr = false; App::I->has_vr = false;
vr_running.store(false, std::memory_order_relaxed); vr_running.store(false, std::memory_order_relaxed);
vive->Terminate(); state.vive->Terminate();
LOG("hmd renderer terminated"); LOG("hmd renderer terminated");
}); });
return true; return true;
@@ -608,23 +625,25 @@ bool win32_vr_start()
void win32_vr_stop() void win32_vr_stop()
{ {
if (vive) auto& state = retained_state();
if (state.vive)
{ {
vr_running.store(false, std::memory_order_relaxed); vr_running.store(false, std::memory_order_relaxed);
if (hmd_renderer.joinable()) if (state.hmd_renderer.joinable())
{ {
hmd_renderer.request_stop(); state.hmd_renderer.request_stop();
hmd_renderer.join(); state.hmd_renderer.join();
} }
vive->Terminate(); state.vive->Terminate();
vive.reset(); state.vive.reset();
} }
} }
void win32_save_window_state() void win32_save_window_state()
{ {
auto& state = retained_state();
WINDOWPLACEMENT p; WINDOWPLACEMENT p;
GetWindowPlacement(hWnd, &p); GetWindowPlacement(state.hWnd, &p);
pp::panopainter::set_legacy_window_preferences(p.showCmd, { pp::panopainter::set_legacy_window_preferences(p.showCmd, {
p.rcNormalPosition.left, p.rcNormalPosition.left,
p.rcNormalPosition.top, p.rcNormalPosition.top,
@@ -690,23 +709,23 @@ LRESULT CALLBACK splash_proc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lPara
return DefWindowProc(hWndDlg, Msg, wParam, lParam);; return DefWindowProc(hWndDlg, Msg, wParam, lParam);;
} }
HWND splash_dialog;
void splash_thread_loop() void splash_thread_loop()
{ {
auto& state = retained_state();
BT_SetTerminate(); BT_SetTerminate();
splash_dialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast<DLGPROC>(splash_proc)); state.splash_dialog = CreateDialog(state.hInst, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast<DLGPROC>(splash_proc));
MSG msg; MSG msg;
while (GetMessage(&msg, 0, 0, 0) > 0) while (GetMessage(&msg, 0, 0, 0) > 0)
{ {
if (IsDialogMessage(splash_dialog, &msg)) if (IsDialogMessage(state.splash_dialog, &msg))
{ {
DispatchMessage(&msg); DispatchMessage(&msg);
TranslateMessage(&msg); TranslateMessage(&msg);
} }
} }
DestroyWindow(splash_dialog); DestroyWindow(state.splash_dialog);
} }
BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle) BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle)
@@ -741,6 +760,7 @@ void _post_call_callback(const char* name, void* funcptr, int len_args, ...)
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
auto& state = retained_state();
WNDCLASS wc; WNDCLASS wc;
PIXELFORMATDESCRIPTOR pfd; PIXELFORMATDESCRIPTOR pfd;
@@ -785,17 +805,17 @@ int main(int argc, char** argv)
// Inizialize data structures to zero // Inizialize data structures to zero
memset(&wc, 0, sizeof(wc)); memset(&wc, 0, sizeof(wc));
memset(&keys, 0, sizeof(keys)); memset(&state.keys, 0, sizeof(state.keys));
memset(&pfd, 0, sizeof(pfd)); memset(&pfd, 0, sizeof(pfd));
// Create the main window // Create the main window
hInst = GetModuleHandle(NULL); state.hInst = GetModuleHandle(NULL);
className = L"EngineMain"; state.className = L"EngineMain";
wc.hInstance = hInst; wc.hInstance = state.hInst;
wc.lpfnWndProc = (WNDPROC)WndProc; wc.lpfnWndProc = (WNDPROC)WndProc;
wc.lpszClassName = className; wc.lpszClassName = state.className;
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hCursor = LoadCursor(NULL, IDC_ARROW);
@@ -833,9 +853,9 @@ int main(int argc, char** argv)
{ {
AdjustWindowRect(&clientRect, wnd_style, false); AdjustWindowRect(&clientRect, wnd_style, false);
} }
hWnd = CreateWindow(wc.lpszClassName, L"PanoPainter", wnd_style, clientPos.x, clientPos.y, state.hWnd = CreateWindow(wc.lpszClassName, L"PanoPainter", wnd_style, clientPos.x, clientPos.y,
(float)(clientRect.right - clientRect.left), (float)(clientRect.right - clientRect.left),
(float)(clientRect.bottom - clientRect.top), 0, 0, hInst, 0); (float)(clientRect.bottom - clientRect.top), 0, 0, state.hInst, 0);
// Setup GL Rendering Context // Setup GL Rendering Context
pfd.nSize = sizeof(pfd); pfd.nSize = sizeof(pfd);
@@ -846,11 +866,11 @@ int main(int argc, char** argv)
pfd.cDepthBits = 24; pfd.cDepthBits = 24;
pfd.iLayerType = PFD_MAIN_PLANE; pfd.iLayerType = PFD_MAIN_PLANE;
hDC = GetDC(hWnd); state.hDC = GetDC(state.hWnd);
int pxfmt = ChoosePixelFormat(hDC, &pfd); int pxfmt = ChoosePixelFormat(state.hDC, &pfd);
SetPixelFormat(hDC, pxfmt, &pfd); SetPixelFormat(state.hDC, pxfmt, &pfd);
hRC = wglCreateContext(hDC); // Create OpenGL 2.1 or less state.hRC = wglCreateContext(state.hDC); // Create OpenGL 2.1 or less
wglMakeCurrent(hDC, hRC); wglMakeCurrent(state.hDC, state.hRC);
// Initialize extensions // Initialize extensions
if (!gladLoadGL()) if (!gladLoadGL())
@@ -858,7 +878,7 @@ int main(int argc, char** argv)
LOG("gladLoadGL() failed"); LOG("gladLoadGL() failed");
return 0; return 0;
} }
if (!gladLoadWGL(hDC)) if (!gladLoadWGL(state.hDC))
{ {
LOG("gladLoadWGL() failed"); LOG("gladLoadWGL() failed");
return 0; return 0;
@@ -882,7 +902,7 @@ int main(int argc, char** argv)
#endif // USE_RENDERDOC #endif // USE_RENDERDOC
const auto renderer_name = std::string(runtime_info.renderer != nullptr ? runtime_info.renderer : ""); const auto renderer_name = std::string(runtime_info.renderer != nullptr ? runtime_info.renderer : "");
swprintf_s(window_title, L"PanoPainter %s (%s)", g_version_number_w, swprintf_s(state.window_title, L"PanoPainter %s (%s)", g_version_number_w,
str2wstr(renderer_name).c_str()); str2wstr(renderer_name).c_str());
// If supported create a 3.3 context // If supported create a 3.3 context
@@ -892,18 +912,18 @@ int main(int argc, char** argv)
UINT numFormat; UINT numFormat;
wglMakeCurrent(NULL, NULL); wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC); wglDeleteContext(state.hRC);
DestroyWindow(hWnd); DestroyWindow(state.hWnd);
hWnd = CreateWindow(wc.lpszClassName, window_title, wnd_style, clientPos.x, clientPos.y, state.hWnd = CreateWindow(wc.lpszClassName, state.window_title, wnd_style, clientPos.x, clientPos.y,
(float)(clientRect.right - clientRect.left), (float)(clientRect.right - clientRect.left),
(float)(clientRect.bottom - clientRect.top), 0, 0, hInst, 0); (float)(clientRect.bottom - clientRect.top), 0, 0, state.hInst, 0);
hDC = GetDC(hWnd); state.hDC = GetDC(state.hWnd);
wglChoosePixelFormatARB(hDC, wgl_config.pixel_format_attributes.data(), nullptr, 1, &pxfmt, &numFormat); wglChoosePixelFormatARB(state.hDC, wgl_config.pixel_format_attributes.data(), nullptr, 1, &pxfmt, &numFormat);
SetPixelFormat(hDC, pxfmt, &pfd); SetPixelFormat(state.hDC, pxfmt, &pfd);
hRC = wglCreateContextAttribsARB(hDC, NULL, wgl_config.context_attributes.data()); state.hRC = wglCreateContextAttribsARB(state.hDC, NULL, wgl_config.context_attributes.data());
wglMakeCurrent(hDC, hRC); wglMakeCurrent(state.hDC, state.hRC);
} }
else else
{ {
@@ -932,7 +952,7 @@ int main(int argc, char** argv)
} }
// link: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registertouchwindow // link: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registertouchwindow
if (RegisterTouchWindow(hWnd, 0) == 0) if (RegisterTouchWindow(state.hWnd, 0) == 0)
{ {
LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str()); LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str());
} }
@@ -948,10 +968,10 @@ int main(int argc, char** argv)
glad_set_post_callback(_post_call_callback); glad_set_post_callback(_post_call_callback);
#endif #endif
if (!sandboxed) if (!state.sandboxed)
{ {
LOG("init WinTab"); LOG("init WinTab");
WacomTablet::I.init(hWnd); WacomTablet::I.init(state.hWnd);
} }
else else
{ {
@@ -959,16 +979,16 @@ int main(int argc, char** argv)
} }
LOG("change icon"); LOG("change icon");
SendMessage(hWnd, WM_SETICON, ICON_SMALL, SendMessage(state.hWnd, WM_SETICON, ICON_SMALL,
(LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1))); (LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1)));
App::I->ui_sync(); App::I->ui_sync();
{ {
WINDOWPLACEMENT wp; WINDOWPLACEMENT wp;
GetWindowPlacement(hWnd, &wp); GetWindowPlacement(state.hWnd, &wp);
wp.showCmd = show_cmd; wp.showCmd = show_cmd;
SetWindowPlacement(hWnd, &wp); SetWindowPlacement(state.hWnd, &wp);
//GetClientRect(hWnd, &clientRect); //GetClientRect(hWnd, &clientRect);
//App::I->width = clientRect.right - clientRect.left; //App::I->width = clientRect.right - clientRect.left;
//App::I->height = clientRect.bottom - clientRect.top; //App::I->height = clientRect.bottom - clientRect.top;
@@ -978,10 +998,10 @@ int main(int argc, char** argv)
App::I->vr_start(); App::I->vr_start();
LOG("show main window"); LOG("show main window");
SetForegroundWindow(hWnd); SetForegroundWindow(state.hWnd);
//ShowWindow(hWnd, show_cmd); //ShowWindow(hWnd, show_cmd);
SendMessage(splash_dialog, WM_USER_CLOSE, 0, 0); SendMessage(state.splash_dialog, WM_USER_CLOSE, 0, 0);
if (dialog_thread.joinable()) if (dialog_thread.joinable())
dialog_thread.join(); dialog_thread.join();
@@ -1006,8 +1026,8 @@ int main(int argc, char** argv)
{ {
std::deque<std::packaged_task<void()>> working_list; std::deque<std::packaged_task<void()>> working_list;
{ {
std::lock_guard<std::mutex> lock(main_task_mutex); std::lock_guard<std::mutex> lock(state.main_task_mutex);
working_list = std::move(main_tasklist); working_list = std::move(state.main_tasklist);
} }
if (!working_list.empty()) if (!working_list.empty())
@@ -1023,12 +1043,13 @@ int main(int argc, char** argv)
// Clean up // Clean up
WacomTablet::I.terminate(); WacomTablet::I.terminate();
UnregisterClass(className, hInst); UnregisterClass(state.className, state.hInst);
LogRemote::I.stop(); LogRemote::I.stop();
} }
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{ {
auto& state = retained_state();
static bool leftDown = false; static bool leftDown = false;
static DWORD lastTime; static DWORD lastTime;
static glm::vec2 lastPoint; static glm::vec2 lastPoint;
@@ -1041,10 +1062,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{ {
case WM_USER_CLOSE: case WM_USER_CLOSE:
running.store(0, std::memory_order_relaxed); running.store(0, std::memory_order_relaxed);
if (hmd_renderer.joinable()) if (state.hmd_renderer.joinable())
{ {
hmd_renderer.request_stop(); state.hmd_renderer.request_stop();
hmd_renderer.join(); state.hmd_renderer.join();
} }
App::I->runtime().ui_thread_stop(); App::I->runtime().ui_thread_stop();
App::I->runtime().render_thread_stop(); App::I->runtime().render_thread_stop();
@@ -1093,7 +1114,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
if (GetKeyboardState(keys)) if (GetKeyboardState(keys))
{ {
bool alt = keys[VK_MENU] & 0x80; bool alt = keys[VK_MENU] & 0x80;
for (auto k : vkey_map) for (auto k : state.vkey_map)
{ {
// ignore alt + tab // ignore alt + tab
if (alt && k.first == kKey::KeyTab) if (alt && k.first == kKey::KeyTab)
@@ -1118,7 +1139,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
case WT_PACKET: case WT_PACKET:
{ {
App::I->set_stylus(); App::I->set_stylus();
timer_stylus = 0; state.timer_stylus = 0;
App::I->ui_task_async([=] { App::I->ui_task_async([=] {
WacomTablet::I.handle_message(hWnd, msg, wp, lp); WacomTablet::I.handle_message(hWnd, msg, wp, lp);
}); });
@@ -1318,7 +1339,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
// success, process touchInfo // success, process touchInfo
// mark as handled to skip call to DefWindowProc // mark as handled to skip call to DefWindowProc
//fHandled = TRUE; //fHandled = TRUE;
timer_ink_touch = 0; state.timer_ink_touch = 0;
WacomTablet::I.m_ink_touch = true; WacomTablet::I.m_ink_touch = true;
WacomTablet::I.m_pen_pres = 1; WacomTablet::I.m_pen_pres = 1;
} }
@@ -1335,7 +1356,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
// mark as handled to skip call to DefWindowProc // mark as handled to skip call to DefWindowProc
//fHandled = TRUE; //fHandled = TRUE;
//penInfo.pressure //penInfo.pressure
timer_ink_pen = 0; state.timer_ink_pen = 0;
WacomTablet::I.m_ink_pen = true; WacomTablet::I.m_ink_pen = true;
WacomTablet::I.m_pen_pres = (float)penInfo.pressure / 1024.f; WacomTablet::I.m_pen_pres = (float)penInfo.pressure / 1024.f;
App::I->set_stylus(); App::I->set_stylus();

View File

@@ -769,9 +769,7 @@ void NodeCanvas::draw()
for (auto& mode : Canvas::modes[(int)kCanvasMode::Grid]) for (auto& mode : Canvas::modes[(int)kCanvasMode::Grid])
mode->on_Draw(ortho_proj, proj, camera); mode->on_Draw(ortho_proj, proj, camera);
}, },
.draw_heightmap = [&] { .draw_heightmap = pp::panopainter::make_legacy_canvas_draw_merge_heightmap_draw(App::I->grid, proj, camera),
App::I->grid->draw_heightmap(proj, camera, false);
},
.draw_current_modes = [&] { .draw_current_modes = [&] {
for (auto& mode : *m_canvas->m_mode) for (auto& mode : *m_canvas->m_mode)
mode->on_Draw(ortho_proj, proj, camera); mode->on_Draw(ortho_proj, proj, camera);

View File

@@ -101,26 +101,55 @@ struct RetainedLegacyGlfwWindowHooks final {
std::function<void()> request_app_close; std::function<void()> request_app_close;
}; };
[[nodiscard]] RetainedLegacyGlfwWindowHooks& active_legacy_glfw_window_hooks() struct RetainedLegacyGlfwWindowState final {
decltype(App::I->glfw_window) window = nullptr;
RetainedLegacyGlfwWindowHooks hooks;
};
[[nodiscard]] RetainedLegacyGlfwWindowState& active_legacy_glfw_window_state()
{ {
static RetainedLegacyGlfwWindowHooks hooks = [] { static RetainedLegacyGlfwWindowState state = [] {
RetainedLegacyGlfwWindowHooks retained; RetainedLegacyGlfwWindowState retained;
GLFWwindow* const window = App::I->glfw_window; retained.window = App::I->glfw_window;
retained.acquire_render_context = [window] { retained.hooks.acquire_render_context = [window = retained.window] {
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
}; };
retained.present_render_context = [window] { retained.hooks.present_render_context = [window = retained.window] {
glfwSwapBuffers(window); glfwSwapBuffers(window);
}; };
retained.request_app_close = [window] { retained.hooks.request_app_close = [window = retained.window] {
glfwSetWindowShouldClose(window, GLFW_TRUE); glfwSetWindowShouldClose(window, GLFW_TRUE);
}; };
return retained; return retained;
}(); }();
return hooks; return state;
}
[[nodiscard]] RetainedLegacyGlfwWindowHooks& active_legacy_glfw_window_hooks()
{
return active_legacy_glfw_window_state().hooks;
} }
#endif #endif
struct RetainedLegacyStoragePaths final {
pp::platform::PlatformStoragePaths storage_paths;
};
[[nodiscard]] RetainedLegacyStoragePaths& active_legacy_storage_paths()
{
static RetainedLegacyStoragePaths state = [] {
RetainedLegacyStoragePaths retained;
retained.storage_paths = {
App::I->data_path,
App::I->work_path,
App::I->rec_path,
App::I->tmp_path,
};
return retained;
}();
return state;
}
#if defined(__IOS__) || defined(__OSX__) #if defined(__IOS__) || defined(__OSX__)
[[nodiscard]] NSMutableArray<NSString*>* apple_file_types_array(const std::vector<std::string>& file_types) [[nodiscard]] NSMutableArray<NSString*>* apple_file_types_array(const std::vector<std::string>& file_types)
{ {
@@ -377,12 +406,7 @@ public:
{}, {},
}; };
#else #else
return { return active_legacy_storage_paths().storage_paths;
App::I->data_path,
App::I->work_path,
App::I->rec_path,
App::I->tmp_path,
};
#endif #endif
} }