/*------------------------------------------------------------------------------
  test-11180-g-teardown-mouse-decoy.p                    (mouse/keyboard path)

  ONE-VARIABLE VARIANT of test-11180-g-teardown-mouse.p.  Identical topology -
  secondary WINDOW W2 holds frmO + fO, focus is parked in fO, fO's frame LEAVE
  DELETEs W2 with NO APPLY, and the transfer is driven by a physical CLICK on
  fT in the default window - EXCEPT that frmT now contains a DECOY enabled
  fill-in (fDecoy) placed AHEAD of fT, both in the frame definition and on
  screen.

  WHY THE DECOY EXISTS (root cause measured with jdb against FWD 16725 on
  2026-08-17, client PID 47876):

  The parent probe's missing frmT/fT ENTRY is NOT a FocusManager defect -
  FocusManager.handleFocusChange is never called at all on that path.  The
  actual chain, taken from a live breakpoint stack, is:

      WindowManager.processWindowEvent:2320   sendLeave(frmO, null)
        ThinClient.sendLeave                  posts the LEAVE event
          ThinClient.invokeTriggers -> RPC    the 4GL oLeave runs on the server
            ThinClient.removeWindow:11927     <- DELETE WIDGET hWin2 calls back
              WindowManager.removeWindow -> remove -> WindowGuiImpl.destroy:1417
                WindowManager.setFocusWindow:1179 -> :1206  window.requestFocus()
                  ... -> AbstractContainer.setFocusInt(fT)

  WindowGuiImpl.destroy ends with setFocusWindow(getActiveWindow()), whose
  one-arg overload passes focusFirstWidget = TRUE (verified live), so :1206
  calls window.requestFocus() and the focus is driven down to the FIRST
  FOCUSABLE WIDGET of the newly active window.  requestFocus is a raw
  assignment: it emits NO frame ENTRY and NO widget ENTRY.

  In the parent probe the default window's first focusable widget IS fT - the
  very widget being clicked.  So by the time the mouse PRESSED is processed,
  oldFocus == newFocus == fT, the guard at ThinClient.java:22677 fails on
  "newFocus != oldFocus", handleFocusChange is skipped, and the click's ENTRYs
  are never dispatched.  FWD gets the right answer for the wrong reason only
  when the teardown fallback happens to land somewhere else.

  THE VARIABLE THIS PROBE CHANGES: make the first focusable widget NOT be the
  click target.  The teardown fallback should then land on fDecoy, leaving
  oldFocus = fDecoy != newFocus = fT at the press, so handleFocusChange DOES
  run and the click is honoured.

  HOW TO READ THE FWD LOG (the discrimination this probe exists for):

    * fDecoy LEAVE then fT ENTRY, and NO fDecoy ENTRY
        -> mechanism CONFIRMED.  The fallback landed on fDecoy silently (no
           ENTRY, exactly as the debugger showed the raw requestFocus does),
           and the click then drove a real focus change.  Note frmT ENTRY is
           NOT expected here: fDecoy and fT share frmT, so oldFrame ==
           newFrame and handleFocusChange skips both frame-level branches.
    * nothing after "frmO LEAVE (end...)", exactly like the parent probe
        -> mechanism REFUTED.  The fallback did not land on fDecoy, so
           "first focusable widget of the newly active window" is the wrong
           description of what requestFocus resolves to.
    * fDecoy ENTRY appears
        -> the teardown fallback is NOT silent after all, which contradicts the
           breakpoint evidence and would need re-measuring.

  NATIVE OPENEDGE BASELINE - MEASURED 2026-08-17 (verbatim):

      === test-11180-g-teardown-mouse-decoy start ===
      01 --- APPLY ENTRY to fO (park focus in secondary window W2) ---
      02 --- now CLICK fT - the SECOND field - in the default window ---
      03 frmT   ENTRY
      04 fDecoy ENTRY
      05 frmO   LEAVE  (begin - delete secondary WINDOW W2, NO APPLY)
      06 frmO   LEAVE  (end - no APPLY performed)
      07 fDecoy LEAVE
      08 fT     ENTRY
      09 --- F5: settled FOCUS is now: FILL-IN/fT      (2nd run; F5 pressed)

  Reproduced twice on 2026-08-17; lines 01-08 identical both times.  Line 09
  appears only when F5 is pressed after the click and before closing.

  SETTLED FOCUS AGREES ACROSS ENGINES: OE reports FILL-IN/fT, and the FWD trace
  reports settledFocus=fT.  So this is a TRIGGER-VISIBILITY divergence (two
  events never emitted), NOT a focus-state divergence - application code that
  only reads FOCUS sees the same thing on both engines.

  F5 ALSO ACTS AS A CONTROL: it produces no ENTRY/LEAVE of its own, only its
  own log line.  That matters for the PARENT probe, whose trailing fT ENTRY was
  once attributed to the ESC keypress that closes the window.  Line 08 here
  arrives BEFORE the F5 line, so on this shape the trailing ENTRY is
  click-driven; the parent's line 07 is the same step, which on its
  single-field frame shows up as a second fT ENTRY with no matching LEAVE
  (focus never left fT).  Do not re-attribute it to ESC.

  THIS FALSIFIED THE HYPOTHESIS THIS PROBE WAS WRITTEN WITH.  The guess was
  that OE would ignore fDecoy and enter the clicked widget directly, as the
  parent probe's log appears to show.  It does not.  OE enters the target
  FRAME at its FIRST ENABLED FIELD (03, 04), runs the source frame's LEAVE
  (05, 06), and only then moves WITHIN the frame to the widget actually
  clicked (07, 08).  The parent probe cannot show this: frmT has exactly one
  field there, so "first field" and "clicked field" are the same widget and
  the two steps collapse onto it.

  Consequence for the FWD reading: the teardown fallback landing on the first
  focusable widget is NOT a wrong destination - OE goes there too.  The defect
  is that WindowGuiImpl.destroy -> requestFocus performs that step SILENTLY,
  losing OE's lines 03 and 04, while the second move (07, 08) does go through
  handleFocusChange and should be emitted correctly.  In the single-field
  parent probe the silent step IS the whole transfer, which is why every event
  disappeared there.

  FWD 16725 RESULT - MEASURED 2026-08-17 (verbatim):

      === test-11180-g-teardown-mouse-decoy start ===
      01 --- APPLY ENTRY to fO (park focus in secondary window W2) ---
      02 --- now CLICK fT - the SECOND field - in the default window ---
      03 frmO   LEAVE  (begin - delete secondary WINDOW W2, NO APPLY)
      04 frmO   LEAVE  (end - no APPLY performed)
      05 fDecoy LEAVE
      06 fT     ENTRY
      07 --- F9: settled FOCUS is now: FILL-IN/fT

  Line 07 is the F9 settled-focus probe (F5 is unusable here - it reloads the
  browser page; see the trigger comment below).  It confirms 4GL-side, not just
  from the Java trace, that FWD ends on the SAME widget as OE.

  TRUNK CONTROL - MEASURED 2026-08-17: an FWD *trunk* web client produces this
  log byte-identically (verified with cmp: deploy/client/... vs
  docs/11180b/logs/...).  Two consequences:
    * the divergence is PRE-EXISTING IN TRUNK, not a regression introduced by
      the #11180 work, so it can be filed and scheduled independently;
    * the temporary [11180m] instrumentation carried by the 11180b build is
      side-effect free - the uninstrumented trunk jar behaves the same.
  The parent probe already showed the same trunk parity (docs/p2j/logs/ and
  docs/11180c/logs/ test-11180-g-mouse.log).

  WATCH THE DIRECTORY WHEN COMPARING LOGS: under docs/11180b/, the "4gl/"
  subdirectory holds the NATIVE OPENEDGE baseline (9 numbered lines here) while
  "logs/" holds the FWD run (7 numbered lines).  Both paths are branch-scoped by
  name, so a 9-vs-7 line difference between them is the OE-vs-FWD divergence -
  NOT a trunk-vs-branch difference.

  MECHANISM CONFIRMED - this is the "fDecoy LEAVE then fT ENTRY, and NO fDecoy
  ENTRY" row of the table above.  FWD is missing EXACTLY OE's lines 03 and 04
  (frmT ENTRY + fDecoy ENTRY) and matches OE's 05-08 in order.  Client trace
  (tag [11180m]) shows the whole chain: the teardown fallback set globalFocus =
  fDecoy silently; the ACTIVATE arm sent only the window ENTRY; the mouse
  PRESSED then saw old=fDecoy != new=fT so handleFocusChange DID run, emitted
  the widget LEAVE, correctly SKIPPED the frame ENTRY (oldFrame == newFrame)
  and emitted fT's ENTRY.

  Why the parent probe loses THREE events and this one only TWO: same cause.
  There the silent fallback lands on fT itself, so the press sees
  old == new, handleFocusChange is skipped, and the within-frame move that
  produces OE's line 07 never happens either.

  Note the ORDERING difference this probe exposes, which no minimal fix closes:
  OE enters the target frame at click time, BEFORE the source frame's LEAVE
  runs.  FWD re-homes focus only as a side effect of the window destroy, which
  happens INSIDE that LEAVE.  So setFocusWindow(..., false) at
  WindowGuiImpl.java:1417 would make the events exist but in the wrong order,
  and would skip the first-field step entirely.

  NB the "<==" annotations on the log lines below were written FWD-first, from
  the pre-measurement hypothesis.  On OE, line 04 "fDecoy ENTRY ... (fallback
  NOT silent)" is NORMAL frame-entry behaviour, not an anomaly - read it as
  "OE announces the first-field entry".  The strings are deliberately left
  unchanged so this baseline stays byte-comparable with future runs.

  Steps to drive (GUI - NOT head-less, needs a real GUI driver):
    1. Focus starts in fO (secondary window W2).
    2. On FWD, drag WINDOW-2 aside first (it opens IN FRONT of the default
       window and occludes frmT; on OE it opens behind and no drag is needed).
       See auto/gmouse_drive.cjs.  NB the click coordinate is NOT the parent
       probe's [40,137] - frmT now has two rows and fT sits one row LOWER.
       Take the b_dragged screenshot and read the target off it.
    3. CLICK fT - the SECOND field.  Do NOT click fDecoy.
    4. Log the settled focus (the parent probe could never capture it: ESC
       aborts the procedure before the post-WAIT-FOR statements run, so the
       trailing "done" line never appears).  Press the key AFTER the click and
       BEFORE closing.
         * on FWD (web client): press F9.  DO NOT press F5 - the browser
           reloads the page; FWD only suppresses the browser default for TAB
           and F3.
         * on OE: either works.  The banked line 09 below was taken with F5.
    5. Close the window to end.

  Log to test-11180-g-mouse-decoy.log.
------------------------------------------------------------------------------*/

DEFINE VARIABLE seq AS INTEGER NO-UNDO.

DEFINE VARIABLE hWin2 AS HANDLE NO-UNDO.
DEFINE VARIABLE hFrmO AS HANDLE NO-UNDO.
DEFINE VARIABLE hFO   AS HANDLE NO-UNDO.

DEFINE VARIABLE fDecoy AS CHARACTER NO-UNDO FORMAT "x(8)" INITIAL "ddd".
DEFINE VARIABLE fT     AS CHARACTER NO-UNDO FORMAT "x(8)" INITIAL "ttt".

/* fDecoy is FIRST in the frame definition AND first on screen, so it is the
   first focusable widget of the default window - the slot the teardown
   fallback resolves to. */
DEFINE FRAME frmT
   fDecoy AT ROW 1 COLUMN 2
   fT     AT ROW 2 COLUMN 2
   WITH TITLE "FRAME-T (decoy first, click target second)"
   AT ROW 3 COLUMN 2 SIZE 44 BY 6 SIDE-LABELS.

PROCEDURE logit:
   DEFINE INPUT PARAMETER m AS CHARACTER NO-UNDO.
   seq = seq + 1.
   OUTPUT TO "test-11180-g-mouse-decoy.log" APPEND.
   PUT UNFORMATTED STRING(seq, "99") " " m SKIP.
   OUTPUT CLOSE.
END PROCEDURE.

/* NB FOCUS:NAME is the UNKNOWN value for a dynamic widget that was never given
   a NAME, and "..." + ? is unknown in 4GL, so the whole line would vanish. */
PROCEDURE logFocus:
   DEFINE INPUT PARAMETER m AS CHARACTER NO-UNDO.
   RUN logit(m + (IF VALID-HANDLE(FOCUS)
                  THEN STRING(FOCUS:TYPE)
                       + "/"
                       + (IF FOCUS:NAME = ? THEN "<noname>" ELSE FOCUS:NAME)
                  ELSE "<none/invalid>")).
END PROCEDURE.

/* fO's frame LEAVE (driven by the click): tear down window W2, NO APPLY */
PROCEDURE oLeave:
   RUN logit("frmO   LEAVE  (begin - delete secondary WINDOW W2, NO APPLY)").
   IF VALID-HANDLE(hWin2) THEN DELETE WIDGET hWin2.
   RUN logit("frmO   LEAVE  (end - no APPLY performed)").
END PROCEDURE.

ON ENTRY OF FRAME frmT
   RUN logit("frmT   ENTRY  <== target FRAME ENTRY (not expected on FWD: same frame)").
ON LEAVE OF FRAME frmT
   RUN logit("frmT   LEAVE").

/* The decoy: ENTRY here would mean the teardown fallback is NOT silent.
   LEAVE here is the signature that the fallback DID land on it and the click
   then drove a real focus change. */
ON ENTRY OF fDecoy IN FRAME frmT
   RUN logit("fDecoy ENTRY  <== fallback NOT silent (contradicts jdb evidence)").
ON LEAVE OF fDecoy IN FRAME frmT
   RUN logit("fDecoy LEAVE  <== fallback landed here, click drove a real change").

ON ENTRY OF fT IN FRAME frmT
   RUN logit("fT     ENTRY  <== clicked target ENTRY (dropped by the parent probe)").
ON LEAVE OF fT IN FRAME frmT
   RUN logit("fT     LEAVE").

/* Settled-focus probe: press the key AFTER the click and BEFORE closing.  Adds
   no event to the measured click window (verified on OE: the keypress emits
   only its own log line, no ENTRY/LEAVE).
   TWO keys are bound on purpose:
     F5 - what the banked OE baseline (line 09) was recorded with.  UNUSABLE on
          the FWD web client: p2j.keyboard.js forwards F5 to the server but its
          permittedKeyStrokes() only suppresses the browser default for TAB and
          F3, so Chrome reloads the page instead.
     F9 - use this one on FWD.  No Chrome shortcut, and 4GL reserves a default
          function only for F1 (HELP) and F2 (GO) - see Keyboard.java:1704 - so
          F3-F12 reach the application untouched.
   Each logs its own key name, so a log line is unambiguous about which was
   pressed and the F5 baseline text stays byte-identical. */
ON "F5":U ANYWHERE
   RUN logFocus("--- F5: settled FOCUS is now: ").
ON "F9":U ANYWHERE
   RUN logFocus("--- F9: settled FOCUS is now: ").

/* ---- main ---- */
OUTPUT TO "test-11180-g-mouse-decoy.log".
PUT UNFORMATTED "=== test-11180-g-teardown-mouse-decoy start ===" SKIP.
OUTPUT CLOSE.

ENABLE fDecoy fT WITH FRAME frmT.
VIEW FRAME frmT.

CREATE WINDOW hWin2
   ASSIGN TITLE = "WINDOW-2 (torn down in its own LEAVE)"
          WIDTH = 40 HEIGHT = 6 VISIBLE = FALSE.
CREATE FRAME hFrmO
   ASSIGN TITLE = "FRAME-O (source, in window 2)"
          PARENT = hWin2
          ROW = 2 COLUMN = 2 WIDTH = 34 HEIGHT = 4
          BOX = TRUE VISIBLE = FALSE.
CREATE FILL-IN hFO
   ASSIGN FRAME = hFrmO
          NAME = "fO" FORMAT = "x(8)" SCREEN-VALUE = "ooo"
          X = 4 Y = 4 WIDTH = 20
          SENSITIVE = TRUE VISIBLE = TRUE.
ON "LEAVE":U OF hFrmO PERSISTENT RUN oLeave.

hWin2:VISIBLE = TRUE.
hFrmO:VISIBLE = TRUE.

CURRENT-WINDOW = hWin2.

RUN logit("--- APPLY ENTRY to fO (park focus in secondary window W2) ---").
APPLY "ENTRY" TO hFO.
RUN logit("--- now CLICK fT - the SECOND field - in the default window ---").
PAUSE 0.
/* Keep the session alive so the tester can click fT; the LEAVE fires from the
   click, not from an APPLY.  Close the window to end. */
WAIT-FOR CLOSE OF THIS-PROCEDURE.

RUN logFocus("--- done; FOCUS is now: ").
RUN logit("--- OE baseline for this shape was UNMEASURED when written ---").
