diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index fe74cbf7..627c3db9 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,23 @@ agent or engineer to remove them without reconstructing context from chat. ## 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 and VR worker coordination flags in `src/main.cpp` now use `std::atomic` instead of unsynchronized globals; retained Win32 entrypoint ownership, diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 6fdd30bf..f0f0213e 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -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 route through retained local GLFW callback hooks, and retained Apple ObjC 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 app shell. - `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, 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 - 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 rather than thin composition/binding surfaces. - `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 ownership model, the Windows VR device now has explicit `std::unique_ptr` 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`, explicit result/status objects, and a few concepts, but the live app still does not consistently express ownership, thread affinity, or renderer diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 2d4bc1c1..663073ea 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -143,6 +143,9 @@ Current slice: draw, heightmap draw, and current-mode draw now also route through `execute_legacy_canvas_draw_merge_post_draw(...)`, but broader canvas draw 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 now also route through `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` - Windows main-loop run-state and VR worker coordination flags in `main.cpp` 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 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 also route through retained local GLFW callback hooks instead of direct 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 reach are still open diff --git a/src/legacy_canvas_draw_merge_services.h b/src/legacy_canvas_draw_merge_services.h index 53f08855..b161cbf5 100644 --- a/src/legacy_canvas_draw_merge_services.h +++ b/src/legacy_canvas_draw_merge_services.h @@ -239,6 +239,17 @@ struct LegacyCanvasDrawMergePostDrawExecution { std::function draw_current_modes; }; +template +[[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 { std::function set_active_texture_unit; std::function enable_blend; diff --git a/src/main.cpp b/src/main.cpp index 0e45771e..f8cc2a74 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,31 +38,43 @@ #define WM_USER_WAKEUP (WM_USER + 2) LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); -HINSTANCE hInst; -HWND hWnd; -HDC hDC; -HGLRC hRC; -const wchar_t* className; -bool keys[256]; -std::mutex gl_mutex; -std::mutex async_mutex; -std::thread::id gl_thread; -std::map vkey_map; -static wchar_t window_title[512]; +struct RetainedState +{ + HINSTANCE hInst{}; + HWND hWnd{}; + HDC hDC{}; + HGLRC hRC{}; + const wchar_t* className{}; + bool keys[256]{}; + std::mutex gl_mutex; + std::mutex async_mutex; + std::thread::id gl_thread{}; + std::map vkey_map; + wchar_t window_title[512]{}; + std::jthread hmd_renderer; + int gl_count = 0; + std::deque> 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; + HWND splash_dialog{}; +}; + +RetainedState& retained_state() +{ + static RetainedState state; + return state; +} -std::jthread hmd_renderer; std::atomic vr_frames{0}; std::atomic running{-1}; std::atomic_bool vr_running{false}; -int gl_count = 0; -std::deque> 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 RENDERDOC_API_1_4_0* rdoc_api = NULL; bool win32_renderdoc_init() @@ -149,8 +161,9 @@ std::string GetLastErrorAsString() void destroy_window() { - std::lock_guard lock(main_task_mutex); - main_tasklist.emplace_back([=] { + auto& state = retained_state(); + std::lock_guard lock(state.main_task_mutex); + state.main_tasklist.emplace_back([hWnd = state.hWnd] { PostMessage(hWnd, WM_USER_CLOSE, 0, 0); }); } @@ -158,73 +171,77 @@ void destroy_window() void async_lock() { //std::lock_guard _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(); - bool ret = wglMakeCurrent(hDC, hRC); + state.gl_mutex.lock(); + bool ret = wglMakeCurrent(state.hDC, state.hRC); if (ret == false) LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str()); - gl_thread = std::this_thread::get_id(); + state.gl_thread = std::this_thread::get_id(); //LOG("lock"); } - gl_count++; + state.gl_count++; //assert(ret == true); } bool async_lock_try() { //std::lock_guard _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; - bool ret = wglMakeCurrent(hDC, hRC); + bool ret = wglMakeCurrent(state.hDC, state.hRC); if (ret == false) LOG("FAILED wglMakeCurrent: %s", GetLastErrorAsString().c_str()); - gl_thread = std::this_thread::get_id(); + state.gl_thread = std::this_thread::get_id(); //LOG("lock"); } - gl_count++; + state.gl_count++; //assert(ret == true); return true; } void win32_async_swap() { - SwapBuffers(hDC); + SwapBuffers(retained_state().hDC); //LOG("swap"); } void async_unlock() { //std::lock_guard _lock(async_mutex); - gl_count--; - if (gl_count == 0) + auto& state = retained_state(); + state.gl_count--; + if (state.gl_count == 0) { //LOG("unlock"); wglMakeCurrent(0, 0); - gl_mutex.unlock(); + state.gl_mutex.unlock(); } } void win32_update_stylus(float dt) { - timer_stylus += dt; - timer_ink_touch += dt; - timer_ink_pen += dt; + auto& state = retained_state(); + state.timer_stylus += 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_eraser = false; 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; 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; App::I->redraw = true; @@ -235,18 +252,19 @@ void win32_update_fps(int frames) { static wchar_t title_fps[512]; const int vr_fps = vr_frames.load(std::memory_order_relaxed); + auto& state = retained_state(); 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 - 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 lock(main_task_mutex); - main_tasklist.emplace_back([] { + std::lock_guard lock(state.main_task_mutex); + state.main_tasklist.emplace_back([hWnd = state.hWnd] { SetWindowText(hWnd, title_fps); }); } - PostMessage(hWnd, WM_USER_WAKEUP, 0, 0); + PostMessage(state.hWnd, WM_USER_WAKEUP, 0, 0); } int read_WMI_info() @@ -403,7 +421,7 @@ int read_WMI_info() if (get_int(clsObj, L"CodeIntegrityPolicyEnforcementStatus") > 0) { LOG("SANDBOX DETECTED"); - sandboxed = true; + retained_state().sandboxed = true; } SAFEARRAY *psaNames = NULL; @@ -491,7 +509,7 @@ static void SetupExceptionHandler() GetFullPathNameA(path.c_str(), MAX_PATH, abspath, NULL); static char message[4096]; 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(); }, 0); @@ -500,50 +518,49 @@ static void SetupExceptionHandler() // create a reverse map from kKey to VK_XXX void init_vk_map() { + auto& state = retained_state(); for (int k = 1; k < 256; k++) // ignore kKey::Unknown = 0 { for (int vk = 0; vk < 256; 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 { 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; bool win32_vr_start() { - if (sandboxed) + auto& state = retained_state(); + if (state.sandboxed) return false; - vive = std::make_unique(); - 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()) + state.vive = std::make_unique(); + state.vive->on_draw = [](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) { App::I->vr_draw(proj, view, pose); }; + if (!state.vive->Initialize()) { - vive.reset(); + state.vive.reset(); LOG("VR: failed to initialize vive"); return false; } - if (hmd_renderer.joinable()) + if (state.hmd_renderer.joinable()) { - hmd_renderer.request_stop(); - hmd_renderer.join(); + state.hmd_renderer.request_stop(); + state.hmd_renderer.join(); } - hmd_renderer = std::jthread([&](std::stop_token stop_token) { - if (!vive) + state.hmd_renderer = std::jthread([&state](std::stop_token stop_token) { + if (!state.vive) return; BT_SetTerminate(); @@ -551,9 +568,9 @@ bool win32_vr_start() App::I->has_vr = true; 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); - 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); App::I->render_task([] { @@ -564,9 +581,9 @@ bool win32_vr_start() auto t0 = GetTickCount64(); float one_sec_timer = 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 lock(hmd_render_mutex); + std::unique_lock lock(state.hmd_render_mutex); auto t1 = GetTickCount64(); float dt = (float)(t1 - t0) / 1000.0f; @@ -579,28 +596,28 @@ bool win32_vr_start() } frames++; - vive->Update(); - App::I->vr_active = vive->m_active; - App::I->vr_controllers[0] = vive->m_controllers[0]; - App::I->vr_head = vive->m_pose; + state.vive->Update(); + App::I->vr_active = state.vive->m_active; + App::I->vr_controllers[0] = state.vive->m_controllers[0]; + App::I->vr_head = state.vive->m_pose; 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([] { - vive->Draw(); + App::I->render_task([&state] { + state.vive->Draw(); }); } const int framerate = (1.f / target_tick_rate) * 1000; 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; } App::I->vr_active = false; App::I->has_vr = false; vr_running.store(false, std::memory_order_relaxed); - vive->Terminate(); + state.vive->Terminate(); LOG("hmd renderer terminated"); }); return true; @@ -608,23 +625,25 @@ bool win32_vr_start() void win32_vr_stop() { - if (vive) + auto& state = retained_state(); + if (state.vive) { vr_running.store(false, std::memory_order_relaxed); - if (hmd_renderer.joinable()) + if (state.hmd_renderer.joinable()) { - hmd_renderer.request_stop(); - hmd_renderer.join(); + state.hmd_renderer.request_stop(); + state.hmd_renderer.join(); } - vive->Terminate(); - vive.reset(); + state.vive->Terminate(); + state.vive.reset(); } } void win32_save_window_state() { + auto& state = retained_state(); WINDOWPLACEMENT p; - GetWindowPlacement(hWnd, &p); + GetWindowPlacement(state.hWnd, &p); pp::panopainter::set_legacy_window_preferences(p.showCmd, { p.rcNormalPosition.left, 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);; } -HWND splash_dialog; void splash_thread_loop() { + auto& state = retained_state(); BT_SetTerminate(); - splash_dialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast(splash_proc)); + state.splash_dialog = CreateDialog(state.hInst, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast(splash_proc)); MSG msg; while (GetMessage(&msg, 0, 0, 0) > 0) { - if (IsDialogMessage(splash_dialog, &msg)) + if (IsDialogMessage(state.splash_dialog, &msg)) { DispatchMessage(&msg); TranslateMessage(&msg); } } - DestroyWindow(splash_dialog); + DestroyWindow(state.splash_dialog); } 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) { + auto& state = retained_state(); WNDCLASS wc; PIXELFORMATDESCRIPTOR pfd; @@ -785,17 +805,17 @@ int main(int argc, char** argv) // Inizialize data structures to zero memset(&wc, 0, sizeof(wc)); - memset(&keys, 0, sizeof(keys)); + memset(&state.keys, 0, sizeof(state.keys)); memset(&pfd, 0, sizeof(pfd)); // Create the main window - hInst = GetModuleHandle(NULL); - className = L"EngineMain"; + state.hInst = GetModuleHandle(NULL); + state.className = L"EngineMain"; - wc.hInstance = hInst; + wc.hInstance = state.hInst; wc.lpfnWndProc = (WNDPROC)WndProc; - wc.lpszClassName = className; + wc.lpszClassName = state.className; wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.hCursor = LoadCursor(NULL, IDC_ARROW); @@ -833,9 +853,9 @@ int main(int argc, char** argv) { 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.bottom - clientRect.top), 0, 0, hInst, 0); + (float)(clientRect.bottom - clientRect.top), 0, 0, state.hInst, 0); // Setup GL Rendering Context pfd.nSize = sizeof(pfd); @@ -846,11 +866,11 @@ int main(int argc, char** argv) pfd.cDepthBits = 24; pfd.iLayerType = PFD_MAIN_PLANE; - hDC = GetDC(hWnd); - int pxfmt = ChoosePixelFormat(hDC, &pfd); - SetPixelFormat(hDC, pxfmt, &pfd); - hRC = wglCreateContext(hDC); // Create OpenGL 2.1 or less - wglMakeCurrent(hDC, hRC); + state.hDC = GetDC(state.hWnd); + int pxfmt = ChoosePixelFormat(state.hDC, &pfd); + SetPixelFormat(state.hDC, pxfmt, &pfd); + state.hRC = wglCreateContext(state.hDC); // Create OpenGL 2.1 or less + wglMakeCurrent(state.hDC, state.hRC); // Initialize extensions if (!gladLoadGL()) @@ -858,7 +878,7 @@ int main(int argc, char** argv) LOG("gladLoadGL() failed"); return 0; } - if (!gladLoadWGL(hDC)) + if (!gladLoadWGL(state.hDC)) { LOG("gladLoadWGL() failed"); return 0; @@ -882,7 +902,7 @@ int main(int argc, char** argv) #endif // USE_RENDERDOC 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()); // If supported create a 3.3 context @@ -892,18 +912,18 @@ int main(int argc, char** argv) UINT numFormat; wglMakeCurrent(NULL, NULL); - wglDeleteContext(hRC); - DestroyWindow(hWnd); + wglDeleteContext(state.hRC); + 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.bottom - clientRect.top), 0, 0, hInst, 0); + (float)(clientRect.bottom - clientRect.top), 0, 0, state.hInst, 0); - hDC = GetDC(hWnd); - wglChoosePixelFormatARB(hDC, wgl_config.pixel_format_attributes.data(), nullptr, 1, &pxfmt, &numFormat); - SetPixelFormat(hDC, pxfmt, &pfd); - hRC = wglCreateContextAttribsARB(hDC, NULL, wgl_config.context_attributes.data()); - wglMakeCurrent(hDC, hRC); + state.hDC = GetDC(state.hWnd); + wglChoosePixelFormatARB(state.hDC, wgl_config.pixel_format_attributes.data(), nullptr, 1, &pxfmt, &numFormat); + SetPixelFormat(state.hDC, pxfmt, &pfd); + state.hRC = wglCreateContextAttribsARB(state.hDC, NULL, wgl_config.context_attributes.data()); + wglMakeCurrent(state.hDC, state.hRC); } 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 - if (RegisterTouchWindow(hWnd, 0) == 0) + if (RegisterTouchWindow(state.hWnd, 0) == 0) { LOG("RegisterTouchWindow error: %s", GetLastErrorAsString().c_str()); } @@ -948,10 +968,10 @@ int main(int argc, char** argv) glad_set_post_callback(_post_call_callback); #endif - if (!sandboxed) + if (!state.sandboxed) { LOG("init WinTab"); - WacomTablet::I.init(hWnd); + WacomTablet::I.init(state.hWnd); } else { @@ -959,16 +979,16 @@ int main(int argc, char** argv) } LOG("change icon"); - SendMessage(hWnd, WM_SETICON, ICON_SMALL, + SendMessage(state.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1))); App::I->ui_sync(); { WINDOWPLACEMENT wp; - GetWindowPlacement(hWnd, &wp); + GetWindowPlacement(state.hWnd, &wp); wp.showCmd = show_cmd; - SetWindowPlacement(hWnd, &wp); + SetWindowPlacement(state.hWnd, &wp); //GetClientRect(hWnd, &clientRect); //App::I->width = clientRect.right - clientRect.left; //App::I->height = clientRect.bottom - clientRect.top; @@ -978,10 +998,10 @@ int main(int argc, char** argv) App::I->vr_start(); LOG("show main window"); - SetForegroundWindow(hWnd); + SetForegroundWindow(state.hWnd); //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()) dialog_thread.join(); @@ -1006,8 +1026,8 @@ int main(int argc, char** argv) { std::deque> working_list; { - std::lock_guard lock(main_task_mutex); - working_list = std::move(main_tasklist); + std::lock_guard lock(state.main_task_mutex); + working_list = std::move(state.main_tasklist); } if (!working_list.empty()) @@ -1023,12 +1043,13 @@ int main(int argc, char** argv) // Clean up WacomTablet::I.terminate(); - UnregisterClass(className, hInst); + UnregisterClass(state.className, state.hInst); LogRemote::I.stop(); } LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { + auto& state = retained_state(); static bool leftDown = false; static DWORD lastTime; static glm::vec2 lastPoint; @@ -1041,10 +1062,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { case WM_USER_CLOSE: running.store(0, std::memory_order_relaxed); - if (hmd_renderer.joinable()) + if (state.hmd_renderer.joinable()) { - hmd_renderer.request_stop(); - hmd_renderer.join(); + state.hmd_renderer.request_stop(); + state.hmd_renderer.join(); } App::I->runtime().ui_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)) { bool alt = keys[VK_MENU] & 0x80; - for (auto k : vkey_map) + for (auto k : state.vkey_map) { // ignore alt + tab if (alt && k.first == kKey::KeyTab) @@ -1118,7 +1139,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) case WT_PACKET: { App::I->set_stylus(); - timer_stylus = 0; + state.timer_stylus = 0; App::I->ui_task_async([=] { 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 // mark as handled to skip call to DefWindowProc //fHandled = TRUE; - timer_ink_touch = 0; + state.timer_ink_touch = 0; WacomTablet::I.m_ink_touch = true; 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 //fHandled = TRUE; //penInfo.pressure - timer_ink_pen = 0; + state.timer_ink_pen = 0; WacomTablet::I.m_ink_pen = true; WacomTablet::I.m_pen_pres = (float)penInfo.pressure / 1024.f; App::I->set_stylus(); diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index faa1b515..741c6e27 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -769,9 +769,7 @@ void NodeCanvas::draw() for (auto& mode : Canvas::modes[(int)kCanvasMode::Grid]) mode->on_Draw(ortho_proj, proj, camera); }, - .draw_heightmap = [&] { - App::I->grid->draw_heightmap(proj, camera, false); - }, + .draw_heightmap = pp::panopainter::make_legacy_canvas_draw_merge_heightmap_draw(App::I->grid, proj, camera), .draw_current_modes = [&] { for (auto& mode : *m_canvas->m_mode) mode->on_Draw(ortho_proj, proj, camera); diff --git a/src/platform_legacy/legacy_platform_services.cpp b/src/platform_legacy/legacy_platform_services.cpp index 5ae96551..58c29862 100644 --- a/src/platform_legacy/legacy_platform_services.cpp +++ b/src/platform_legacy/legacy_platform_services.cpp @@ -101,26 +101,55 @@ struct RetainedLegacyGlfwWindowHooks final { std::function 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 = [] { - RetainedLegacyGlfwWindowHooks retained; - GLFWwindow* const window = App::I->glfw_window; - retained.acquire_render_context = [window] { + static RetainedLegacyGlfwWindowState state = [] { + RetainedLegacyGlfwWindowState retained; + retained.window = App::I->glfw_window; + retained.hooks.acquire_render_context = [window = retained.window] { glfwMakeContextCurrent(window); }; - retained.present_render_context = [window] { + retained.hooks.present_render_context = [window = retained.window] { glfwSwapBuffers(window); }; - retained.request_app_close = [window] { + retained.hooks.request_app_close = [window = retained.window] { glfwSetWindowShouldClose(window, GLFW_TRUE); }; return retained; }(); - return hooks; + return state; +} + +[[nodiscard]] RetainedLegacyGlfwWindowHooks& active_legacy_glfw_window_hooks() +{ + return active_legacy_glfw_window_state().hooks; } #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__) [[nodiscard]] NSMutableArray* apple_file_types_array(const std::vector& file_types) { @@ -377,12 +406,7 @@ public: {}, }; #else - return { - App::I->data_path, - App::I->work_path, - App::I->rec_path, - App::I->tmp_path, - }; + return active_legacy_storage_paths().storage_paths; #endif }