package net.torvald.terrarum.gamecontroller import TypewriterGDX import com.badlogic.gdx.Gdx import com.badlogic.gdx.Input /** * Created by minjaesong on 2021-11-06. */ class InputStrober(val typewriter: TypewriterGDX) { companion object { 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) // println("Key strobed: ${keys.joinToString()}") if (stroboStatus % 2 == 0 && (keys[0] != 0 || oldKeys[0] != 0)) { stroboStatus += 1 stroboTime = System.nanoTime() repeatCount += 1 val shiftin = keys.contains(Input.Keys.SHIFT_LEFT) || keys.contains(Input.Keys.SHIFT_RIGHT) val newKeysym0 = keysToStr(keyDiff) val newKeysym = if (newKeysym0 == null) null else if (shiftin && newKeysym0.size > 1 && newKeysym0[1]?.isNotBlank() == true) newKeysym0[1] else newKeysym0[0] val headKeyCode = (if (keyDiff.size < 1) keys[0] else keyDiff[0]).and(127) or (if (shiftin) 128 else 0) if (repeatCount == 1) { if (!keyChanged) { // println("KEY_DOWN '$keysym' ($headKeyCode) $repeatCount; ${keys.joinToString()}") // App.inputStrobed(TerrarumKeyboardEvent(KEY_DOWN, keysym, headKeyCode, repeatCount, keys)) typewriter.acceptKey(headKeyCode) } else if (newKeysym != null) { // println("KEY_DOWC '$newKeysym' ($headKeyCode) $repeatCount; ${keys.joinToString()}") // App.inputStrobed(TerrarumKeyboardEvent(KEY_DOWN, newKeysym, headKeyCode, repeatCount, keys)) typewriter.acceptKey(headKeyCode) } // println("shiftin=${shiftin} oldkeys=${oldKeys.joinToString()}") if (!shiftin && (oldKeys.contains(Input.Keys.SHIFT_LEFT) || oldKeys.contains(Input.Keys.SHIFT_RIGHT))) { typewriter.shiftOut() } } 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(keys: IntArray): Array? { if (keys.isEmpty()) return null val headkey = keys[0] return keymap[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() } private val keymap = arrayOf(arrayOf(""),arrayOf(null), arrayOf(null), arrayOf(""), arrayOf(null), arrayOf(""), arrayOf(""), arrayOf("0",")"), arrayOf("1","!"), arrayOf("2","@"), arrayOf("3","#"), arrayOf("4","$"), arrayOf("5","%"), arrayOf("6","^"), arrayOf("7","&"), arrayOf("8","*"), arrayOf("9","("), arrayOf("*"), arrayOf("#"), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf("
"), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf("a","A"), arrayOf("b","B"), arrayOf("c","C"), arrayOf("d","D"), arrayOf("e","E"), arrayOf("f","F"), arrayOf("g","G"), arrayOf("h","H"), arrayOf("i","I"), arrayOf("j","J"), arrayOf("k","K"), arrayOf("l","L"), arrayOf("m","M"), arrayOf("n","N"), arrayOf("o","O"), arrayOf("p","P"), arrayOf("q","Q"), arrayOf("r","R"), arrayOf("s","S"), arrayOf("t","T"), arrayOf("u","U"), arrayOf("v","V"), arrayOf("w","W"), arrayOf("x","X"), arrayOf("y","Y"), arrayOf("z","Z"), arrayOf(",","<"), arrayOf(".",">"), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(" "), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf("\n"), arrayOf("\u0008"), arrayOf("`","~"), arrayOf("-","_"), arrayOf("=","+"), arrayOf("arrayOf(","{"), arrayOf(")","}"), arrayOf("\\","|"), arrayOf(";",":"), arrayOf("'","\""), arrayOf("/","?"), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf("+"), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf("TCH_CHARSET>"), arrayOf("<:A:>"), arrayOf("<:B:>"), arrayOf("<:C:>"), arrayOf("<:X:>"), arrayOf("<:Y:>"), arrayOf("<:Z:>"), arrayOf("<:L1:>"), arrayOf("<:R1:>"), arrayOf("<:L2:>"), arrayOf("<:R2:>"), arrayOf("<:TL:>"), arrayOf("<:TR:>"), arrayOf("<:START:>"), arrayOf("<:SELECT:>"), arrayOf("<:MODE:>"), arrayOf(""), arrayOf(""), arrayOf(null), arrayOf(null), arrayOf(""), arrayOf(""), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(""), arrayOf(""), arrayOf(null), arrayOf(""), arrayOf(""), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf(""), arrayOf("0"), arrayOf("1"), arrayOf("2"), arrayOf("3"), arrayOf("4"), arrayOf("5"), arrayOf("6"), arrayOf("7"), arrayOf("8"), arrayOf("9"), arrayOf("/"), arrayOf("*"), arrayOf("-"), arrayOf("+"), arrayOf("."), arrayOf("."), arrayOf("\n"), arrayOf("="), arrayOf("("), arrayOf(")"), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf(null), arrayOf("<:CIRCLE:>") ) } data class TerrarumKeyboardEvent( val type: Int, val character: String?, // representative key symbol val headkey: Int, // representative keycode val repeatCount: Int, val keycodes: IntArray )