summaryrefslogtreecommitdiff
path: root/src/jsMain/kotlin/WebOS.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/jsMain/kotlin/WebOS.kt')
-rw-r--r--src/jsMain/kotlin/WebOS.kt258
1 files changed, 170 insertions, 88 deletions
diff --git a/src/jsMain/kotlin/WebOS.kt b/src/jsMain/kotlin/WebOS.kt
index 680ded7..4ad2f86 100644
--- a/src/jsMain/kotlin/WebOS.kt
+++ b/src/jsMain/kotlin/WebOS.kt
@@ -9,6 +9,7 @@ import org.w3c.dom.Node
import org.w3c.dom.asList
import org.w3c.dom.events.Event
import org.w3c.dom.events.KeyboardEvent
+import kotlin.properties.Delegates
fun main() {
console.log("Hello from Kotlin")
@@ -24,22 +25,83 @@ fun main() {
data class CharacterRun(val text: String, val color: String)
abstract class Activity(val console: Console) {
+
+ open fun handleKeyPress(key: Key) {}
+ open fun update() {}
abstract fun render(columns: Int, rows: Int): List<List<CharacterRun>>
}
-class PrimitiveShell(override val console: Console) : Shell {
+class ShellRunner(console: Console) : Activity(console) {
+
+ val history = mutableListOf<String>()
+
+ val shellProgramStack = ArrayDeque<ShellProgram>()
- var currentInput = ""
+ var lastLine = ""
- override fun start() {
- console.print("${console.currentUser!!.name}@${console.os.hostname} ${console.workingDirectory} > ")
+ fun newLine() {
+ history.add(lastLine)
+ lastLine = ""
+ console.invalidateRender()
}
+ fun println(it: String = "") {
+ print(it)
+ newLine()
+ }
+
+ fun print(it: String) {
+ lastLine += it
+ console.invalidateRender()
+ }
+
+ override fun update() {
+ shellProgramStack.lastOrNull()?.update()
+ }
+
+ fun getInput(): String? {
+ if (inputBuffer.contains("\n")) {
+ val r = inputBuffer.substringBefore("\n")
+ inputBuffer = inputBuffer.substringAfter("\n")
+ return r
+ }
+ return null
+ }
+
+ override fun render(columns: Int, rows: Int): List<List<CharacterRun>> =
+ history.map { listOf(CharacterRun(it.take(columns), "white")) }.takeLast(rows - 1) + listOf(
+ listOf(
+ CharacterRun(
+ lastLine + inputBuffer,
+ "white"
+ )
+ )
+ )
+
+ var inputBuffer = ""
+
override fun handleKeyPress(key: Key) = when (key) {
- is Key.Printable -> { currentInput += key.char }
- else -> Unit
+ is Key.Printable -> {
+ inputBuffer += key.char
+ }
+ is Key.Enter -> {
+ inputBuffer += "\n"
+ }
+ else -> Unit
}
+ fun <T : ShellProgram> openShellProgram(program: (ShellRunner) -> T): ShellRunner = openShellProgram(program(this))
+
+ fun <T : ShellProgram> openShellProgram(program: T): ShellRunner {
+ shellProgramStack.addLast(program)
+ return this
+ }
+
+}
+
+abstract class ShellProgram(val shellRunner: ShellRunner) {
+
+ abstract fun update(): Unit
}
sealed class Key {
@@ -64,6 +126,7 @@ sealed class Key {
object Right : Key()
object Up : Key()
}
+
object End : Key()
object Home : Key()
object PageUp : Key()
@@ -93,53 +156,54 @@ sealed class Key {
class Printable(val char: Char) : Key()
class FunctionN(val n: Int) : Key()
companion object {
+
fun from(string: String) = when (string) {
- "Alt" -> Alt
- "AltGraph" -> AltGraph
- "CapsLock" -> CapsLock
- "Control" -> Control
- "Fn" -> Function
- "FnLock" -> FunctionLock
- "Hyper" -> Hyper
- "Meta" -> Meta
- "NumLock" -> NumLock
- "Shift" -> Shift
- "Super" -> Super
- "Symbol" -> Symbol
- "SymbolLock" -> SymbolLock
- "Enter" -> Enter
- "Tab" -> Tab
- "Down" -> Arrow.Down
- "Left" -> Arrow.Left
- "Right" -> Arrow.Right
- "Up" -> Arrow.Up
- "End" -> End
- "Home" -> Home
- "PageUp" -> PageUp
- "PageDown" -> PageDown
- "Backspace" -> Backspace
- "Clear" -> Clear
- "Copy" -> Copy
- "CrSel" -> CrSel
- "Cut" -> Cut
- "Delete" -> Delete
- "EraseEof" -> EraseEof
- "ExSel" -> ExSel
- "Insert" -> Insert
- "Paste" -> Paste
- "Redo" -> Redo
- "Undo" -> Undo
- "Accept" -> Accept
- "Again" -> Again
- "Attn" -> Attn
- "Cancel" -> Cancel
+ "Alt" -> Alt
+ "AltGraph" -> AltGraph
+ "CapsLock" -> CapsLock
+ "Control" -> Control
+ "Fn" -> Function
+ "FnLock" -> FunctionLock
+ "Hyper" -> Hyper
+ "Meta" -> Meta
+ "NumLock" -> NumLock
+ "Shift" -> Shift
+ "Super" -> Super
+ "Symbol" -> Symbol
+ "SymbolLock" -> SymbolLock
+ "Enter" -> Enter
+ "Tab" -> Tab
+ "Down" -> Arrow.Down
+ "Left" -> Arrow.Left
+ "Right" -> Arrow.Right
+ "Up" -> Arrow.Up
+ "End" -> End
+ "Home" -> Home
+ "PageUp" -> PageUp
+ "PageDown" -> PageDown
+ "Backspace" -> Backspace
+ "Clear" -> Clear
+ "Copy" -> Copy
+ "CrSel" -> CrSel
+ "Cut" -> Cut
+ "Delete" -> Delete
+ "EraseEof" -> EraseEof
+ "ExSel" -> ExSel
+ "Insert" -> Insert
+ "Paste" -> Paste
+ "Redo" -> Redo
+ "Undo" -> Undo
+ "Accept" -> Accept
+ "Again" -> Again
+ "Attn" -> Attn
+ "Cancel" -> Cancel
"ContextMenu" -> ContextMenu
- "Escape" -> Escape
- "Execute" -> Execute
- "Find" -> Find
- "Finish" -> Finish
- "Help" -> Help
- else -> if (string.length == 1)
+ "Escape" -> Escape
+ "Execute" -> Execute
+ "Find" -> Find
+ "Finish" -> Finish
+ "Help" -> Help
+ else -> if (string.length == 1)
Printable(string.first())
else if (string.first() == 'F')
FunctionN(string.substring(1).toInt())
@@ -148,15 +212,45 @@ sealed class Key {
}
}
-interface Shell {
- val console: Console
- fun start()
- fun handleKeyPress(key: Key)
-}
+class Login(shellRunner: ShellRunner) : ShellProgram(shellRunner) {
+
+ override fun update() {
+ while (true)
+ when (state) {
+ 0 -> {
+ shellRunner.print("Username: ")
+ state = 1
+ }
+ 1 -> {
+ val inp = shellRunner.getInput() ?: return
+ shellRunner.println(inp)
+ shellRunner.print("Password: ")
+ username = inp
+ state = 2
+ }
+ 2 -> {
+ val inp = shellRunner.getInput() ?: return
+ shellRunner.println(inp)
+ shellRunner.println("Login complete)")
+ password = inp
+ // TODO: check password and set user
+ state = 3
+ }
+ 3 -> {
+ TODO()
+ }
+ }
+
+ }
-typealias KeypressHandler = (Key) -> Unit
+ var state = 0
+ var username = ""
+ var password = ""
+
+}
class Console(val os: WebOS, val renderElement: Element?) {
+
val isVirtual get() = renderElement == null
val activityStack = ArrayDeque<Activity>()
@@ -176,31 +270,33 @@ class Console(val os: WebOS, val renderElement: Element?) {
}
init {
- renderElement?.addEventListener("keypress", ::computeKeypressEvent)
+ renderElement?.addEventListener("keydown", ::computeKeypressEvent)
}
- var keypressHandler: KeypressHandler? = null
-
fun computeKeypressEvent(event: Event) {
if (!isInFocus()) return
if (event !is KeyboardEvent) return
- keypressHandler?.invoke(Key.from(event.key))
+ activityStack.lastOrNull()?.handleKeyPress(Key.from(event.key))
}
private fun isInFocus(): Boolean = renderElement.containsOrIs(document.activeElement)
private fun Node?.containsOrIs(node: Node?) = this == node || this?.contains(node) ?: false
- fun openActivity(activity: Activity) {
+ fun <T : Activity> openActivity(activity: (Console) -> T): Console = openActivity(activity(this))
+
+ fun <T : Activity> openActivity(activity: T): Console {
activityStack.addLast(activity)
invalidateRender()
+ return this
}
fun render() {
if (renderElement == null) return
if (!shouldRerender) return
shouldRerender = false
- activityStack.last()
+ val x = activityStack.lastOrNull()?.render(columns, rows)
+ console.log(x)
}
fun invalidateRender() {
@@ -214,39 +310,25 @@ class Console(val os: WebOS, val renderElement: Element?) {
// TODO: Handle resizes of the renderElement
- fun print(text: String): Unit = TODO()
-
- fun loginAndThen(block: () -> Unit) {
- print("Username: ")
- var enteredUsername: Boolean = false
- var username = ""
- var password = ""
- keypressHandler = handler@{
- if (it is Key.Enter)
- if (enteredUsername) {
- //TODO: Login
- block()
- return@handler
- } else {
- enteredUsername = true
- print("Password: ")
- }
- if (it !is Key.Printable) return@handler
- if (!enteredUsername) username += it.char else password += it.char
- print(it.char)
- }
+ fun update(): Unit {
+ activityStack.lastOrNull()?.update()
}
- fun start() = loginAndThen {
- val shell = PrimitiveShell(this) //TODO: choose shell properly
- keypressHandler = shell::handleKeyPress
- shell.start()
+ var updateInterval by Delegates.notNull<Int>()
+
+ fun start(): Console {
+ updateInterval = window.setInterval(::update, 10)
+ return openActivity(ShellRunner(this).also { it.openShellProgram(Login(it)) })
}
+ fun destroy() {
+ window.clearInterval(updateInterval)
+ }
}
class WebOS {
+
val hostname: String = "host"
private val _consoles = mutableListOf<Console>()
val consoles get() = _consoles.toList()