authorclover caruso <git@paperclover.net> 2026-10-16 13:51:14-07:00
committerclover caruso <git@paperclover.net> 2026-10-16 14:25:20-07:00
log20174448f624d39ffedda6ad2291f07b96542644
tree21a40c982e7614309df3c330ac47e951e43bd755
parent4534c84f0003ef1bc0827ad8d1ab37a6ce30655f
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover)

fix(mobile): draw the page on Android 8 after the keyboard resizes it

On a device without Vulkan, wgpu kept one EGL surface across resizes, and the emulator's EGL took the window's new size a resize late. A cold start that opened with the keyboard showing drew the page into buffers of the old height, leaving it blank above a black band until a tap; the next resize left a stray copy of the page's title box near the bottom. A resize under GL now makes a new surface, as GLSurfaceView does, and the Android check fails when the reopened page leaves the screen black. - The page draws on Android 8 as soon as it opens, without a tap. Fixes #153 Assisted-by: claude-opus-5.5

2 files changed, 29 insertions(+), 3 deletions(-)

apps/android/check.py+11-1
......@@ -6,12 +6,14 @@ the caller starts and stops:
66
77It types a token into the page, waits until the section reports it saved, force-stops the app,
88turns the network off, starts it again and screenshots the page, then reads the section file
9back with onestore's inspect example and looks for the token in it.
9back with onestore's inspect example and looks for the token in it. The page must have drawn
10where the keyboard was, which GL on Android 8 once left black.
1011"""
1112import argparse
1213import json
1314from pathlib import Path
1415import secrets
16import struct
1517import subprocess
1618import time
1719
......@@ -70,6 +72,11 @@ def main():
7072 adb('shell', 'cmd', 'connectivity', 'airplane-mode', 'enable')
7173 launch()
7274 (output / 'reopened.png').write_bytes(adb('exec-out', 'screencap', '-p', binary=True))
75 # A raw capture: width and height, then RGBA rows after a header whose size varies by release.
76 screen = adb('exec-out', 'screencap', binary=True)
77 columns, rows = struct.unpack_from('<2I', screen)
78 pixel = len(screen) - columns * rows * 4 + (rows * 3 // 4 * columns + columns // 2) * 4
79 drawn = any(screen[pixel:pixel + 3])
7380 section = output / 'section.one'
7481 section.write_bytes(adb('exec-out', 'run-as', PACKAGE, 'cat', SECTION, binary=True))
7582 (output / 'logcat.txt').write_text(adb('logcat', '-d'))
......@@ -84,11 +91,14 @@ def main():
8491 'intact': inspected.endswith(' 0 checksum mismatches'),
8592 # Text is stored as UTF-16, or as single bytes where it fits them.
8693 'tokenInSection': token.encode('utf-16-le') in data or token.encode() in data,
94 'drawn': drawn,
8795 }
8896 (output / 'result.json').write_text(json.dumps(result, indent=2) + '\n')
8997 print(json.dumps(result, indent=2))
9098 if not (result['intact'] and result['tokenInSection']):
9199 raise SystemExit('The edit did not survive')
100 if not drawn:
101 raise SystemExit('The reopened page left the screen black')
92102
93103
94104if __name__ == '__main__':
crates/mobile/src/lib.rs+18-2
......@@ -1167,8 +1167,8 @@ pub unsafe extern "C" fn sb_view_free(view: *mut View) {
11671167
11681168#[unsafe(no_mangle)]
11691169pub extern "C" fn sb_view_resize(view: &mut View, width: f32, height: f32, scale: f32) {
1170 let gpu = gpu();
1171 let Some(gpu) = gpu.as_ref() else { return };
1170 let guard = gpu();
1171 let Some(gpu) = guard.as_ref() else { return };
11721172 let Some(pixels) = report(view_pixels(
11731173 [width, height],
11741174 scale,
......@@ -1182,6 +1182,22 @@ pub extern "C" fn sb_view_resize(view: &mut View, width: f32, height: f32, scale
11821182 let _ = report(canvas.page.scale_factor_changed(scale));
11831183 }
11841184 let _ = report(canvas.page.resized(pixels));
1185 #[cfg(target_os = "android")]
1186 if gpu.adapter.get_info().backend == wgpu::Backend::Gl
1187 && let Some(Presentation {
1188 surface,
1189 _window: window,
1190 ..
1191 }) = view.presentation.take()
1192 {
1193 // The emulator's EGL takes a window's new size late, so GL makes a new surface, as
1194 // GLSurfaceView does.
1195 drop(surface);
1196 drop(guard);
1197 // SAFETY: `window` holds a reference until the new presentation has acquired its own.
1198 view.presentation = report(unsafe { Presentation::new(window.0.as_ptr(), pixels) });
1199 return;
1200 }
11851201 if let Some(presentation) = &mut view.presentation {
11861202 presentation.config.width = pixels[0];
11871203 presentation.config.height = pixels[1];