From fff1e837d3576be1925aebe5dec093c81dec74fd Mon Sep 17 00:00:00 2001 From: The412Banner Date: Sun, 27 Sep 2026 16:15:58 -0400 Subject: [PATCH] Cemu (Wii U) from the front end and the desktop, and two session fixes Cemu never showed a frame on DroidDeck: its Getting Started dialog blocked every first launch (the folder picker cannot reach ROMs), the session's controller helper aborted it, and the desktop's Vulkan renderer drew no cursor. With the Turnip KGSL fix in the driver and the haptic-optional Cemu in the catalog, this makes the app side complete: - bannerlator-pad-defaults seeds Cemu's settings.xml once: the Wii U games folder, Vulkan, and Cubeb audio on the default device for both the TV and the GamePad speaker at full volume (Wipeout 3 plays through the GamePad; Cemu's own default is 20 %). - The Cemu tile launches full screen (-f). - libfakeinput.so is preloaded into every session process and exported its static C++ runtime (~7000 symbols); programs unwound through it and aborted (shadPS4 in pthread_exit, Cemu's controller thread). Link with --exclude-libs,ALL, and fail the build if the runtime leaks out again. - On the Vulkan and GLES desktops the pointer worked but no cursor was drawn: wlroots hands the cursor to the app's compositor, which draws none. WLR_NO_HARDWARE_CURSORS=1 makes labwc composite it itself. Device-tested on the FIT: Super Mario 3D World in-game at 35-37 fps with sound, Deus Ex HR DC and Wipeout 3 launch with sound; RPCS3, Dolphin and a Steam session (FlatOut) unchanged on the same driver. --- .github/workflows/build.yml | 10 +++++-- .../droiddeck/launcher/frontend/Library.kt | 4 ++- tools/build_local.sh | 2 +- tools/linuxfs/desktop/droiddeck-desktop | 4 +++ .../usr/local/bin/bannerlator-pad-defaults | 28 +++++++++++++++++++ 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cf1cf715e..8eb47b0a1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ concurrency: cancel-in-progress: true env: - PRELOAD_CACHE_VERSION: v1 + PRELOAD_CACHE_VERSION: v2 jobs: build: @@ -160,7 +160,7 @@ jobs: D=app/src/main/assets/linuxfs mkdir -p "$D" aarch64-linux-gnu-g++ -shared -fPIC -O2 -Wall -Wno-attributes -Wno-nonnull-compare \ - -pthread -std=c++17 -static-libstdc++ -static-libgcc \ + -pthread -std=c++17 -static-libstdc++ -static-libgcc -Wl,--exclude-libs,ALL \ -o "$D/libfakeinput.so" app/src/main/cpp/fakeinput_steam.cpp -ldl aarch64-linux-gnu-strip --strip-unneeded "$D/libfakeinput.so" aarch64-linux-gnu-gcc -shared -fPIC -O2 -Wall -pthread \ @@ -270,6 +270,12 @@ jobs: for sym in open openat ioctl read close poll ppoll select stat fstat access scandir; do grep -qx "$sym" <<<"$FAKE" || { echo "::error::libfakeinput.so does not export $sym"; exit 1; } done + # The static C++ runtime stays private: every session process preloads this library, + # and an exported personality routine takes over other programs' unwinding (shadPS4 + # aborted in pthread_exit). + if grep -qxE '__gxx_personality_v0|__cxa_throw|_Unwind_Resume' <<<"$FAKE"; then + echo "::error::libfakeinput.so exports the C++ runtime"; exit 1 + fi SESSION=$(syms "$D/libblsession.so") for sym in socket bind getsockname setsockopt statfs statvfs; do grep -qx "$sym" <<<"$SESSION" || { echo "::error::libblsession.so does not export $sym"; exit 1; } diff --git a/app/src/main/java/com/droiddeck/launcher/frontend/Library.kt b/app/src/main/java/com/droiddeck/launcher/frontend/Library.kt index 96610d0cf..273972ada 100644 --- a/app/src/main/java/com/droiddeck/launcher/frontend/Library.kt +++ b/app/src/main/java/com/droiddeck/launcher/frontend/Library.kt @@ -289,7 +289,9 @@ object Library { "-C", "Dolphin.General.HotkeysRequireFocus=False", "-C", "Dolphin.Input.BackgroundInput=True", "-e", guestPath, ) - "cemu" -> listOf("-g", guestPath) + // Full screen (LaunchSettings.cpp -f); the settings seed keeps the Getting Started + // dialog away (bannerlator-pad-defaults). + "cemu" -> listOf("-f", "-g", guestPath) // Full screen (CLI.cpp --fullscreen); the guide button leaves it for melonDS's menus and // comes back (HK_FullscreenToggle, bannerlator-pad-defaults). "melonds" -> listOf("-f", guestPath) diff --git a/tools/build_local.sh b/tools/build_local.sh index 09040662f..6673e469c 100755 --- a/tools/build_local.sh +++ b/tools/build_local.sh @@ -100,7 +100,7 @@ docker run --rm --platform linux/amd64 \ d=app/src/main/assets/linuxfs mkdir -p "$d" aarch64-linux-gnu-g++ -shared -fPIC -O2 -Wall -Wno-attributes -Wno-nonnull-compare \ - -pthread -std=c++17 -static-libstdc++ -static-libgcc \ + -pthread -std=c++17 -static-libstdc++ -static-libgcc -Wl,--exclude-libs,ALL \ -o "$d/libfakeinput.so" app/src/main/cpp/fakeinput_steam.cpp -ldl aarch64-linux-gnu-strip --strip-unneeded "$d/libfakeinput.so" aarch64-linux-gnu-gcc -shared -fPIC -O2 -Wall -pthread \ diff --git a/tools/linuxfs/desktop/droiddeck-desktop b/tools/linuxfs/desktop/droiddeck-desktop index 55adbdf07..6820a9dce 100644 --- a/tools/linuxfs/desktop/droiddeck-desktop +++ b/tools/linuxfs/desktop/droiddeck-desktop @@ -55,6 +55,10 @@ if [ -n "${BL_DEBUG_DIR:-}" ]; then echo "== droiddeck-desktop $(date -u '+%Y-%m-%d %H:%M:%S UTC') renderer=$WLR_RENDERER" fi export WLR_BACKENDS=wayland +# The app's compositor draws no wl_pointer cursor surfaces, so a cursor wlroots hands to its +# parent ("hardware" cursor on the wayland backend) never shows - on the GPU renderers the whole +# cursor was missing. Draw it into the desktop's own frames instead. +export WLR_NO_HARDWARE_CURSORS=${WLR_NO_HARDWARE_CURSORS:-1} mkdir -p "$HOME/.config/labwc" "$HOME/.config/lxqt" # Our defaults for labwc and the panel, refreshed every session. What a user changes in the # panel's own settings lands in panel.conf and is kept; only rc.xml and autostart are ours. diff --git a/tools/linuxfs/overlay/usr/local/bin/bannerlator-pad-defaults b/tools/linuxfs/overlay/usr/local/bin/bannerlator-pad-defaults index 5b254a223..adba13d6a 100755 --- a/tools/linuxfs/overlay/usr/local/bin/bannerlator-pad-defaults +++ b/tools/linuxfs/overlay/usr/local/bin/bannerlator-pad-defaults @@ -717,8 +717,36 @@ CEMU_XML = """ """ +# Cemu's settings, so its Getting Started dialog (shown while settings.xml is missing, and whose +# folder picker cannot reach ROMs) never blocks a launch: the Wii U games folder, Vulkan +# (Graphic api 1), and audio through Cubeb (the only backend on Linux; api 3) on the default +# device for both the TV and the GamePad speaker - some titles play everything through the +# GamePad - at full volume (Cemu's own default is 20 %). Keys from config/CemuConfig.cpp. +CEMU_SETTINGS = """ + + + + {games} + + + 1 + + + +""" + + def cemu(): seed(os.path.join(CONFIG, "Cemu/controllerProfiles/controller0.xml"), CEMU_XML, "Cemu") + games = system_folder(["wiiu", "wii u"], "wiiu") + seed(os.path.join(CONFIG, "Cemu/settings.xml"), CEMU_SETTINGS.format(games=games), "Cemu", + done=f"games in {games}, Vulkan, Cubeb audio on the default device") # melonDS 1.x: raw SDL joystick indices. The session's pad has the kernel's Xbox 360 layout