Bind Win32 tablet state in runtime shell

This commit is contained in:
2026-06-17 08:52:02 +02:00
parent 3930e70817
commit e9723276be
6 changed files with 107 additions and 80 deletions

View File

@@ -92,6 +92,14 @@ Current hotspot files:
Latest slice: Latest slice:
- The Win32 stylus and pointer-input path no longer reaches
`WacomTablet::I` directly inside
`src/platform_windows/windows_window_shell.cpp` or
`src/platform_windows/windows_stylus_input.cpp`; the live Windows runtime
shell now binds the active tablet explicitly through
`src/platform_windows/windows_runtime_shell.*`, leaving
`WacomTablet::I` only at the entry composition edge where the runtime shell
binds the active tablet instance.
- The remaining dense Windows bootstrap singleton pocket moved off - The remaining dense Windows bootstrap singleton pocket moved off
`App::I`: `setup_exception_handler(...)`, `initialize_main_window_startup_state(...)`, `App::I`: `setup_exception_handler(...)`, `initialize_main_window_startup_state(...)`,
and `_pre_call_callback(...)` now use explicit app/bound-runtime state and `_pre_call_callback(...)` now use explicit app/bound-runtime state

View File

@@ -56,6 +56,9 @@ Completed, blocked, and superseded task history moved to
- canvas async import/export/save/open now run through an owned in-file - canvas async import/export/save/open now run through an owned in-file
worker, but their retained progress execution is still not a clean runtime worker, but their retained progress execution is still not a clean runtime
service boundary service boundary
- the Win32 input path now binds the active `WacomTablet*` explicitly
through `windows_runtime_shell`, but that tablet binding still lives at a
composition edge instead of a broader runtime/platform-owned controller
- thread-affinity rules are enforced by convention and asserts instead of - thread-affinity rules are enforced by convention and asserts instead of
explicit runtime contracts explicit runtime contracts
- The UI ownership boundary is not finished: - The UI ownership boundary is not finished:
@@ -1202,6 +1205,13 @@ Why now:
platform-handle state on `App`, which blocks a real `pp_platform_*` shell split. platform-handle state on `App`, which blocks a real `pp_platform_*` shell split.
Current slice: Current slice:
- The Win32 stylus and pointer-input path no longer reaches
`WacomTablet::I` directly in
`src/platform_windows/windows_window_shell.cpp` or
`src/platform_windows/windows_stylus_input.cpp`;
`src/platform_windows/windows_runtime_shell.*` now binds the active tablet
explicitly and the touched consumers read it through that runtime-shell
binding instead of the process-global singleton.
- The remaining dense Windows bootstrap singleton pocket moved off - The remaining dense Windows bootstrap singleton pocket moved off
`App::I`: `setup_exception_handler(...)`, `App::I`: `setup_exception_handler(...)`,
`initialize_main_window_startup_state(...)`, and `_pre_call_callback(...)` `initialize_main_window_startup_state(...)`, and `_pre_call_callback(...)`

View File

@@ -20,6 +20,7 @@ namespace {
struct RetainedWindowsRuntimeState final { struct RetainedWindowsRuntimeState final {
App* app = nullptr; App* app = nullptr;
AppRuntime* runtime = nullptr; AppRuntime* runtime = nullptr;
WacomTablet* tablet = nullptr;
}; };
[[nodiscard]] RetainedWindowsRuntimeState& retained_runtime_state() [[nodiscard]] RetainedWindowsRuntimeState& retained_runtime_state()
@@ -56,10 +57,12 @@ void install_debug_gl_callbacks()
void initialize_wintab(HWND hWnd, bool sandboxed) void initialize_wintab(HWND hWnd, bool sandboxed)
{ {
auto* tablet = bound_wacom_tablet();
assert(tablet);
if (!sandboxed) if (!sandboxed)
{ {
LOG("init WinTab"); LOG("init WinTab");
WacomTablet::I.init(hWnd); tablet->init(hWnd);
} }
else else
{ {
@@ -110,7 +113,8 @@ void run_main_message_loop()
void shutdown_main_window_runtime(const MainWindowStartupState& startup, HINSTANCE hInst) void shutdown_main_window_runtime(const MainWindowStartupState& startup, HINSTANCE hInst)
{ {
WacomTablet::I.terminate(); if (auto* tablet = retained_runtime_state().tablet)
tablet->terminate();
UnregisterClass(startup.window_class.lpszClassName, hInst); UnregisterClass(startup.window_class.lpszClassName, hInst);
LogRemote::I.stop(); LogRemote::I.stop();
} }
@@ -137,6 +141,16 @@ AppRuntime* bound_runtime() noexcept
return retained_runtime_state().runtime; return retained_runtime_state().runtime;
} }
void bind_wacom_tablet(WacomTablet* tablet) noexcept
{
retained_runtime_state().tablet = tablet;
}
WacomTablet* bound_wacom_tablet() noexcept
{
return retained_runtime_state().tablet;
}
int run_main_application(int argc, char** argv) int run_main_application(int argc, char** argv)
{ {
auto& state = retained_state(); auto& state = retained_state();
@@ -146,6 +160,7 @@ int run_main_application(int argc, char** argv)
App::I = app; App::I = app;
bind_app(app); bind_app(app);
bind_runtime(&app->runtime()); bind_runtime(&app->runtime());
bind_wacom_tablet(&WacomTablet::I);
app->set_platform_services(&pp::platform::windows::platform_services()); app->set_platform_services(&pp::platform::windows::platform_services());
app->initLog(); app->initLog();
@@ -176,10 +191,12 @@ int run_main_application(int argc, char** argv)
case pp::platform::windows::MainStartupResult::GladLoadFailure: case pp::platform::windows::MainStartupResult::GladLoadFailure:
bind_app(nullptr); bind_app(nullptr);
bind_runtime(nullptr); bind_runtime(nullptr);
bind_wacom_tablet(nullptr);
return 0; return 0;
case pp::platform::windows::MainStartupResult::MissingCoreContextSupport: case pp::platform::windows::MainStartupResult::MissingCoreContextSupport:
bind_app(nullptr); bind_app(nullptr);
bind_runtime(nullptr); bind_runtime(nullptr);
bind_wacom_tablet(nullptr);
return -1; return -1;
} }
@@ -195,6 +212,7 @@ int run_main_application(int argc, char** argv)
app->cmd_convert(argv[2], argv[3]); app->cmd_convert(argv[2], argv[3]);
bind_app(nullptr); bind_app(nullptr);
bind_runtime(nullptr); bind_runtime(nullptr);
bind_wacom_tablet(nullptr);
return 0; return 0;
case const_hash("-vrmode"): case const_hash("-vrmode"):
start_in_vr = true; start_in_vr = true;
@@ -205,6 +223,7 @@ int run_main_application(int argc, char** argv)
} }
pp::platform::windows::run_main_window_runtime(startup, start_in_vr, splash); pp::platform::windows::run_main_window_runtime(startup, start_in_vr, splash);
bind_wacom_tablet(nullptr);
return 0; return 0;
} }

View File

@@ -5,6 +5,7 @@
class App; class App;
class AppRuntime; class AppRuntime;
class WacomTablet;
namespace pp::platform::windows { namespace pp::platform::windows {
@@ -14,5 +15,7 @@ void bind_app(App* app) noexcept;
[[nodiscard]] App* bound_app() noexcept; [[nodiscard]] App* bound_app() noexcept;
void bind_runtime(AppRuntime* runtime) noexcept; void bind_runtime(AppRuntime* runtime) noexcept;
[[nodiscard]] AppRuntime* bound_runtime() noexcept; [[nodiscard]] AppRuntime* bound_runtime() noexcept;
void bind_wacom_tablet(WacomTablet* tablet) noexcept;
[[nodiscard]] WacomTablet* bound_wacom_tablet() noexcept;
} }

View File

@@ -8,6 +8,7 @@
#include "wacom.h" #include "wacom.h"
namespace pp::platform::windows { namespace pp::platform::windows {
namespace { namespace {
struct StylusInputState final struct StylusInputState final
@@ -28,6 +29,13 @@ StylusInputState& stylus_input_state()
return state; return state;
} }
[[nodiscard]] WacomTablet& active_wacom_tablet()
{
auto* tablet = bound_wacom_tablet();
assert(tablet);
return *tablet;
}
} }
void initialize_stylus_input() void initialize_stylus_input()
@@ -55,24 +63,25 @@ void update_stylus_state(float dt)
{ {
auto& state = stylus_input_state(); auto& state = stylus_input_state();
auto* app = bound_app(); auto* app = bound_app();
auto& tablet = active_wacom_tablet();
state.timer_stylus += dt; state.timer_stylus += dt;
state.timer_ink_touch += dt; state.timer_ink_touch += dt;
state.timer_ink_pen += dt; state.timer_ink_pen += dt;
if (state.timer_stylus > 0.1f && (WacomTablet::I.m_stylus || WacomTablet::I.m_eraser)) if (state.timer_stylus > 0.1f && (tablet.m_stylus || tablet.m_eraser))
{ {
WacomTablet::I.m_stylus = false; tablet.m_stylus = false;
WacomTablet::I.m_eraser = false; tablet.m_eraser = false;
app->redraw = true; app->redraw = true;
} }
if (state.timer_ink_pen > 0.1f && WacomTablet::I.m_ink_pen) if (state.timer_ink_pen > 0.1f && tablet.m_ink_pen)
{ {
WacomTablet::I.m_ink_pen = false; tablet.m_ink_pen = false;
app->redraw = true; app->redraw = true;
} }
if (state.timer_ink_touch > 0.1f && WacomTablet::I.m_ink_touch) if (state.timer_ink_touch > 0.1f && tablet.m_ink_touch)
{ {
WacomTablet::I.m_ink_touch = false; tablet.m_ink_touch = false;
app->redraw = true; app->redraw = true;
} }
} }
@@ -87,6 +96,7 @@ void note_wintab_packet()
void handle_pointer_update_message(WPARAM wp) void handle_pointer_update_message(WPARAM wp)
{ {
auto& state = stylus_input_state(); auto& state = stylus_input_state();
auto& tablet = active_wacom_tablet();
if (!state.get_pointer_info) if (!state.get_pointer_info)
return; return;
@@ -109,8 +119,8 @@ void handle_pointer_update_message(WPARAM wp)
return; return;
state.timer_ink_touch = 0.0f; state.timer_ink_touch = 0.0f;
WacomTablet::I.m_ink_touch = true; tablet.m_ink_touch = true;
WacomTablet::I.m_pen_pres = 1.0f; tablet.m_pen_pres = 1.0f;
return; return;
case PT_PEN: case PT_PEN:
@@ -118,8 +128,8 @@ void handle_pointer_update_message(WPARAM wp)
return; return;
state.timer_ink_pen = 0.0f; state.timer_ink_pen = 0.0f;
WacomTablet::I.m_ink_pen = true; tablet.m_ink_pen = true;
WacomTablet::I.m_pen_pres = static_cast<float>(pen_info.pressure) / 1024.0f; tablet.m_pen_pres = static_cast<float>(pen_info.pressure) / 1024.0f;
bound_app()->set_stylus(); bound_app()->set_stylus();
return; return;

View File

@@ -14,6 +14,28 @@ void destroy_window();
namespace pp::platform::windows { namespace pp::platform::windows {
namespace {
[[nodiscard]] WacomTablet& active_wacom_tablet()
{
auto* tablet = bound_wacom_tablet();
assert(tablet);
return *tablet;
}
[[nodiscard]] kEventSource resolve_pointer_source(const WacomTablet& tablet, LPARAM extra)
{
if (tablet.m_ink_pen || tablet.m_ink_touch)
return tablet.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
auto pointer_source = tablet.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
return pointer_source;
}
}
void initialize_retained_input_state() void initialize_retained_input_state()
{ {
auto& state = retained_state(); auto& state = retained_state();
@@ -94,7 +116,7 @@ LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
platform_services().set_cursor_visible(true); platform_services().set_cursor_visible(true);
app->ui_task_async([app, &state, wp, lp] { app->ui_task_async([app, &state, wp, lp] {
int active = GET_WM_ACTIVATE_STATE(wp, lp); int active = GET_WM_ACTIVATE_STATE(wp, lp);
WacomTablet::I.set_focus(active); active_wacom_tablet().set_focus(active);
static BYTE keys[256]; static BYTE keys[256];
if (GetKeyboardState(keys)) if (GetKeyboardState(keys))
{ {
@@ -117,7 +139,7 @@ LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{ {
note_wintab_packet(); note_wintab_packet();
app->ui_task_async([=] { app->ui_task_async([=] {
WacomTablet::I.handle_message(hWnd, msg, wp, lp); active_wacom_tablet().handle_message(hWnd, msg, wp, lp);
}); });
break; break;
} }
@@ -149,19 +171,11 @@ LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
lastPoint = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) }; lastPoint = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) };
{ {
auto pt = lastPoint; auto pt = lastPoint;
app->ui_task_async([app, pt, extra, p = WacomTablet::I.get_pressure()] { auto& tablet = active_wacom_tablet();
kEventSource pointer_source; app->ui_task_async([app, pt, extra, p = tablet.get_pressure()] {
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch) auto& ui_tablet = active_wacom_tablet();
{ app->mouse_move((float)pt.x, (float)pt.y, p,
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch; resolve_pointer_source(ui_tablet, extra), ui_tablet.m_eraser);
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
app->mouse_move((float)pt.x, (float)pt.y, p, pointer_source, WacomTablet::I.m_eraser);
}); });
} }
break; break;
@@ -169,19 +183,11 @@ LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{ {
SetCapture(hWnd); SetCapture(hWnd);
auto pt = lastPoint; auto pt = lastPoint;
app->ui_task_async([app, pt, extra, p = WacomTablet::I.get_pressure()] { auto& tablet = active_wacom_tablet();
kEventSource pointer_source; app->ui_task_async([app, pt, extra, p = tablet.get_pressure()] {
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch) auto& ui_tablet = active_wacom_tablet();
{ app->mouse_down(0, (float)pt.x, (float)pt.y, p,
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch; resolve_pointer_source(ui_tablet, extra), ui_tablet.m_eraser);
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
app->mouse_down(0, (float)pt.x, (float)pt.y, p, pointer_source, WacomTablet::I.m_eraser);
}); });
} }
break; break;
@@ -190,19 +196,10 @@ LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
ReleaseCapture(); ReleaseCapture();
auto pt = lastPoint; auto pt = lastPoint;
app->ui_task_async([app, pt, extra] { app->ui_task_async([app, pt, extra] {
WacomTablet::I.reset_pressure(); auto& tablet = active_wacom_tablet();
kEventSource pointer_source; tablet.reset_pressure();
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch) app->mouse_up(0, (float)pt.x, (float)pt.y,
{ resolve_pointer_source(tablet, extra), tablet.m_eraser);
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
app->mouse_up(0, (float)pt.x, (float)pt.y, pointer_source, WacomTablet::I.m_eraser);
}); });
} }
break; break;
@@ -211,18 +208,8 @@ LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
SetCapture(hWnd); SetCapture(hWnd);
auto pt = lastPoint; auto pt = lastPoint;
app->ui_task_async([app, pt, extra] { app->ui_task_async([app, pt, extra] {
kEventSource pointer_source; const auto& tablet = active_wacom_tablet();
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch) app->mouse_down(1, (float)pt.x, (float)pt.y, 1.f, resolve_pointer_source(tablet, extra), 0);
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
app->mouse_down(1, (float)pt.x, (float)pt.y, 1.f, pointer_source, 0);
}); });
} }
break; break;
@@ -231,18 +218,8 @@ LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
ReleaseCapture(); ReleaseCapture();
auto pt = lastPoint; auto pt = lastPoint;
app->ui_task_async([app, pt, extra] { app->ui_task_async([app, pt, extra] {
kEventSource pointer_source; const auto& tablet = active_wacom_tablet();
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch) app->mouse_up(1, (float)pt.x, (float)pt.y, resolve_pointer_source(tablet, extra), 0);
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
app->mouse_up(1, (float)pt.x, (float)pt.y, pointer_source, 0);
}); });
} }
break; break;