Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1ef535f
Let a profile say which console the game runs on, and add Patapon's s…
MHunterG Sep 26, 2026
c0a4f4a
Trace the language and confirm button a game asks for and sets
MHunterG Sep 29, 2026
2dfadc9
Put the console's confirm button where the player's setting says
MHunterG Sep 29, 2026
e9e6faf
Tilt a game's world by tilting the device
MHunterG Sep 29, 2026
72adfd8
Merge tilt controls into the Purun deck-dev build
MHunterG Sep 29, 2026
7a94d1f
Read a Steam Deck's gyroscope beside Steam Input
MHunterG Sep 29, 2026
13f058a
Merge the Steam Deck gyroscope into the Purun deck-dev build
MHunterG Sep 29, 2026
5d9f45c
Read the Steam Deck's motion sensors on a thread of their own
MHunterG Sep 29, 2026
1a93b46
Merge the threaded Steam Deck motion reader into the Purun deck-dev b…
MHunterG Sep 29, 2026
ab97b1c
Hold a game's ground still in Angle + level horizon
MHunterG Sep 29, 2026
7b45f22
Merge the level horizon from the game's own tilt into the Purun deck-…
MHunterG Sep 29, 2026
5695599
Jump with one button beside tilt controls, and keep the level horizon…
MHunterG Sep 29, 2026
ea1e5d0
Merge the jump button and the hidden level horizon into the Purun dec…
MHunterG Sep 29, 2026
fe86456
Say what the emulated PSP is doing when a game stops drawing
MHunterG Sep 29, 2026
84b1d9c
Merge the stall watchdog into the Purun deck-dev build
MHunterG Sep 29, 2026
56cb885
Answer closing the window and SIGTERM while a game stalls
MHunterG Sep 29, 2026
227863d
Merge the stalled-game quit into the Purun deck-dev build
MHunterG Sep 29, 2026
388b344
Let a game set the keyboard it starts with
MHunterG Sep 30, 2026
6993f98
Merge game keyboard defaults into the Purun deck-dev build
MHunterG Sep 30, 2026
43e9217
Merge Purun controls and stall fixes into the current framework
MHunterG Oct 7, 2026
8c243d6
Package Android games without overlays and include license texts
MHunterG Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,17 @@ if(PSPRECOMP_BUILD_TESTS)
add_test(NAME portablekit_profile_tests COMMAND portablekit_profile_tests)
endif()

# Tilt controls: the device's roll from its motion sensors as a game's tilt
# buttons, with synthetic sensor readings.
if(PSPRECOMP_BUILD_TESTS)
add_executable(portablekit_tilt_tests
"${PORTABLEKIT_ROOT}/tests/tilt_tests.cpp"
"${PORTABLEKIT_ROOT}/host/input/tilt.cpp")
target_include_directories(portablekit_tilt_tests PRIVATE "${PORTABLEKIT_ROOT}/host")
set_target_properties(portablekit_tilt_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
add_test(NAME portablekit_tilt_tests COMMAND portablekit_tilt_tests)
endif()

# Texture pack import: finding a pack in the layouts packs come in, the
# checks made before copying, and the copy, swap and backup; and the PSP's DXT
# block layout. No game data.
Expand Down
8 changes: 7 additions & 1 deletion cmake/PortableKit.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ set(PORTABLEKIT_HOST_SOURCES
host/gpu/texture_decode.cpp
host/input/bindings.cpp
host/input/touch_controls.cpp
host/input/tilt.cpp
host/gpu/texture_pack.cpp
host/gpu/texture_pack_import.cpp
host/perf/frame_stats.cpp
Expand All @@ -50,6 +51,7 @@ set(PORTABLEKIT_HOST_SOURCES
host/kernel/rtc_time.cpp
host/kernel/load_trace.cpp
host/kernel/fast_loading.cpp
host/kernel/stall_watchdog.cpp
host/kernel/load_detector.cpp
host/kernel/iso_image.cpp
host/hle/hle_common.cpp
Expand Down Expand Up @@ -97,6 +99,8 @@ set(PORTABLEKIT_RENDERER_SOURCES
host/gpu/vulkan_renderer.cpp
host/ui/file_browser.cpp
host/ui/font_menu.cpp
host/input/motion.cpp
host/input/steam_deck_imu.cpp
host/ui/input_script.cpp
host/ui/layer.cpp
host/ui/menu.cpp
Expand Down Expand Up @@ -309,7 +313,9 @@ function(portablekit_add_game target)
"kGeVertexShader=${PORTABLEKIT_ROOT}/host/gpu/shaders/ge.vert"
"kGeRawVertexShader=GE_RAW_VERTICES@${PORTABLEKIT_ROOT}/host/gpu/shaders/ge.vert"
"kGeCheckVertexShader=GE_RAW_VERTICES,GE_CHECK_DECODE@${PORTABLEKIT_ROOT}/host/gpu/shaders/ge.vert"
"kGeFragmentShader=${PORTABLEKIT_ROOT}/host/gpu/shaders/ge.frag")
"kGeFragmentShader=${PORTABLEKIT_ROOT}/host/gpu/shaders/ge.frag"
"kLevelVertexShader=${PORTABLEKIT_ROOT}/host/gpu/shaders/level.vert"
"kLevelFragmentShader=${PORTABLEKIT_ROOT}/host/gpu/shaders/level.frag")
if(ANDROID)
# Pre-rotation of the finished frame for a display turned sideways.
list(APPEND shaders
Expand Down
2 changes: 2 additions & 0 deletions docs/PROFILE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ Two more are for a port that goes further than the PSP did, and both are null un

`trigger_profiles` are for how a game is played rather than how it is run: what the triggers may press instead of L and R, each with the key settings.ini keeps and the label the menu shows. Yakumo offers Bows (R on L2 to hold the aim, △ on R2 to shoot) and Bowguns (R and ○). Without any, the triggers press L and R and the menu has no row for them.

`tilt` is for a game played by tilting its world, as LocoRoco is with L and R: the buttons that tilt left and right, whether the game eases its tilt towards the held side (then a small roll of the device presses the button for part of the frames and tilts the world part of the way), and a sentence for the menu. With it, Controls gets a Tilt controls section, off by default, and the motion sensors of a gamepad (DualShock 4, DualSense, Switch Pro, a Steam Deck seen through SDL's own driver) or of the phone itself press those buttons (`host/input/tilt.hpp` for the arithmetic, `host/input/motion.hpp` for the sensors). Find the buttons and the easing with the game's pad trace before filling it in. Without it, nothing opens a sensor and the menu has no such section.

`player_name_label` and `default_player_name` are what the interface calls the name a game asks for and what it answers before the player sets one: "Player name" and "Player" unless a profile says otherwise. Yakumo says "Hunter name" and "Hunter".

`glyph_cache` is for changing the font while a game runs. A game that caches the glyphs it has drawn keeps them until its atlas needs the cell, so a new font shows only in text it has not drawn yet. Where the game keeps that atlas, read off its own text code, lets the framework make it draw them again. Null for any game nobody has traced.
Expand Down
29 changes: 29 additions & 0 deletions host/gpu/shaders/level.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#version 450

// Level horizon: the game's picture turned about its centre and zoomed just
// enough to cover its rectangle, so no corner is ever left empty.

layout(set = 0, binding = 0) uniform sampler2D frame;

layout(push_constant) uniform Level {
// Where the picture lies in the frame image: offset and size, 0 to 1.
vec4 rect;
// cos and sin of the turn (anticlockwise on screen), the zoom, and the
// picture's width over its height.
vec4 turn;
} level;

layout(location = 0) in vec2 picture;
layout(location = 0) out vec4 color;

void main() {
// Centred, in units of the picture's height, y down as on screen.
vec2 point = (picture - 0.5) * vec2(level.turn.w, 1.0);
// The picture is turned anticlockwise as seen, which with y down is a
// clockwise turn of the coordinates: sample where this point came from.
float c = level.turn.x;
float s = level.turn.y;
vec2 source = vec2(c * point.x - s * point.y, s * point.x + c * point.y) / level.turn.z;
vec2 uv = source / vec2(level.turn.w, 1.0) + 0.5;
color = texture(frame, level.rect.xy + clamp(uv, 0.0, 1.0) * level.rect.zw);
}
14 changes: 14 additions & 0 deletions host/gpu/shaders/level.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#version 450

// Level horizon (Angle + level horizon tilt controls): one triangle covering
// the picture's rectangle, which the viewport gives. The fragment shader
// turns and zooms the game's picture inside it.

layout(location = 0) out vec2 picture;

void main() {
// (0,0), (2,0), (0,2): the triangle that covers the whole viewport.
vec2 uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(uv * 2.0 - 1.0, 0.0, 1.0);
picture = uv;
}
Loading
Loading