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
- 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,

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

View File

@@ -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

View File

@@ -239,6 +239,17 @@ struct LegacyCanvasDrawMergePostDrawExecution {
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 {
std::function<void()> set_active_texture_unit;
std::function<void()> enable_blend;

View File

@@ -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<kKey, int> 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<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> running{-1};
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
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<std::mutex> lock(main_task_mutex);
main_tasklist.emplace_back([=] {
auto& state = retained_state();
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);
});
}
@@ -158,73 +171,77 @@ void destroy_window()
void async_lock()
{
//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();
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<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;
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<std::mutex> _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<std::mutex> lock(main_task_mutex);
main_tasklist.emplace_back([] {
std::lock_guard<std::mutex> 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> vive;
bool win32_vr_start()
{
if (sandboxed)
auto& state = retained_state();
if (state.sandboxed)
return false;
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); };
if (!vive->Initialize())
state.vive = std::make_unique<Vive>();
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<std::mutex> lock(hmd_render_mutex);
std::unique_lock<std::mutex> 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<DLGPROC>(splash_proc));
state.splash_dialog = CreateDialog(state.hInst, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast<DLGPROC>(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<std::packaged_task<void()>> working_list;
{
std::lock_guard<std::mutex> lock(main_task_mutex);
working_list = std::move(main_tasklist);
std::lock_guard<std::mutex> 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();

View File

@@ -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);

View File

@@ -101,26 +101,55 @@ struct RetainedLegacyGlfwWindowHooks final {
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 = [] {
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<NSString*>* apple_file_types_array(const std::vector<std::string>& 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
}