Skip to content

Stop the pre-boot gate persisting its pointer switch, and repair already-broken installs - #4

Open
bward-dev1 wants to merge 1 commit into
fix/device-only-playfrom
fix/landscape-wii-pointer-regression
Open

bward-dev1 wants to merge 1 commit into
fix/device-only-playfrom
fix/landscape-wii-pointer-regression

Conversation

@bward-dev1

@bward-dev1 bward-dev1 commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Stacked on #5 (fix/device-only-play) — merge that first. Base is set to it so this diff shows only the increment.

The regression

Reported: "ever since improvements; it's almost impossible to play Mario Kart Wii and other landscape-controller games like it don't work."

Cause: effbb0924e — "Add mandatory pre-game pointer calibration screen", which I found independently before seeing #5. We agree on the trigger; #5 fixes the defaults, this fixes the damage and two other hard failures in the same commit.

Verified chain:

  1. PreGameCalibrationPreferences.calibrationMode defaulted to .pointAtTV, on a screen that is mandatory, full-screen and modalInPresentation = true. Tapping Calibrate & Continue is the only way to reach a game.
  2. updateVisibleTouchPadToWii then calls switchToMotionPointingIfNeededConfig::SetBaseOrCurrent(MAIN_TOUCH_PAD_IR_MODE, None). SetBaseOrCurrent writes the Base layer, i.e. straight into Dolphin.ini, overwriting the shipped default (2 = Drag).
  3. TCWiiPad.handleLongPress (TCWiiPad.swift:69) returns immediately when mode == .nonethe on-screen touch pointer emits nothing at all.
  4. Pointing falls to the gyro, which has no drift correction left: IMUIR/Accelerometer Influence = 0 (a57ce46dc5) and IMUGyroscope/Calibration Period = 0 (96f567bca7), so EmulateIMUCursor (Dynamics.cpp:325-328) runs a zero-weight complementary filter over unbounded integrated gyro.

Every menu in Mario Kart Wii — license, mode, character, kart, course — is pointer-driven, so they became unclickable.

Correction to the earlier hypothesis: tilt steering was never affected

Worth stating plainly, because it changes what to test. The claim that this breaks MKWii's motion steering does not hold:

  • Wiimote::GetTotalAcceleration() (WiimoteEmu.cpp:944) → GetAcceleration() (:841), which calls GetTransformation() with its default identity extra_rotation.
  • m_imu_cursor_state.rotation — what the recenter pulse and gyro integration produce — is applied only in GetTotalTransformation() (:960), used at :507 for the IR camera data, not at :497 where the accelerometer report is built.
  • IMUAccelerometer::GetState() (IMUAccelerometer.cpp:31) is gated on AreInputsBound() alone, entirely independent of the IMUPoint enable flag this code toggles.

So the recenter pulse cannot move the steering. Racing itself always worked — which is precisely why Brandon said "almost impossible to play" rather than "won't launch", and why the fix should be judged on menus, not on handling.

What #5 already fixes, and what it doesn't

#5's display-derived, persisted defaults are a better answer to the default problem than my original one-line flip, so I dropped mine and rebased onto it. Three things remain:

1. The switch is persisted — so #5 alone leaves Brandon still broken

SetBaseOrCurrent writes Dolphin.ini. Better defaults only prevent new damage; anyone who has already run a build from master has TouchPadIRMode = 0 on disk and stays broken after #5. (The brief that reached me said the gate "persists nothing" — the answers persist nothing, but the consequence is written to disk permanently.) #5 also adds promptHandheldRecenter, which calls the same persisting path.

Fix: split into switchToMotionPointingIfNeededPersisting:. The automatic pre-game path writes LayerType::CurrentRun (dropped by BootManager::RestoreConfig(), never saved — the layer has no loader). The Motion menu's explicit Calibrate/Recenter actions still persist, as their alert text promises. SetBaseOrCurrent keeps targeting CurrentRun afterwards, so the in-game menu still works on top of it. Plus a one-time repair (DOLDidRepairForcedMotionPointingV1) restoring the shipped default for clobbered installs.

2. calibrateFlat could hang the boot forever

Its completion only ever fired from inside the gyro handler, and the gate blocks startEmulation on it behind a non-cancellable modal. No usable gyro, or motion access denied → the game never boots, with no way out. Now guards on isGyroAvailable with a 3s watchdog; averaging zero samples no longer yields NaN biases. Calibration state moved behind a lock since two threads can now finish it.

3. The gate ran before GameCube titles

No Wii Remote exists in a GameCube boot for any of those questions to affect. Added EmulationBootParameter.targetsWii (System Menu → yes, GC IPL → no, file boot → from the GameFile's DiscIO::Platform, defaulting to yes for any path that doesn't set it) and skip to startEmulation otherwise.

Note: #5 changed the subtitle from "every Wii game" to "every game" to match the then-current behaviour. I've put it back, since this PR makes "every Wii game" true again. Flagging it as a deliberate override of that choice.

What to test on device

CI-compiled only — not verified on hardware.

  1. Mario Kart Wii menus, iPad, no TV. Boot, tap straight through. The touch pointer should work again — drag to move the cursor through license/mode/character/kart/course.
  2. The setting stays put. Set Touch IR Pointer to Follow or Drag in the in-game menu, quit, boot again, tap through. Should still be Follow/Drag, not "Disabled (Use Motion)".
  3. Already-broken install self-heals. If your current install has the pointer dead, first launch of this build should restore it without touching any setting.
  4. Motion pointing on demand. Motion > Recenter Pointer (Handheld) should switch to motion pointing and stick across boots.
  5. A GameCube game (e.g. Melee) boots with no calibration screen at all.
  6. Racing handling — never affected by any of this; confirm it's unchanged.

Suspicious, deliberately not changed

  • IMUIR/Accelerometer Influence = 0 and IMUGyroscope/Calibration Period = 0 in Touchscreen.ini. Together these remove all drift correction from motion pointing. Both were deliberate fixes for a different symptom (calibration decaying in ~15s); re-tuning needs device testing, and a small non-zero influence is probably right rather than 0.
  • The gate still appears before every Wii boot. That was the explicit requirement in effbb0924e. Once-per-install or a "Don't ask again" is the obvious next step.
  • Five of six questions are still collected and never read (holdOrientation, flatFacing, isPlayingOnTV, tvScreenSize, tvScreenType) — Stop assuming a TV is attached when playing on the device alone #5 noted this too. Wire them up or drop them.
  • TCJoystick.sharedInit's guard let ... else { return } (3694061dbd) bails before attaching the pan recognizer, so a bad joystickType yields a silently dead stick. Not reachable with current XIBs.
  • TCDeviceMotion.port defaults to 0 and the gate enables motion before setPort:4 runs, so Wii accel axes are briefly written to controller port 0. Harmless pre-boot, but a real ordering smell.
  • Renderer full-bleed at the bottom in Emulation.storyboard — flagged by Stop assuming a TV is attached when playing on the device alone #5, left alone; agreed it's separate.

Ruled out as causes, for the record: supported interface orientations (unchanged, landscape still allowed both idioms); the touchPads outlet-collection order (still matches DOLEmulationVisibleTouchPad); 47f7c75e18's tag-based row dispatch (correct — segue rows are tag 0 and correctly fall through); the D-pad/joystick skin wiring in 835c5bf9ea (hit regions come from the view frame, not the image); built-in skin seeding (never activates a skin); and the core input additions in db05493f5b (purely additive).

🤖 Generated with Claude Code

…alls

Builds on fix/device-only-play, which fixes the *defaults* (display-derived
+ persisted) so a handheld player is no longer pushed into "Point at TV".
This covers three things that change doesn't reach.

1. The switch was persisted, and still is on that branch.
   switchToMotionPointingIfNeeded used Config::SetBaseOrCurrent, which
   writes the *Base* layer -> Dolphin.ini. So every Wii boot under the old
   defaults permanently wrote TouchPadIRMode = None (shipped default is
   2 = Drag). Deriving better defaults only stops new damage: anyone who
   has already run a build from master still has touch pointing disabled
   on disk and stays broken. Split into
   switchToMotionPointingIfNeededPersisting: -- the automatic pre-game
   path now writes LayerType::CurrentRun (dropped by
   BootManager::RestoreConfig, never saved), while the Motion menu's
   explicit Calibrate/Recenter actions still persist as their alert text
   promises. Added a one-time repair
   (DOLDidRepairForcedMotionPointingV1) that restores the shipped default
   for already-clobbered installs.

   Why this is the regression Brandon reported: TCWiiPad.handleLongPress
   returns immediately when mode == .none, so the on-screen touch pointer
   emits nothing at all. Pointing then falls to the gyro, which has no
   drift correction left (IMUIR/Accelerometer Influence = 0 from
   a57ce46, IMUGyroscope/Calibration Period = 0 from 96f567b, so
   EmulateIMUCursor runs a zero-weight complementary filter over raw
   integrated gyro). Every Mario Kart Wii menu is pointer-driven, so they
   became unclickable.

   Note the tilt-steering half of the earlier hypothesis does NOT hold:
   GetTotalAcceleration -> GetAcceleration uses GetTransformation() with
   an identity extra rotation, and m_imu_cursor_state.rotation is applied
   only in GetTotalTransformation (the IR camera path). IMUAccelerometer
   ::GetState is gated on AreInputsBound alone, independent of the
   IMUPoint enable this code toggles. Racing was never affected -- which
   is exactly why it read as "almost impossible to play" rather than
   "won't launch".

2. calibrateFlat could hang the boot forever. Its completion only ever
   fired from inside the gyro handler, and the gate blocks startEmulation
   on it behind a non-cancellable full-screen modal -- no usable gyro, or
   motion access denied, meant the game simply never started. Now guards
   on isGyroAvailable with a 3s watchdog, and averaging zero samples no
   longer produces NaN biases. Calibration state moved behind a lock since
   two threads can now finish it.

3. The gate ran before GameCube titles. Every question on it is about the
   emulated Wii Remote's pointer. Added EmulationBootParameter.targetsWii
   (System Menu yes, GC IPL no, file boot from the GameFile's
   DiscIO::Platform) and skip straight to startEmulation otherwise.
   Subtitle restored to "every Wii game" to match.

Not verified on device -- only Brandon can confirm the gameplay fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@bward-dev1
bward-dev1 force-pushed the fix/landscape-wii-pointer-regression branch from 7a0244a to 3a78003 Compare August 10, 2026 22:45
@bward-dev1
bward-dev1 changed the base branch from master to fix/device-only-play August 10, 2026 22:45
@bward-dev1 bward-dev1 changed the title Fix Wii pointer regression that made Mario Kart Wii unplayable Stop the pre-boot gate persisting its pointer switch, and repair already-broken installs Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants