android: route asymmetric companion displays

This commit is contained in:
AverageConsumer
2026-08-17 03:00:05 +02:00
parent 051040371f
commit 360b692963
8 changed files with 384 additions and 53 deletions
@@ -0,0 +1,35 @@
local function read(path)
local file = assert(io.open(path, "rb"))
local source = file:read("*a")
file:close()
return source
end
local function check(value, message)
if not value then error(message, 2) end
end
local java = read(
"mobile/android/love/src/main/java/org/love2d/android/GameActivity.java")
local manifest = read("mobile/android/app/src/main/AndroidManifest.xml")
check(manifest:find("android.allow_multiple_resumed_activities", 1, true)
and manifest:find("GameActivity$SecondaryActivity", 1, true)
and manifest:find('android:exported="false"', 1, true),
"the private companion Activity opts into Android multi-display resume")
check(java:find("android.os.Build.VERSION.SDK_INT < 29", 1, true)
and java:find("options.setLaunchDisplayId", 1, true),
"the primary-display fallback is restricted to Android 10+")
check(java:find("SECONDARY_TARGET_HANDHELD", 1, true)
and java:find("SECONDARY_TARGET_EXTERNAL", 1, true)
and java:find("handheldAvailable ? handheld : external", 1, true),
"routing hints retain a safe available-display fallback")
check(java:find("dualScreenDisplayMode != %-1")
and java:find("AYN_SECOND_SCREEN", 1, true)
and java:find("dualScreenModeObserverRegistered", 1, true),
"the optional AYN state is guarded and lifecycle-bound")
check(java:find("activity.dispatchKeyEvent", 1, true)
and java:find("activity.dispatchGenericMotionEvent", 1, true),
"companion windows forward controller input to the game Activity")
print("android asymmetric display routing: ok")
+2 -1
View File
@@ -30,7 +30,8 @@ check(java:find("Math.max((float) vw / fw, (float) vh / fh)", 1, true)
"FrameView supports cover and pixel-friendly contain fits")
check(cpp:find("love_android_secondary_detected", 1, true)
and cpp:find("love_android_present_secondary", 1, true)
and cpp:find("love_android_secondary_target", 1, true)
and cpp:find('"(Ljava/nio/ByteBuffer;IIIZ)Z"', 1, true),
"JNI exports the optional detected and presentation calls")
"JNI exports the optional detected, routing, and presentation calls")
print("android secondary presentation: ok")
+6
View File
@@ -14,6 +14,7 @@ local C = {
calls.push = { ptr, w, h }
end,
love_android_secondary_enable = function(on) calls.enabled = on end,
love_android_secondary_target = function(target) calls.target = target end,
love_android_secondary_detected = function() return 1 end,
love_android_present_secondary = function(ptr, w, h, background, cover)
calls.present = { ptr, w, h, background, cover }
@@ -44,6 +45,10 @@ T.eq(SecondScreen.push(image, 160, 144, 0x112233, "secondary:cover"), true,
"extended Android presentation accepts frame metadata")
T.same(calls.present, { "pixels", 160, 144, 0x112233, 1 },
"cover and RGB background reach the native bridge")
T.eq(calls.target, 2, "secondary routing reaches the optional native bridge")
T.eq(SecondScreen.push(image, 160, 144, 0x112233, "handheld"), true,
"handheld routing remains a contain presentation")
T.eq(calls.target, 1, "handheld routing reaches the optional native bridge")
T.eq(SecondScreen.push(image, 160, 144, 0x112233, "secondary"), true,
"contain presentation remains available")
T.same(calls.present, { "pixels", 160, 144, 0x112233, 0 },
@@ -52,6 +57,7 @@ T.eq(SecondScreen.push(image, 160, 144, nil, "secondary:cover"), true,
"a fit preference can request extended presentation by itself")
T.same(calls.present, { "pixels", 160, 144, 0, 1 },
"preference-only presentation defaults to a black background")
T.eq(calls.target, 2, "a suffixed route keeps its target")
T.eq(SecondScreen.push(image, 160, 144), true,
"the original push ABI remains available")
T.same(calls.push, { "pixels", 160, 144 },