chore: auto-commit (286 archivos)

- .claude/agents/fn-orquestador/SKILL.md
- .claude/commands/fn_claude.md
- .claude/rules/INDEX.md
- .claude/rules/cpp_apps.md
- .claude/rules/ids_naming.md
- CHANGELOG.md
- apps/dag_engine/README.md
- apps/dag_engine/api.go
- apps/dag_engine/dags_migrated/example.yaml
- apps/dag_engine/dags_migrated/example_lineage_tracking.yaml
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 16:33:22 +02:00
parent 0b9af8f1bb
commit a03675113a
281 changed files with 12596 additions and 19526 deletions
+221 -17
View File
@@ -23,12 +23,14 @@
#include <filesystem>
#include <string>
#include <sys/stat.h>
#include <unordered_map>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <windowsx.h> // GET_X_LPARAM / GET_Y_LPARAM
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h>
#else
@@ -57,37 +59,148 @@ static void glfw_error_callback(int error, const char* description) {
// the existing buffer pixels. We replicate that contract: while sizemove is
// active, skip render + glfwSwapBuffers, only pump the message queue. As soon
// as WM_EXITSIZEMOVE arrives, normal rendering resumes.
//
// IMPORTANT: the subclass must cover EVERY HWND owned by the process — main
// window AND every secondary viewport platform window the ImGui GLFW backend
// creates when the user drags a panel outside the main. Otherwise AltSnap on
// a secondary HWND would not be observed, the main loop would keep rendering,
// and the visible jitter would return on that panel. g_in_sizemove stays
// global on purpose: any external move on ANY of our HWNDs pauses the whole
// render pipeline, exactly like the native title-bar drag contract.
static std::atomic<bool> g_in_sizemove{false};
static WNDPROC g_orig_wndproc = nullptr;
static HWND g_subclassed_hwnd = nullptr;
// Test observability — monotonic counters. fn::internal exposes accessors.
static std::atomic<int> g_sizemove_enter_count{0};
static std::atomic<int> g_alt_rmb_resize_count{0};
static std::atomic<int> g_alt_lmb_move_count{0};
// Test hook — bypasses GetAsyncKeyState(VK_MENU) so headless tests can drive
// the Alt+RMB / Alt+LMB paths without UI-access for keybd_event.
static std::atomic<bool> g_force_alt_for_test{false};
// Diagnostic: every WM_RBUTTONDOWN this subclass sees (Alt-or-not). Used to
// distinguish "message never arrived" from "Alt check failed".
static std::atomic<int> g_rbuttondown_seen_count{0};
// Accessed only from the main (render) thread. Map value is the original
// WNDPROC for that HWND so we can restore and chain CallWindowProcW.
static std::unordered_map<HWND, WNDPROC> g_subclassed;
// Pick the WMSZ_* direction whose modal resize will feel natural depending
// on which quadrant of the client rect the cursor is in. Matches AltSnap's
// quadrant rule (top-left -> shrink toward top-left, etc.).
static int alt_rmb_resize_direction(HWND hwnd, int client_x, int client_y) {
RECT rc{};
if (!GetClientRect(hwnd, &rc)) return 8 /* WMSZ_BOTTOMRIGHT */;
int cx = (rc.right - rc.left) / 2;
int cy = (rc.bottom - rc.top) / 2;
bool top = (client_y < cy);
bool left = (client_x < cx);
if (top && left) return 4; // WMSZ_TOPLEFT
if (top && !left) return 5; // WMSZ_TOPRIGHT
if (!top && left) return 7; // WMSZ_BOTTOMLEFT
return 8; // WMSZ_BOTTOMRIGHT
}
static LRESULT CALLBACK fn_subclass_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_ENTERSIZEMOVE:
g_in_sizemove.store(true, std::memory_order_release);
g_sizemove_enter_count.fetch_add(1, std::memory_order_acq_rel);
break;
case WM_EXITSIZEMOVE:
g_in_sizemove.store(false, std::memory_order_release);
break;
case WM_LBUTTONDOWN:
// Alt + LMB anywhere on the window initiates a native modal MOVE
// via WM_SYSCOMMAND, SC_MOVE | HTCAPTION. Same pattern as our
// Alt+RMB resize: ReleaseCapture, post the syscommand, return 0
// to consume the click. Windows then drives a normal move modal
// (DefWindowProc blocks the thread) and our existing
// WM_ENTERSIZEMOVE gate pauses render so there's no jitter.
{
bool alt_real = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
bool alt_test = g_force_alt_for_test.load(std::memory_order_acquire);
if (alt_real || alt_test) {
g_alt_lmb_move_count.fetch_add(1, std::memory_order_acq_rel);
if (alt_test) {
// Test mode: skip SC_MOVE post to keep the harness
// out of Windows' modal move loop.
return 0;
}
ReleaseCapture();
PostMessageW(hwnd, WM_SYSCOMMAND,
(WPARAM)(0xF010 /* SC_MOVE */ | 2 /* HTCAPTION */), 0);
return 0;
}
}
break;
case WM_RBUTTONDOWN:
g_rbuttondown_seen_count.fetch_add(1, std::memory_order_acq_rel);
// Alt + RMB anywhere on the window initiates a native modal
// resize. Direction is chosen by cursor quadrant relative to
// window center so dragging "feels" like grabbing the nearest
// corner. ReleaseCapture is required before WM_SYSCOMMAND
// SC_SIZE so the modal loop can take input. The subsequent
// WM_ENTERSIZEMOVE bracket is observed above, so render is
// gated for free and no jitter appears.
{
bool alt_real = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
bool alt_test = g_force_alt_for_test.load(std::memory_order_acquire);
if (alt_real || alt_test) {
int cx = GET_X_LPARAM(lp);
int cy = GET_Y_LPARAM(lp);
int dir = alt_rmb_resize_direction(hwnd, cx, cy);
g_alt_rmb_resize_count.fetch_add(1, std::memory_order_acq_rel);
if (alt_test) {
// Test mode: skip the SC_SIZE post to keep the modal
// out of the headless test harness. The counter is
// sufficient to verify the path was taken; the modal
// entry is exercised in the real-input manual test.
return 0;
}
ReleaseCapture();
PostMessageW(hwnd, WM_SYSCOMMAND,
(WPARAM)(0xF000 /* SC_SIZE */ | dir), 0);
return 0; // consume so ImGui doesn't see a right-click
}
}
break;
default: break;
}
return CallWindowProcW(g_orig_wndproc, hwnd, msg, wp, lp);
auto it = g_subclassed.find(hwnd);
WNDPROC orig = (it != g_subclassed.end()) ? it->second : nullptr;
if (orig) return CallWindowProcW(orig, hwnd, msg, wp, lp);
return DefWindowProcW(hwnd, msg, wp, lp);
}
static void install_sizemove_subclass_hwnd(HWND hwnd) {
if (!hwnd) return;
if (g_subclassed.find(hwnd) != g_subclassed.end()) return; // idempotent
WNDPROC orig = (WNDPROC)SetWindowLongPtrW(
hwnd, GWLP_WNDPROC, (LONG_PTR)fn_subclass_wndproc);
g_subclassed[hwnd] = orig;
}
static void install_sizemove_subclass(GLFWwindow* w) {
HWND hwnd = glfwGetWin32Window(w);
if (!hwnd) return;
g_subclassed_hwnd = hwnd;
g_orig_wndproc = (WNDPROC)SetWindowLongPtrW(
hwnd, GWLP_WNDPROC, (LONG_PTR)fn_subclass_wndproc);
if (!w) return;
install_sizemove_subclass_hwnd(glfwGetWin32Window(w));
}
static void uninstall_sizemove_subclass() {
if (g_subclassed_hwnd && g_orig_wndproc) {
SetWindowLongPtrW(g_subclassed_hwnd, GWLP_WNDPROC, (LONG_PTR)g_orig_wndproc);
// Reap stale entries: when a secondary viewport is destroyed (panel re-docked
// back into main), the GLFW backend calls glfwDestroyWindow and the HWND is
// invalidated. Drop those entries so we don't hold dangling pointers and so a
// fresh HWND at the same address gets re-subclassed cleanly.
static void prune_dead_subclassed() {
for (auto it = g_subclassed.begin(); it != g_subclassed.end();) {
if (!IsWindow(it->first)) it = g_subclassed.erase(it);
else ++it;
}
g_subclassed_hwnd = nullptr;
g_orig_wndproc = nullptr;
}
static void uninstall_sizemove_subclass_all() {
for (auto& kv : g_subclassed) {
if (IsWindow(kv.first) && kv.second) {
SetWindowLongPtrW(kv.first, GWLP_WNDPROC, (LONG_PTR)kv.second);
}
}
g_subclassed.clear();
}
static inline bool external_sizemove_active() {
@@ -312,6 +425,15 @@ int run_app(AppConfig config, std::function<void()> render_fn) {
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
// Title-bar-only move for ImGui windows. Critical for secondary viewports
// (floating panels) whose entire OS window is a single borderless ImGui
// window: without this flag, ImGui moves the window when the user drags
// any empty client-area pixel, which translates to the OS viewport
// following the mouse "from anywhere" with no modifier. With this flag,
// floating panels obey the same "header only" contract as a native
// decorated window. Alt+LMB anywhere still moves via our WndProc subclass
// (consumed before ImGui sees the click).
io.ConfigWindowsMoveFromTitleBarOnly = true;
// Convencion local_files: imgui.ini y app_settings.ini viven en
// <exe_dir>/local_files/. Migra automaticamente desde el cwd o
@@ -406,17 +528,59 @@ int run_app(AppConfig config, std::function<void()> render_fn) {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
// When the main window is iconified we used to glfwWaitEvents+continue
// to save CPU. That is wrong when floating panels (secondary
// viewports) exist: skipping the frame stops UpdatePlatformWindows /
// RenderPlatformWindowsDefault so those panels go blank or get
// ungrouped by the WM. We therefore detect secondary viewports first
// and, if any are alive, fall through to a normal frame (main GL
// clear/swap is harmless on the hidden main HWND, secondary GL
// contexts keep refreshing). Only when there are NO floating panels
// do we sleep on glfwWaitEvents the way we used to.
if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) {
glfwWaitEvents();
continue;
bool has_secondary_viewport = false;
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGuiPlatformIO& pio_ic = ImGui::GetPlatformIO();
for (int i = 0; i < pio_ic.Viewports.Size; ++i) {
ImGuiViewport* vp = pio_ic.Viewports[i];
if (!vp || !vp->PlatformHandle) continue;
if ((GLFWwindow*)vp->PlatformHandle == window) continue;
has_secondary_viewport = true;
break;
}
}
if (!has_secondary_viewport) {
glfwWaitEvents();
continue;
}
// fallthrough: render normally so floating panels stay alive.
}
#ifdef _WIN32
// Subclass any platform window we haven't subclassed yet. Covers the
// main window AND every secondary viewport (panels dragged outside
// main) so AltSnap's WM_ENTERSIZEMOVE/WM_EXITSIZEMOVE brackets are
// observed regardless of which HWND it targets. Runs BEFORE the
// sizemove gate below so newly-created secondaries are protected from
// their very first frame onwards.
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
prune_dead_subclassed();
ImGuiPlatformIO& pio_sub = ImGui::GetPlatformIO();
for (int i = 0; i < pio_sub.Viewports.Size; ++i) {
ImGuiViewport* vp = pio_sub.Viewports[i];
if (!vp || !vp->PlatformHandle) continue;
install_sizemove_subclass((GLFWwindow*)vp->PlatformHandle);
}
}
#endif
// While an external mover (AltSnap on Win32, tiling WMs) is dragging
// the window we mirror the native title-bar contract: do not render,
// do not swap, just pump events. The DWM compositor scrolls the last
// presented framebuffer with the window — no race between SetWindowPos
// (async) and glfwSwapBuffers, so no jitter. WM_EXITSIZEMOVE clears
// the flag and the main loop resumes normal rendering.
// the flag and the main loop resumes normal rendering. Applies to
// brackets on ANY subclassed HWND (main or secondary viewports).
if (external_sizemove_active()) {
// Bound the busy loop so the message queue gets drained but we
// don't burn CPU when AltSnap pauses between mouse moves.
@@ -576,7 +740,7 @@ int run_app(AppConfig config, std::function<void()> render_fn) {
ImPlot::DestroyContext();
ImGui::DestroyContext();
#ifdef _WIN32
uninstall_sizemove_subclass();
uninstall_sizemove_subclass_all();
#endif
glfwDestroyWindow(window);
glfwTerminate();
@@ -588,6 +752,46 @@ int run_app(std::function<void()> render_fn) {
return run_app(AppConfig{}, render_fn);
}
// Test-only observability of the Win32 subclass. Always defined (zero cost);
// on non-Windows the counters never increment.
namespace internal {
int sizemove_enter_count() {
#ifdef _WIN32
return g_sizemove_enter_count.load(std::memory_order_acquire);
#else
return 0;
#endif
}
int alt_rmb_resize_count() {
#ifdef _WIN32
return g_alt_rmb_resize_count.load(std::memory_order_acquire);
#else
return 0;
#endif
}
void set_force_alt_for_test(bool v) {
#ifdef _WIN32
g_force_alt_for_test.store(v, std::memory_order_release);
#else
(void)v;
#endif
}
int rbuttondown_seen_count() {
#ifdef _WIN32
return g_rbuttondown_seen_count.load(std::memory_order_acquire);
#else
return 0;
#endif
}
int alt_lmb_move_count() {
#ifdef _WIN32
return g_alt_lmb_move_count.load(std::memory_order_acquire);
#else
return 0;
#endif
}
} // namespace internal
} // namespace fn
#ifdef IMGUI_ENABLE_TEST_ENGINE