Keyboard strobing moved to a new thread so that fast typing is possible even if the game's update method is running slowly

This commit is contained in:
minjaesong
2021-11-06 11:30:42 +09:00
parent ddd7492922
commit 7503876d1f
15 changed files with 240 additions and 131 deletions

View File

@@ -25,7 +25,9 @@ import net.torvald.terrarum.controller.TerrarumController;
import net.torvald.terrarum.controller.XinputControllerAdapter;
import net.torvald.terrarum.gameactors.BlockMarkerActor;
import net.torvald.terrarum.gamecontroller.IME;
import net.torvald.terrarum.gamecontroller.InputStrober;
import net.torvald.terrarum.gamecontroller.KeyToggler;
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent;
import net.torvald.terrarum.gameworld.GameWorld;
import net.torvald.terrarum.imagefont.TinyAlphNum;
import net.torvald.terrarum.langpack.Lang;
@@ -234,7 +236,7 @@ public class App implements ApplicationListener {
private static com.badlogic.gdx.graphics.Color gradWhiteTop = new com.badlogic.gdx.graphics.Color(0xf8f8f8ff);
private static com.badlogic.gdx.graphics.Color gradWhiteBottom = new com.badlogic.gdx.graphics.Color(0xd8d8d8ff);
private static Screen currentScreen;
private static TerrarumGamescreen currentScreen;
private static LoadScreenBase currentSetLoadScreen;
private void initViewPort(int width, int height) {
@@ -284,6 +286,8 @@ public class App implements ApplicationListener {
"xinput", "xbox", "game", "joy", "pad"
};
public static InputStrober inputStrober = InputStrober.INSTANCE; // kinda dummy field
public static Screen getCurrentScreen() {
return currentScreen;
}
@@ -709,6 +713,8 @@ public class App implements ApplicationListener {
resizeReqSize = new Point2i(width, height);
}
@Override
public void dispose() {
System.out.println("Goodbye !");
@@ -762,6 +768,8 @@ public class App implements ApplicationListener {
Terrarum.INSTANCE.dispose();
inputStrober.dispose();
deleteTempfiles();
}
@@ -785,15 +793,19 @@ public class App implements ApplicationListener {
}
public static void setScreen(Screen screen) {
if (!(screen instanceof TerrarumGamescreen)) {
throw new IllegalArgumentException("Screen must be instance of TerrarumGameScreen: " + screen.getClass().getCanonicalName());
}
if (screen instanceof LoadScreenBase) {
throw new RuntimeException(
"Loadscreen '" + screen.getClass().getSimpleName() + "' must be set with 'setLoadScreen()' method");
}
_setScr(screen);
_setScr((TerrarumGamescreen) screen);
}
private static void _setScr(Screen screen) {
private static void _setScr(TerrarumGamescreen screen) {
printdbg("AppLoader-Static", "Changing screen to " + screen.getClass().getCanonicalName());
@@ -1305,4 +1317,12 @@ public class App implements ApplicationListener {
public static long getTIME_T() {
return System.currentTimeMillis() / 1000L;
}
/**
* Just an event handler I'm slipping in
* @param event
*/
public static void inputStrobed(TerrarumKeyboardEvent event) {
currentScreen.inputStrobed(event);
}
}

View File

@@ -8,6 +8,7 @@ import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameactors.BlockMarkerActor
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.gameitem.ItemID
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.IngameRenderer
@@ -31,7 +32,7 @@ import java.util.concurrent.locks.Lock
* Although the game (as product) can have infinitely many stages/planets/etc., those stages must be manually managed by YOU;
* this instance only stores the stage that is currently being used.
*/
open class IngameInstance(val batch: SpriteBatch, val isMultiplayer: Boolean = false) : Screen {
open class IngameInstance(val batch: SpriteBatch, val isMultiplayer: Boolean = false) : TerrarumGamescreen {
open protected val actorMBRConverter = object : MBRConverter<ActorWithBody> {
override fun getDimensions(): Int = 2
@@ -134,6 +135,9 @@ open class IngameInstance(val batch: SpriteBatch, val isMultiplayer: Boolean = f
override fun hide() {
}
override fun inputStrobed(e: TerrarumKeyboardEvent) {
}
override fun show() {
// the very basic show() implementation

View File

@@ -4,11 +4,12 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.ScreenAdapter
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.utils.Disposable
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.util.CircularArray
open class LoadScreenBase : ScreenAdapter(), Disposable {
open class LoadScreenBase : ScreenAdapter(), Disposable, TerrarumGamescreen {
open var preLoadJob: (LoadScreenBase) -> Unit = {}
open var screenToLoad: IngameInstance? = null
@@ -82,4 +83,7 @@ open class LoadScreenBase : ScreenAdapter(), Disposable {
override fun resize(width: Int, height: Int) {
initViewPort(App.scr.width, App.scr.height)
}
override fun inputStrobed(e: TerrarumKeyboardEvent) {
}
}

View File

@@ -53,7 +53,6 @@ typealias RGBA8888 = Int
object Terrarum : Disposable {
init {
IngameController.KEYBOARD_DELAYS[1]
}
/**

View File

@@ -0,0 +1,11 @@
package net.torvald.terrarum
import com.badlogic.gdx.Screen
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
/**
* Created by minjaesong on 2021-11-06.
*/
interface TerrarumGamescreen : Screen {
fun inputStrobed(e: TerrarumKeyboardEvent)
}

View File

@@ -18,6 +18,7 @@ import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF
import net.torvald.terrarum.console.CommandDict
import net.torvald.terrarum.gameactors.*
import net.torvald.terrarum.gameactors.ai.ActorAI
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.WorldTime
import net.torvald.terrarum.gameworld.fmod
@@ -389,6 +390,10 @@ class TitleScreen(batch: SpriteBatch) : IngameInstance(batch) {
warning32bitJavaIcon.texture.dispose()
}
override fun inputStrobed(e: TerrarumKeyboardEvent) {
uiContainer.forEach { it?.inputStrobed(e) }
}
fun setCameraPosition(newX: Float, newY: Float) {

View File

@@ -277,112 +277,5 @@ class IngameController(val terrarumIngame: TerrarumIngame) : InputAdapter() {
return true
}
companion object {
data class TerrarumKeyboardEvent(
val type: Int,
val character: String?, // representative key symbol
val headkey: Int, // representative keycode
val repeatCount: Int,
val keycodes: IntArray
)
private const val KEY_DOWN = 0
private const val KEY_CHANGE = 1
const val N_KEY_ROLLOVER = 8
var KEYBOARD_DELAYS = longArrayOf(0L,250000000L,0L,25000000L,0L)
private var stroboTime = 0L
private var stroboStatus = 0
private var repeatCount = 0
private var oldKeys = IntArray(N_KEY_ROLLOVER) { 0 }
/** always Low Layer */
// private var keymap = IME.getLowLayerByName(App.getConfigString("basekeyboardlayout"))
fun resetKeyboardStrobo() {
stroboStatus = 0
repeatCount = 0
}
// code proudly stolen from tsvm's TVDOS.SYS
fun withKeyboardEvent(callback: (TerrarumKeyboardEvent) -> Unit) {
val keys = strobeKeys()
var keyChanged = !arrayEq(keys, oldKeys)
val keyDiff = arrayDiff(keys, oldKeys)
val keymap = IME.getLowLayerByName(App.getConfigString("basekeyboardlayout"))
if (stroboStatus % 2 == 0 && keys[0] != 0) {
stroboStatus += 1
stroboTime = System.nanoTime()
repeatCount += 1
val shiftin = keys.contains(Keys.SHIFT_LEFT) || keys.contains(Keys.SHIFT_RIGHT)
val keysym0 = keysToStr(keymap, keys)
val newKeysym0 = keysToStr(keymap, keyDiff)
val keysym = if (keysym0 == null) null
else if (shiftin && keysym0[1]?.isNotBlank() == true) keysym0[1]
else keysym0[0]
val newKeysym = if (newKeysym0 == null) null
else if (shiftin && newKeysym0[1]?.isNotBlank() == true) newKeysym0[1]
else newKeysym0[0]
val headKeyCode = if (keyDiff.size < 1) keys[0] else keyDiff[0]
if (!keyChanged) {
// println("KEY_DOWN '$keysym' ($headKeyCode) $repeatCount; ${keys.joinToString()}")
callback(TerrarumKeyboardEvent(KEY_DOWN, keysym, headKeyCode, repeatCount, keys))
}
else if (newKeysym != null) {
// println("KEY_DOWC '$newKeysym' ($headKeyCode) $repeatCount; ${keys.joinToString()}")
callback(TerrarumKeyboardEvent(KEY_DOWN, newKeysym, headKeyCode, repeatCount, keys))
}
oldKeys = keys // don't put this outside of if-cascade
}
else if (keyChanged || keys[0] == 0) {
stroboStatus = 0
repeatCount = 0
if (keys[0] == 0) keyChanged = false
}
else if (stroboStatus % 2 == 1 && System.nanoTime() - stroboTime < KEYBOARD_DELAYS[stroboStatus]) {
Thread.sleep(1L)
}
else {
stroboStatus += 1
if (stroboStatus >= 4)
stroboStatus = 2
}
}
private fun keysToStr(keymap: TerrarumKeyLayout, keys: IntArray): Array<String?>? {
if (keys.size == 0) return null
val headkey = keys[0]
return keymap.symbols?.get(headkey)
}
private fun strobeKeys(): IntArray {
var keysPushed = 0
val keyEventBuffers = IntArray(N_KEY_ROLLOVER) { 0 }
for (k in 1..254) {
if (Gdx.input.isKeyPressed(k)) {
keyEventBuffers[keysPushed] = k
keysPushed += 1
}
if (keysPushed >= N_KEY_ROLLOVER) break
}
return keyEventBuffers
}
private fun arrayEq(a: IntArray, b: IntArray): Boolean {
for (i in 0 until a.size) {
if (a[i] != b.getOrNull(i)) return false
}
return true
}
private fun arrayDiff(a: IntArray, b: IntArray): IntArray {
return a.filter { !b.contains(it) }.toIntArray()
}
}
private inline fun BitSet.bitCount() = this.cardinality()
}

View File

@@ -0,0 +1,136 @@
package net.torvald.terrarum.gamecontroller
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import net.torvald.terrarum.App
/**
* Created by minjaesong on 2021-11-06.
*/
object InputStrober {
const val KEY_DOWN = 0
const val KEY_CHANGE = 1
const val N_KEY_ROLLOVER = 8
var KEYBOARD_DELAYS = longArrayOf(0L,250000000L,0L,25000000L,0L)
private var stroboTime = 0L
private var stroboStatus = 0
private var repeatCount = 0
private var oldKeys = IntArray(N_KEY_ROLLOVER) { 0 }
/** always Low Layer */
// private var keymap = IME.getLowLayerByName(App.getConfigString("basekeyboardlayout"))
private val thread = Thread { while (!Thread.interrupted()) {
if (Gdx.input != null) withKeyboardEvent()
} }
init {
println("InputStrobe start")
thread.start()
}
fun dispose() {
thread.interrupt()
}
fun resetKeyboardStrobo() {
stroboStatus = 0
repeatCount = 0
}
// code proudly stolen from tsvm's TVDOS.SYS
private fun withKeyboardEvent() {
val keys = strobeKeys()
var keyChanged = !arrayEq(keys, oldKeys)
val keyDiff = arrayDiff(keys, oldKeys)
val keymap = IME.getLowLayerByName(App.getConfigString("basekeyboardlayout"))
// println("Key strobed: ${keys.joinToString()}")
if (stroboStatus % 2 == 0 && keys[0] != 0) {
stroboStatus += 1
stroboTime = System.nanoTime()
repeatCount += 1
val shiftin = keys.contains(Input.Keys.SHIFT_LEFT) || keys.contains(Input.Keys.SHIFT_RIGHT)
val keysym0 = keysToStr(keymap, keys)
val newKeysym0 = keysToStr(keymap, keyDiff)
val keysym = if (keysym0 == null) null
else if (shiftin && keysym0[1]?.isNotBlank() == true) keysym0[1]
else keysym0[0]
val newKeysym = if (newKeysym0 == null) null
else if (shiftin && newKeysym0[1]?.isNotBlank() == true) newKeysym0[1]
else newKeysym0[0]
val headKeyCode = if (keyDiff.size < 1) keys[0] else keyDiff[0]
if (!keyChanged) {
// println("KEY_DOWN '$keysym' ($headKeyCode) $repeatCount; ${keys.joinToString()}")
App.inputStrobed(TerrarumKeyboardEvent(KEY_DOWN, keysym, headKeyCode, repeatCount, keys))
}
else if (newKeysym != null) {
// println("KEY_DOWC '$newKeysym' ($headKeyCode) $repeatCount; ${keys.joinToString()}")
App.inputStrobed(TerrarumKeyboardEvent(KEY_DOWN, newKeysym, headKeyCode, repeatCount, keys))
}
oldKeys = keys // don't put this outside of if-cascade
}
else if (keyChanged || keys[0] == 0) {
stroboStatus = 0
repeatCount = 0
if (keys[0] == 0) keyChanged = false
}
else if (stroboStatus % 2 == 1 && System.nanoTime() - stroboTime < KEYBOARD_DELAYS[stroboStatus]) {
Thread.sleep(1L)
}
else {
stroboStatus += 1
if (stroboStatus >= 4)
stroboStatus = 2
}
}
private fun keysToStr(keymap: TerrarumKeyLayout, keys: IntArray): Array<String?>? {
if (keys.isEmpty()) return null
val headkey = keys[0]
return keymap.symbols?.get(headkey)
}
private fun strobeKeys(): IntArray {
var keysPushed = 0
val keyEventBuffers = IntArray(N_KEY_ROLLOVER) { 0 }
for (k in 1..254) {
if (Gdx.input.isKeyPressed(k)) {
keyEventBuffers[keysPushed] = k
keysPushed += 1
}
if (keysPushed >= N_KEY_ROLLOVER) break
}
return keyEventBuffers
}
private fun arrayEq(a: IntArray, b: IntArray): Boolean {
for (i in a.indices) {
if (a[i] != b.getOrNull(i)) return false
}
return true
}
private fun arrayDiff(a: IntArray, b: IntArray): IntArray {
return a.filter { !b.contains(it) }.toIntArray()
}
}
data class TerrarumKeyboardEvent(
val type: Int,
val character: String?, // representative key symbol
val headkey: Int, // representative keycode
val repeatCount: Int,
val keycodes: IntArray
)

View File

@@ -11,6 +11,7 @@ import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockPropUtil
import net.torvald.terrarum.gameactors.*
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.gameitem.ItemID
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
@@ -412,6 +413,9 @@ class BuildingMaker(batch: SpriteBatch) : IngameInstance(batch) {
uiPenMenu.dispose()
}
override fun inputStrobed(e: TerrarumKeyboardEvent) {
}
private fun makePenWork(x: Int, y: Int) {
val world = gameWorld
val palSelection = uiPaletteSelector.fore

View File

@@ -18,6 +18,7 @@ import net.torvald.terrarum.console.Authenticator
import net.torvald.terrarum.gameactors.*
import net.torvald.terrarum.gamecontroller.IngameController
import net.torvald.terrarum.gamecontroller.KeyToggler
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.gameitem.GameItem
import net.torvald.terrarum.gameitem.inInteractableRange
import net.torvald.terrarum.gameparticles.ParticleBase
@@ -1173,6 +1174,10 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
}
}
override fun inputStrobed(e: TerrarumKeyboardEvent) {
uiContainer.forEach { it?.inputStrobed(e) }
}
fun activateDormantActor(actor: Actor) {
if (App.IS_DEVELOPMENT_BUILD && !isInactive(actor.referenceID)) {
/*if (isActive(actor.referenceID))

View File

@@ -11,6 +11,7 @@ import net.torvald.terrarum.App
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.DefaultConfig
import net.torvald.terrarum.gamecontroller.IME
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.linearSearch
import net.torvald.terrarum.ui.*
@@ -176,6 +177,11 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
// addUIitem(keyboardTestPanel)
}
override fun inputStrobed(e: TerrarumKeyboardEvent) {
super.inputStrobed(e)
keyboardTestPanel.inputStrobed(e)
}
private fun resetKeyConfig() {
listOf("control_key_up",
"control_key_left",

View File

@@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.App.printdbgerr
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.serialise.WriteConfig
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UICanvas
@@ -266,7 +267,9 @@ open class UIRemoCon(val parent: TitleScreen, val treeRoot: QNDTreeNode<String>)
return true
}
override fun inputStrobed(e: TerrarumKeyboardEvent) {
openUI?.inputStrobed(e)
}
class UIRemoConElement(uiRemoCon: UIRemoCon, val labels: Array<String>, val tags: Array<Array<String>>) {

View File

@@ -6,6 +6,7 @@ import com.badlogic.gdx.utils.Disposable
import net.torvald.terrarum.App
import net.torvald.terrarum.Second
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import kotlin.math.roundToInt
@@ -219,6 +220,12 @@ abstract class UICanvas(
return false
}
open fun inputStrobed(e: TerrarumKeyboardEvent) {
if (this.isVisible) {
uiItems.forEach { it.inputStrobed(e) }
handler.subUIs.forEach { it.inputStrobed(e) }
}
}
open fun resize(width: Int, height: Int) {
this.width = width

View File

@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.utils.Disposable
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
/**
@@ -238,6 +239,9 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
return false
}
open fun inputStrobed(e: TerrarumKeyboardEvent) {
}
abstract override fun dispose()

View File

@@ -9,9 +9,8 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.jme3.math.FastMath
import net.torvald.terrarum.*
import net.torvald.terrarum.gamecontroller.IME
import net.torvald.terrarum.gamecontroller.IngameController
import net.torvald.terrarum.gamecontroller.TerrarumInputMethod
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.gamecontroller.*
import net.torvald.terrarum.utils.Clipboard
import net.torvald.terrarumsansbitmap.gdx.CodepointSequence
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
@@ -45,6 +44,8 @@ data class InputLenCap(val count: Int, val unit: CharLenUnit) {
}
/**
* Make sure `inputStrobed()` of the parentUI is up and running.
*
* Protip: if there are multiple TextLineInputs on a same UI, draw bottom one first, otherwise the IME's
* candidate window will be hidden by the bottom UIItem if they overlaps.
*
@@ -156,20 +157,15 @@ class UIItemTextLineInput(
}
}
override fun update(delta: Float) {
super.update(delta)
val mouseDown = Terrarum.mouseDown
override fun inputStrobed(e: TerrarumKeyboardEvent) {
val oldActive = isActive
if (mouseDown) {
isActive = mouseUp
}
if (App.getConfigString("inputmethod") == "none") imeOn = false
// process keypresses
if (isActive) {
IngameController.withKeyboardEvent { (_, char, headkey, repeatCount, keycodes) ->
val (eventType, char, headkey, repeatCount, keycodes) = e
if (eventType == InputStrober.KEY_DOWN || eventType == InputStrober.KEY_CHANGE) {
fboUpdateLatch = true
forceLitCursor()
val ime = getIME()
@@ -263,13 +259,28 @@ class UIItemTextLineInput(
}
// don't put innards of tryCursorBack/Forward here -- you absolutely don't want that behaviour
}
if (textbuf.size == 0) {
currentPlaceholderText = CodepointSequence(placeholder().toCodePoints())
}
}
else if (oldActive) { // just became deactivated
endComposing()
}
}
override fun update(delta: Float) {
super.update(delta)
val mouseDown = Terrarum.mouseDown
if (mouseDown) {
isActive = mouseUp
}
if (App.getConfigString("inputmethod") == "none") imeOn = false
if (isActive) {
cursorBlinkCounter += delta
while (cursorBlinkCounter >= CURSOR_BLINK_TIME) {
@@ -277,9 +288,6 @@ class UIItemTextLineInput(
cursorOn = !cursorOn
}
}
else if (oldActive) { // just became deactivated
endComposing()
}
if (mouseDown && !mouseLatched && (enablePasteButton && enableIMEButton && mouseUpOnButton1 || enableIMEButton && !enablePasteButton && mouseUpOnButton2)) {
toggleIME()