Files
Suwayomi-Server/server/src/main/kotlin/suwayomi/tachidesk/global/impl/WebView.kt
renovate[bot] 6ee3348f50 Update javalin to v7 (major) (#1920)
* Update javalin to v7

* Update Javalin usage to v7 and Jackson 3

* Import fix

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Syer10 <syer10@users.noreply.github.com>
2026-05-09 14:52:00 -04:00

133 lines
3.5 KiB
Kotlin

package suwayomi.tachidesk.global.impl
import io.github.oshai.kotlinlogging.KotlinLogging
import io.javalin.websocket.WsContext
import io.javalin.websocket.WsMessageContext
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.eclipse.jetty.websocket.core.CloseStatus
import suwayomi.tachidesk.manga.impl.update.Websocket
object WebView : Websocket<String>() {
private val logger = KotlinLogging.logger {}
private var driver: KcefWebView? = null
override fun addClient(ctx: WsContext) {
if (clients.isNotEmpty()) {
// TODO: allow multiple concurrent accesses?
clients.forEach { it.value.closeSession(CloseStatus(1001, "Other client connected")) }
clients.clear()
}
if (driver == null) {
driver = KcefWebView()
}
super.addClient(ctx)
ctx.enableAutomaticPings()
}
override fun removeClient(ctx: WsContext) {
super.removeClient(ctx)
if (clients.isEmpty()) {
driver?.destroy()
driver = null
}
}
override fun notifyClient(
ctx: WsContext,
value: String?,
) {
if (value != null) {
ctx.send(value)
}
}
@Serializable
sealed class TypeObject
@Serializable
@SerialName("loadUrl")
private data class LoadUrlMessage(
val url: String,
val width: Int,
val height: Int,
) : TypeObject()
@Serializable
@SerialName("resize")
private data class ResizeMessage(
val width: Int,
val height: Int,
) : TypeObject()
@Serializable
@SerialName("event")
data class JsEventMessage(
val eventType: String,
val clickX: Float,
val clickY: Float,
val button: Int? = null,
val ctrlKey: Boolean? = null,
val shiftKey: Boolean? = null,
val altKey: Boolean? = null,
val metaKey: Boolean? = null,
val key: String? = null,
val code: String? = null,
val clientX: Float? = null,
val clientY: Float? = null,
val deltaY: Float? = null,
) : TypeObject()
@Serializable
@SerialName("paste")
data class JsPasteMessage(
val data: String,
) : TypeObject()
@Serializable
@SerialName("copy")
class JsCopyMessage : TypeObject()
@Serializable
@SerialName("ping")
class JsPingMessage : TypeObject()
override fun handleRequest(ctx: WsMessageContext) {
val dr = driver ?: return
try {
val event = Json.decodeFromString<TypeObject>(ctx.message())
when (event) {
is LoadUrlMessage -> {
val url = event.url
dr.loadUrl(url)
dr.resize(event.width, event.height)
logger.debug { "Loading URL $url" }
}
is ResizeMessage -> {
dr.resize(event.width, event.height)
}
is JsEventMessage -> {
dr.event(event)
}
is JsPasteMessage -> {
dr.paste(event.data)
}
is JsCopyMessage -> {
dr.copy()
}
is JsPingMessage -> {
notifyAllClients("{\"type\":\"pong\"}")
}
}
} catch (e: Exception) {
logger.warn(e) { "Failed to deserialize client request: ${ctx.message()}" }
}
}
}