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.
This commit is contained in:
The412Banner committed 2026-09-27 16:30:49 -04:00
1 parent d86e6d7776
commit fff1e837d3
5 files changed
+44 -4

No files matched your search

+8 -2
View File
@@ -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; }
@@ -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)
+1 -1
View File
@@ -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 \
+4
View File
@@ -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.
@@ -717,8 +717,36 @@ CEMU_XML = """<?xml version="1.0" encoding="UTF-8"?>
"""
# 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 = """<?xml version="1.0" encoding="UTF-8"?>
<content>
<mlc_path></mlc_path>
<GamePaths>
<Entry>{games}</Entry>
</GamePaths>
<Graphic>
<api>1</api>
</Graphic>
<Audio>
<api>3</api>
<TVVolume>100</TVVolume>
<PadVolume>100</PadVolume>
<TVDevice>default</TVDevice>
<PadDevice>default</PadDevice>
</Audio>
</content>
"""
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