Stale GL_UNPACK state in gl4es and a missing pre-release GPU sync caused diagonal-banded corruption in the 3D view. Synthetic key press/release must track the real controller button or touch down/up edges, not fire together in one call - fixes a typed movement key sticking forever, then a follow-up that had silently broken A-button jump and on-screen keyboard arrow keys. Also adds force-rebuild.sh for the known stageEngine stale-cache issue.
This commit is contained in:
@@ -4,6 +4,7 @@ import android.app.Activity;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
@@ -17,13 +18,19 @@ import org.libsdl.app.SDLActivity;
|
||||
* The on-screen keyboard quad (see OverlayPanel for the shared off-screen-
|
||||
* render/touch/cursor plumbing this builds on) - a hand-built, full
|
||||
* US-layout key grid (there's no system IME to borrow once immersive) plus
|
||||
* a text field echoing what's been sent. Each key press is forwarded
|
||||
* straight to the game as real input (see handleKeyPress()/
|
||||
* handleCharKeyPress()) - non-printable keys as an
|
||||
* a text field echoing what's been sent. Every key that reaches the game
|
||||
* (everything but Close/Shift/Ctrl/Alt, which are local UI actions - see
|
||||
* handleKeyDown()/handleKeyUp() and handleCharKeyDown()/handleCharKeyUp())
|
||||
* is driven by that key button's own ACTION_DOWN/ACTION_UP (see
|
||||
* charKeyTouchListener()/controlKeyTouchListener()), which
|
||||
* OverlayPanel.handleTouch() dispatches for exactly as long as the
|
||||
* controller's trigger is physically held while aimed at it - so a key
|
||||
* held on this keyboard behaves like a real key held on a physical one,
|
||||
* not an instant tap. Non-printable keys go through an
|
||||
* SDLActivity.onNativeKeyDown()/onNativeKeyUp() pair, the same one a
|
||||
* physical Bluetooth keyboard's presses already drive, and printable keys
|
||||
* via a synthesized SDL_TEXTINPUT event pushed from native code (see
|
||||
* commitPrintableChar()/nativeSendPrintableChar() in questshock_native.c) -
|
||||
* physical Bluetooth keyboard's presses already drive; printable keys go
|
||||
* through a synthesized SDL_TEXTINPUT/SDL_KEYUP pair pushed from native
|
||||
* code (see nativeSendPrintableCharDown()/Up() in questshock_native.c) -
|
||||
* except while a Ctrl/Alt "armed" modifier is active, when they go through
|
||||
* the same onNativeKeyDown()/onNativeKeyUp() path instead, so the modifier
|
||||
* carries across (see toggleModifier()). Shift is a purely local layer
|
||||
@@ -93,9 +100,9 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
// A key whose printed character (and, while unmodified, whose game
|
||||
// input) depends on the Shift layer - covers letters, digits, and
|
||||
// punctuation uniformly (e.g. 'q'/'Q', '1'/'!', '-'/'_'). androidKeyCode
|
||||
// is only used for the Ctrl/Alt-combo path (see handleCharKeyPress()) -
|
||||
// the unmodified path goes through commitPrintableChar() instead, which
|
||||
// doesn't need an Android keycode at all.
|
||||
// is only used for the Ctrl/Alt-combo path (see handleCharKeyDown()) -
|
||||
// the unmodified path goes through nativeSendPrintableCharDown()/Up()
|
||||
// instead, which doesn't need an Android keycode at all.
|
||||
private static final class CharKey {
|
||||
final char base;
|
||||
final char shifted;
|
||||
@@ -224,7 +231,7 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
|
||||
Button close = new Button(activity);
|
||||
close.setText("Close");
|
||||
close.setOnClickListener(v -> handleKeyPress("Close"));
|
||||
close.setOnClickListener(v -> nativeRequestClose());
|
||||
close.setOnTouchListener(CLICK_ON_TOUCH_UP);
|
||||
bar.addView(close,
|
||||
new LinearLayout.LayoutParams(CLOSE_BUTTON_WIDTH, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
@@ -234,9 +241,10 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
|
||||
// Each row fills the panel's remaining height evenly (weight 1 on the
|
||||
// row itself). Accepts a mix of String (a fixed-action control key,
|
||||
// handled by handleKeyPress()) and CharKey (a Shift-sensitive character
|
||||
// key, handled by handleCharKeyPress()) per item, so a row can match
|
||||
// real keyboard geometry (e.g. "Tab" followed by a run of CharKeys).
|
||||
// handled by handleKeyDown()/handleKeyUp()) and CharKey (a
|
||||
// Shift-sensitive character key, handled by handleCharKeyDown()/
|
||||
// handleCharKeyUp()) per item, so a row can match real keyboard
|
||||
// geometry (e.g. "Tab" followed by a run of CharKeys).
|
||||
// Within a row, CharKeys and most control keys share width equally
|
||||
// except Space (wider, like a real keyboard) and Esc/Back/Enter/Tab/
|
||||
// Shift/Ctrl/Alt (narrower, see keyWeight()).
|
||||
@@ -245,13 +253,12 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
for (Object item : items) {
|
||||
Button key = new Button(activity);
|
||||
key.setOnTouchListener(CLICK_ON_TOUCH_UP);
|
||||
float weight;
|
||||
if (item instanceof CharKey) {
|
||||
CharKey charKey = (CharKey) item;
|
||||
charKey.button = key;
|
||||
key.setText(charKeyLabel(charKey, shiftActive));
|
||||
key.setOnClickListener(v -> handleCharKeyPress(charKey));
|
||||
key.setOnTouchListener(charKeyTouchListener(charKey));
|
||||
charKeys.add(charKey);
|
||||
// Space is a CharKey too (base == shifted == ' '), but keeps
|
||||
// its traditional wide key like the other control keys do.
|
||||
@@ -259,8 +266,19 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
} else {
|
||||
String label = (String) item;
|
||||
key.setText(label);
|
||||
key.setOnClickListener(v -> handleKeyPress(label));
|
||||
weight = keyWeight(label);
|
||||
// Shift/Ctrl/Alt are local UI toggles with their own
|
||||
// instantaneous press/release semantics (see
|
||||
// toggleModifier()), not a value forwarded to the game for
|
||||
// as long as the key is held - a plain click is enough.
|
||||
// Every other control key reaches the game and needs real
|
||||
// hold-duration semantics (see controlKeyTouchListener()).
|
||||
if (label.equals("Shift") || label.equals("Ctrl") || label.equals("Alt")) {
|
||||
key.setOnClickListener(v -> handleToggleKey(label));
|
||||
key.setOnTouchListener(CLICK_ON_TOUCH_UP);
|
||||
} else {
|
||||
key.setOnTouchListener(controlKeyTouchListener(label));
|
||||
}
|
||||
if (label.equals("Shift")) {
|
||||
shiftButton = key;
|
||||
} else if (label.equals("Ctrl")) {
|
||||
@@ -276,6 +294,54 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||
}
|
||||
|
||||
// Drives a control key (everything controlKeyCode() maps, i.e. every
|
||||
// key in addRow() except CharKeys and the Shift/Ctrl/Alt toggles) from
|
||||
// its button's own touch lifecycle, so it reaches the game for exactly
|
||||
// as long as the controller's trigger is held on it (see the class
|
||||
// doc). ACTION_CANCEL (the ray straying off the button, or another
|
||||
// overlay claiming it - see xr_input.c) releases the key the same as
|
||||
// ACTION_UP, so a key can never be left stuck down.
|
||||
private View.OnTouchListener controlKeyTouchListener(String label) {
|
||||
return (v, event) -> {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
v.setPressed(true);
|
||||
handleKeyDown(label);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
v.setPressed(false);
|
||||
handleKeyUp(label);
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
v.setPressed(false);
|
||||
handleKeyUp(label);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
// Same as controlKeyTouchListener() above, for a CharKey.
|
||||
private View.OnTouchListener charKeyTouchListener(CharKey key) {
|
||||
return (v, event) -> {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
v.setPressed(true);
|
||||
handleCharKeyDown(key);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
v.setPressed(false);
|
||||
handleCharKeyUp(key);
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
v.setPressed(false);
|
||||
handleCharKeyUp(key);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
private static float keyWeight(String label) {
|
||||
if (label.equals("Back") || label.equals("Esc") || label.equals("Enter")
|
||||
|| label.equals("Tab") || label.equals("Shift") || label.equals("Ctrl")
|
||||
@@ -310,38 +376,31 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// "Close" (the title bar's button - see buildTitleBar()) hides this
|
||||
// overlay's quad entirely (see nativeRequestClose()) and never reaches
|
||||
// the game. Shift/Ctrl/Alt are handled separately below (Shift is a
|
||||
// local layer toggle; Ctrl/Alt are one-shot armed modifiers) since
|
||||
// neither sends a plain key event of its own the way every other
|
||||
// control key here does. Everything else (Back/Esc/Enter/Tab/arrows/
|
||||
// F1-F12) is non-printable, so it's forwarded as a real Android key
|
||||
// event via controlKeyCode() above, then releases any armed Ctrl/Alt
|
||||
// modifier (this key just consumed it). This all matters beyond just
|
||||
// the visible typedTextView below: the engine's cutscene skip handler
|
||||
// (cutsloop.c's cutscene_key_handler()) only reacts to Esc/Enter/Space,
|
||||
// so those need to reach the game as actual game input, not just be
|
||||
// echoed locally.
|
||||
private void handleKeyPress(String label) {
|
||||
if (label.equals("Close")) {
|
||||
nativeRequestClose();
|
||||
return;
|
||||
}
|
||||
// Shift is a local layer toggle; Ctrl/Alt are one-shot armed modifiers
|
||||
// (see toggleModifier()) - neither is forwarded to the game as a key of
|
||||
// its own, so a plain click (rather than the hold-duration dispatch
|
||||
// every other key below uses) is enough.
|
||||
private void handleToggleKey(String label) {
|
||||
if (label.equals("Shift")) {
|
||||
shiftActive = !shiftActive;
|
||||
updateShiftVisual();
|
||||
return;
|
||||
}
|
||||
if (label.equals("Ctrl") || label.equals("Alt")) {
|
||||
toggleModifier(label);
|
||||
return;
|
||||
}
|
||||
toggleModifier(label);
|
||||
}
|
||||
|
||||
// Everything controlKeyCode() maps (Back/Esc/Enter/Tab/arrows/F1-F12)
|
||||
// is non-printable, so it's forwarded as a real Android key event via
|
||||
// that code - down on this key's own ACTION_DOWN, up on ACTION_UP/
|
||||
// CANCEL (see controlKeyTouchListener()), the same down/up split a
|
||||
// physical Bluetooth keyboard's own key events already carry. This all
|
||||
// matters beyond just the visible typedTextView below: the engine's
|
||||
// cutscene skip handler (cutsloop.c's cutscene_key_handler()) only
|
||||
// reacts to Esc/Enter/Space, so those need to reach the game as actual
|
||||
// game input, not just be echoed locally.
|
||||
private void handleKeyDown(String label) {
|
||||
int keyCode = controlKeyCode(label);
|
||||
SDLActivity.onNativeKeyDown(keyCode);
|
||||
SDLActivity.onNativeKeyUp(keyCode);
|
||||
releaseArmedModifiers();
|
||||
|
||||
if (label.equals("Back")) {
|
||||
if (typedText.length() > 0) {
|
||||
@@ -353,31 +412,45 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
typedTextView.setText(typedText.toString());
|
||||
}
|
||||
|
||||
private void handleKeyUp(String label) {
|
||||
SDLActivity.onNativeKeyUp(controlKeyCode(label));
|
||||
releaseArmedModifiers();
|
||||
}
|
||||
|
||||
// A CharKey's printed character depends only on the Shift layer
|
||||
// (updateShiftVisual() keeps its button's label in sync). Which game
|
||||
// input it produces depends on whether a modifier is armed: unmodified,
|
||||
// it goes through commitPrintableChar() below like a normal typed
|
||||
// character; with Ctrl/Alt armed, it instead goes through the same
|
||||
// onNativeKeyDown()/onNativeKeyUp() path handleKeyPress() uses for
|
||||
// control keys, since that's the only path that carries
|
||||
// ev.key.keysym.mod through to the engine (sdl_events.c's pump_events()
|
||||
// reads Ctrl/Alt only off that path, never off SDL_TEXTINPUT).
|
||||
private void handleCharKeyPress(CharKey key) {
|
||||
// it goes through nativeSendPrintableCharDown()/Up() below like a
|
||||
// normal typed character; with Ctrl/Alt armed, it instead goes through
|
||||
// the same onNativeKeyDown()/onNativeKeyUp() path handleKeyDown()/
|
||||
// handleKeyUp() use for control keys, since that's the only path that
|
||||
// carries ev.key.keysym.mod through to the engine (sdl_events.c's
|
||||
// pump_events() reads Ctrl/Alt only off that path, never off
|
||||
// SDL_TEXTINPUT).
|
||||
private void handleCharKeyDown(CharKey key) {
|
||||
char c = shiftActive ? key.shifted : key.base;
|
||||
if (ctrlArmed || altArmed) {
|
||||
SDLActivity.onNativeKeyDown(key.androidKeyCode);
|
||||
SDLActivity.onNativeKeyUp(key.androidKeyCode);
|
||||
releaseArmedModifiers();
|
||||
} else {
|
||||
commitPrintableChar(c);
|
||||
nativeSendPrintableCharDown(c);
|
||||
}
|
||||
typedText.append(c);
|
||||
typedTextView.setText(typedText.toString());
|
||||
}
|
||||
|
||||
private void handleCharKeyUp(CharKey key) {
|
||||
char c = shiftActive ? key.shifted : key.base;
|
||||
if (ctrlArmed || altArmed) {
|
||||
SDLActivity.onNativeKeyUp(key.androidKeyCode);
|
||||
releaseArmedModifiers();
|
||||
} else {
|
||||
nativeSendPrintableCharUp(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl/Alt are "armed" rather than held: pressing one immediately sends
|
||||
// its keydown (so SDL's own modifier tracking picks it up for whatever
|
||||
// key comes next - see handleCharKeyPress()/handleKeyPress()) and
|
||||
// key comes next - see handleCharKeyDown()/handleKeyDown()) and
|
||||
// brackets its label for feedback; pressing the same key again before
|
||||
// it's been used cancels it (sends the matching keyup, un-brackets).
|
||||
// The normal case - actually being consumed by the next key press - is
|
||||
@@ -400,10 +473,10 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
button.setText(nowArmed ? "[" + label + "]" : label);
|
||||
}
|
||||
|
||||
// Called after any key actually reaches the game (a control key in
|
||||
// handleKeyPress(), or a CharKey in handleCharKeyPress()) so an armed
|
||||
// Ctrl/Alt only ever applies to the very next key, then releases -
|
||||
// matching a real Ctrl/Alt+key combo's keyup once the combo is done.
|
||||
// Called after any key actually reaches the game (a control key's
|
||||
// handleKeyUp(), or a CharKey's handleCharKeyUp()) so an armed Ctrl/Alt
|
||||
// only ever applies to the very next key, then releases - matching a
|
||||
// real Ctrl/Alt+key combo's keyup once the combo is done.
|
||||
private void releaseArmedModifiers() {
|
||||
if (ctrlArmed) {
|
||||
SDLActivity.onNativeKeyUp(KeyEvent.KEYCODE_CTRL_LEFT);
|
||||
@@ -429,17 +502,15 @@ public class KeyboardOverlay extends OverlayPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// Synthesizes the SDL_TEXTINPUT event pump_events() (sdl_events.c)
|
||||
// requires for printable characters (see handleCharKeyPress() above),
|
||||
// by building and pushing it directly in native code (see
|
||||
// nativeSendPrintableChar() in questshock_native.c) rather than through
|
||||
// Android's IME (there's no real IME session behind this off-screen,
|
||||
// Synthesizes the SDL_TEXTINPUT/SDL_KEYUP pair pump_events()
|
||||
// (sdl_events.c) requires for printable characters (see
|
||||
// handleCharKeyDown()/handleCharKeyUp() above), by building and
|
||||
// pushing them directly in native code rather than through Android's
|
||||
// IME (there's no real IME session behind this off-screen,
|
||||
// never-attached grid for that plumbing to hook into).
|
||||
private static void commitPrintableChar(char c) {
|
||||
nativeSendPrintableChar(c);
|
||||
}
|
||||
private static native void nativeSendPrintableCharDown(char c);
|
||||
|
||||
private static native void nativeSendPrintableChar(char c);
|
||||
private static native void nativeSendPrintableCharUp(char c);
|
||||
|
||||
// Hides this overlay's quad (xr_overlay_set_visible(..., false)) - the
|
||||
// only way to do so, since the controller's menu button doesn't affect
|
||||
|
||||
Reference in New Issue
Block a user