diff --git a/FontTestGDX/src/TypewriterGDX.kt b/FontTestGDX/src/TypewriterGDX.kt index 04ef9c2..bb0334f 100644 --- a/FontTestGDX/src/TypewriterGDX.kt +++ b/FontTestGDX/src/TypewriterGDX.kt @@ -10,6 +10,7 @@ import com.badlogic.gdx.graphics.OrthographicCamera import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.glutils.FrameBuffer +import net.torvald.terrarum.gamecontroller.InputStrober import net.torvald.terrarumsansbitmap.gdx.CodepointSequence import net.torvald.terrarumtypewriterbitmap.gdx.TerrarumTypewriterBitmap import java.io.StringReader @@ -24,6 +25,8 @@ class TypewriterGDX(val width: Int, val height: Int) : Game() { // lateinit var frameBuffer: FrameBuffer lateinit var camera: OrthographicCamera + lateinit var inputStrober: InputStrober + override fun create() { font = TerrarumTypewriterBitmap( "./assets/typewriter", @@ -41,16 +44,26 @@ class TypewriterGDX(val width: Int, val height: Int) : Game() { camera.update() - Gdx.input.inputProcessor = TypewriterInput(this) + inputStrober = InputStrober(this) } - // uvamr ibwk/f ;rxubnfs private val textbuf: ArrayList = arrayListOf( - CodepointSequence(listOf(49,50,29,49,34,29,41,46, 62 ,37,30,51,39,76,34).map { it + 0xF3000 }) + CodepointSequence(listOf( + 39,50,29, // kva (HANG_GONG) + 42,31, // nc (HANG_SE) + 74,48,51, // ;tw (HANG_BEOL) + 62, // space + 184,69,171,170, // >-ON (ASC_3-90) + 62, // space + 75,34, // 'f (HANG_TA) + 40,34, // lf (HANG_JA) + 39,32 // kd (HANG_GI) + ).map { it + 0xF3000 }), + CodepointSequence(/* new line */) ) fun acceptKey(keycode: Int) { - println("[TypewriterGDX] Accepting key: $keycode") +// println("[TypewriterGDX] Accepting key: $keycode") if (keycode == Input.Keys.ENTER) { textbuf.add(CodepointSequence()) @@ -73,9 +86,13 @@ class TypewriterGDX(val width: Int, val height: Int) : Game() { batch.begin() batch.color = textCol - textbuf.forEachIndexed { index, s -> - font.draw(batch, s, 40f, 40f + 32*index) + + try { + textbuf.forEachIndexed { index, s -> + font.draw(batch, s, 40f, 40f + 32 * index) + } } + catch (e: ConcurrentModificationException) {} batch.end() } @@ -83,6 +100,7 @@ class TypewriterGDX(val width: Int, val height: Int) : Game() { override fun dispose() { font.dispose() batch.dispose() + inputStrober.dispose() } } diff --git a/FontTestGDX/src/net/torvald/terrarum/gamecontroller/InputStrober.kt b/FontTestGDX/src/net/torvald/terrarum/gamecontroller/InputStrober.kt new file mode 100644 index 0000000..437a38d --- /dev/null +++ b/FontTestGDX/src/net/torvald/terrarum/gamecontroller/InputStrober.kt @@ -0,0 +1,394 @@ +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) { + 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) + } + } + + 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 +) \ No newline at end of file diff --git a/assets/typewriter/typewriter_ko_3set-390.tga b/assets/typewriter/typewriter_ko_3set-390.tga index a139a30..29c44da 100644 Binary files a/assets/typewriter/typewriter_ko_3set-390.tga and b/assets/typewriter/typewriter_ko_3set-390.tga differ diff --git a/src/net/torvald/terrarumtypewriterbitmap/gdx/TerrarumTypewriterBitmap.kt b/src/net/torvald/terrarumtypewriterbitmap/gdx/TerrarumTypewriterBitmap.kt index 34b154f..9cc3d46 100644 --- a/src/net/torvald/terrarumtypewriterbitmap/gdx/TerrarumTypewriterBitmap.kt +++ b/src/net/torvald/terrarumtypewriterbitmap/gdx/TerrarumTypewriterBitmap.kt @@ -255,6 +255,7 @@ class TerrarumTypewriterBitmap( } private val pixmapOffsetY = 10 + private val linotypePad = 16 private var flagFirstRun = true private var textBuffer = CodepointSequence(256) private lateinit var tempLinotype: Texture @@ -303,7 +304,7 @@ class TerrarumTypewriterBitmap( // resetHash(charSeq, x.toFloat(), y.toFloat()) - val _pw = posXbuffer.last() + val _pw = posXbuffer.last() + 2*linotypePad val _ph = TerrarumSansBitmap.H + (pixmapOffsetY * 2) if (_pw < 0 || _ph < 0) throw RuntimeException("Illegal linotype dimension (w: $_pw, h: $_ph)") val linotypePixmap = Pixmap(_pw, _ph, Pixmap.Format.RGBA8888) @@ -333,7 +334,7 @@ class TerrarumTypewriterBitmap( val texture = sheets[sheetID]?.get(sheetX, sheetY) texture?.let { - linotypePixmap.drawPixmap(it, posX, posY + pixmapOffsetY, renderCol) + linotypePixmap.drawPixmap(it, posX + linotypePad, posY + pixmapOffsetY, renderCol) } } @@ -360,11 +361,11 @@ class TerrarumTypewriterBitmap( if (!flipY) { - batch.draw(tempLinotype, x.toFloat(), (y - pixmapOffsetY).toFloat()) + batch.draw(tempLinotype, (x - linotypePad).toFloat(), (y - pixmapOffsetY).toFloat()) } else { batch.draw(tempLinotype, - x.toFloat(), + (x - linotypePad).toFloat(), (y - pixmapOffsetY + (tempLinotype.height)).toFloat(), (tempLinotype.width.toFloat()), -(tempLinotype.height.toFloat()) diff --git a/work_files/typewriter_input/hangul_3set_glyphs_master.kra b/work_files/typewriter_input/hangul_3set_glyphs_master.kra index 53255d9..8d3ad56 100644 Binary files a/work_files/typewriter_input/hangul_3set_glyphs_master.kra and b/work_files/typewriter_input/hangul_3set_glyphs_master.kra differ diff --git a/work_files/typewriter_input/typewriter_ko_3set-390.psd b/work_files/typewriter_input/typewriter_ko_3set-390.psd index 862e9f1..36694b9 100644 Binary files a/work_files/typewriter_input/typewriter_ko_3set-390.psd and b/work_files/typewriter_input/typewriter_ko_3set-390.psd differ diff --git a/work_files/typewriter_input/typewriter_ko_3set_glyphs_resized.kra b/work_files/typewriter_input/typewriter_ko_3set_glyphs_resized.kra index 7f8add5..c845858 100644 Binary files a/work_files/typewriter_input/typewriter_ko_3set_glyphs_resized.kra and b/work_files/typewriter_input/typewriter_ko_3set_glyphs_resized.kra differ