keyboard input to use properly open/closed input stream

This commit is contained in:
minjaesong
2020-05-15 17:13:56 +09:00
parent e486d8dce4
commit 772354d2d1
7 changed files with 65 additions and 38 deletions

View File

@@ -9,6 +9,7 @@ import net.torvald.tsvm.peripheral.PeriBase
import org.luaj.vm2.LuaValue import org.luaj.vm2.LuaValue
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.io.PrintStream
import java.util.* import java.util.*
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.random.Random import kotlin.random.Random
@@ -68,9 +69,14 @@ class VM(
internal fun getIO(): IOSpace = peripheralTable[0].peripheral as IOSpace internal fun getIO(): IOSpace = peripheralTable[0].peripheral as IOSpace
lateinit var printStream: OutputStream //lateinit var printStream: OutputStream
lateinit var errorStream: OutputStream //lateinit var errorStream: OutputStream
lateinit var inputStream: InputStream //lateinit var inputStream: InputStream // InputStream should not be a singleton, as it HAS TO open and close the stream.
// Printstreams don't need that so they're singleton.
var getPrintStream: () -> OutputStream = { TODO() }
var getErrorStream: () -> OutputStream = { TODO() }
var getInputStream: () -> InputStream = { TODO() }
init { init {
peripheralTable[0] = PeripheralEntry( peripheralTable[0] = PeripheralEntry(

View File

@@ -44,9 +44,9 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
batch.projectionMatrix = camera.combined batch.projectionMatrix = camera.combined
Gdx.gl20.glViewport(0, 0, appConfig.width, appConfig.height) Gdx.gl20.glViewport(0, 0, appConfig.width, appConfig.height)
vm.printStream = gpu.getPrintStream() vm.getPrintStream = { gpu.getPrintStream() }
vm.errorStream = gpu.getErrorStream() vm.getErrorStream = { gpu.getErrorStream() }
vm.inputStream = gpu.getInputStream() vm.getInputStream = { gpu.getInputStream() }
// TEST PRG // TEST PRG
vmRunner = VMRunnerFactory(vm, "js") vmRunner = VMRunnerFactory(vm, "js")

View File

@@ -15,24 +15,25 @@ class VMJSR223Delegate(val vm: VM) {
fun print(s: String) { fun print(s: String) {
//print("[Nashorn] $s") //print("[Nashorn] $s")
vm.printStream.write(s.toByteArray()) vm.getPrintStream().write(s.toByteArray())
} }
fun println(s: String) { fun println(s: String) {
//println("[Nashorn] $s") //println("[Nashorn] $s")
vm.printStream.write((s + '\n').toByteArray()) vm.getPrintStream().write((s + '\n').toByteArray())
} }
fun println() = print('\n') fun println() = print('\n')
fun readKey() = vm.inputStream.read() //fun readKey() = vm.inputStream.read()
/** /**
* Read series of key inputs until Enter/Return key is pressed * Read series of key inputs until Enter/Return key is pressed
*/ */
fun read(): String { fun read(): String {
val inputStream = vm.getInputStream()
val sb = StringBuilder() val sb = StringBuilder()
var key: Int var key: Int
do { do {
key = readKey() key = inputStream.read()
if ((key == 8 && sb.isNotEmpty()) || key in 0x20..0x7E) { if ((key == 8 && sb.isNotEmpty()) || key in 0x20..0x7E) {
this.print("${key.toChar()}") this.print("${key.toChar()}")
@@ -45,6 +46,7 @@ class VMJSR223Delegate(val vm: VM) {
} while (key != 13 && key != 10) } while (key != 13 && key != 10)
this.println() this.println()
inputStream.close()
return sb.toString() return sb.toString()
} }
} }

View File

@@ -368,7 +368,7 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
private lateinit var PRINTSTREAM_INSTANCE: OutputStream private lateinit var PRINTSTREAM_INSTANCE: OutputStream
private lateinit var ERRORSTREAM_INSTANCE: OutputStream private lateinit var ERRORSTREAM_INSTANCE: OutputStream
private lateinit var INPUTSTREAM_INSTANCE: InputStream //private lateinit var INPUTSTREAM_INSTANCE: InputStream
override fun getPrintStream(): OutputStream { override fun getPrintStream(): OutputStream {
try { try {
@@ -412,27 +412,32 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
} }
} }
/**
* As getting the keyboard input now requires proper open and closing, the inputstream cannot be a singleton, unlike
* the printstream.
*/
override fun getInputStream(): InputStream { override fun getInputStream(): InputStream {
try { return object : InputStream() {
return INPUTSTREAM_INSTANCE
}
catch (e: UninitializedPropertyAccessException) {
INPUTSTREAM_INSTANCE = object : InputStream() {
override fun read(): Int { init {
var key: Byte vm.getIO().mmio_write(38L, 1)
do {
Thread.sleep(4L) // if spinning rate is too fast, this function fail.
// Possible cause: Input event handling of GDX is done on separate thread
key = vm.getIO().mmio_read(37L)!!
} while (key == (-1).toByte())
//println("[stdin] key = $key")
return key.toInt().and(255)
}
} }
return INPUTSTREAM_INSTANCE override fun read(): Int {
var key: Byte
do {
Thread.sleep(4L) // if spinning rate is too fast, this function fail.
// Possible cause: Input event handling of GDX is done on separate thread
key = vm.getIO().mmio_read(37L)!!
} while (key == (-1).toByte())
//println("[stdin] key = $key")
return key.toInt().and(255)
}
override fun close() {
vm.getIO().mmio_write(38L, 0)
}
} }
} }

View File

@@ -22,6 +22,8 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
private var mouseY: Short = 0 private var mouseY: Short = 0
private var mouseDown = false private var mouseDown = false
private var keyboardInputRequested = false
override fun peek(addr: Long): Byte? { override fun peek(addr: Long): Byte? {
return mmio_read(addr) return mmio_read(addr)
} }
@@ -37,7 +39,8 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
in 32..33 -> (mouseX.toInt() shr (adi - 32).times(8)).toByte() in 32..33 -> (mouseX.toInt() shr (adi - 32).times(8)).toByte()
in 34..35 -> (mouseY.toInt() shr (adi - 34).times(8)).toByte() in 34..35 -> (mouseY.toInt() shr (adi - 34).times(8)).toByte()
36L -> if (mouseDown) 1 else 0 36L -> if (mouseDown) 1 else 0
37L -> keyboardBuffer.removeHead() ?: -1 37L -> keyboardBuffer.removeTail() ?: -1
38L -> if (keyboardInputRequested) 1 else 0
else -> -1 else -> -1
} }
} }
@@ -46,6 +49,11 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
val adi = addr.toInt() val adi = addr.toInt()
val bi = byte.toInt().and(255) val bi = byte.toInt().and(255)
when (addr) { when (addr) {
37L -> keyboardBuffer.appendHead(byte)
38L -> {
keyboardInputRequested = (byte != 0.toByte())
if (keyboardInputRequested) keyboardBuffer.clear()
}
} }
} }
@@ -67,11 +75,13 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
} }
override fun keyTyped(p0: Char): Boolean { override fun keyTyped(p0: Char): Boolean {
try { if (keyboardInputRequested) {
keyboardBuffer.appendTail(p0.toByte()) keyboardBuffer.appendHead(p0.toByte())
return true
}
else {
return false
} }
catch (e: StackOverflowError) { /* if stack overflow, simply stop reading more keys */ }
return true
} }
override fun scrolled(p0: Int): Boolean { override fun scrolled(p0: Int): Boolean {

View File

@@ -84,6 +84,7 @@ class CircularArray<T>(val size: Int, val overwriteOnOverflow: Boolean): Iterabl
// even if overflowing is enabled, appending at tail causes head element to be altered, therefore such action // even if overflowing is enabled, appending at tail causes head element to be altered, therefore such action
// must be blocked by throwing overflow error // must be blocked by throwing overflow error
// if you think this behaviour is wrong, you're confusing appendHead() with appendTail(). Use appendHead() and removeTail()
if (overflow) { if (overflow) {
throw StackOverflowError() throw StackOverflowError()
} }

View File

@@ -43,13 +43,16 @@ Endianness: little
MMIO MMIO
0..31: Raw Keyboard Buffer read. Won't shift the key buffer 0..31 RO: Raw Keyboard Buffer read. Won't shift the key buffer
32..33: Mouse X pos 32..33 RO: Mouse X pos
34..35: Mouse Y pos 34..35 RO: Mouse Y pos
36: Mouse down? (1 for TRUE, 0 for FALSE) 36 RO: Mouse down? (1 for TRUE, 0 for FALSE)
37: Read/Write single key input. Key buffer will be shifted. Manual writing is 37 RW: Read/Write single key input. Key buffer will be shifted. Manual writing is
usually unnecessary as such action must be automatically managed via LibGDX usually unnecessary as such action must be automatically managed via LibGDX
input processing. input processing.
38 RW: Request keyboard input be read. Write nonzero value to enable, write zero to
close it. Keyboard buffer will be cleared whenever request is received, so
MAKE SURE YOU REQUEST THE KEY INPUT ONLY ONCE!
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------