abstract system/desktop api

Porting on platforms where javafx or awt is not the best choice (e.g. android) is easier
This commit is contained in:
Moritz Zwerger 2023-11-23 18:12:08 +01:00
parent 46dfbe3252
commit 89e4254ddd
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
14 changed files with 121 additions and 66 deletions

View File

@ -50,12 +50,13 @@ import de.bixilon.minosoft.properties.MinosoftPropertiesLoader
import de.bixilon.minosoft.terminal.AutoConnect import de.bixilon.minosoft.terminal.AutoConnect
import de.bixilon.minosoft.terminal.CommandLineArguments import de.bixilon.minosoft.terminal.CommandLineArguments
import de.bixilon.minosoft.terminal.RunConfiguration import de.bixilon.minosoft.terminal.RunConfiguration
import de.bixilon.minosoft.util.DesktopUtil
import de.bixilon.minosoft.util.KUtil import de.bixilon.minosoft.util.KUtil
import de.bixilon.minosoft.util.json.Jackson import de.bixilon.minosoft.util.json.Jackson
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
import de.bixilon.minosoft.util.system.DesktopAPI
import de.bixilon.minosoft.util.system.SystemUtil
object Minosoft { object Minosoft {
@ -96,7 +97,7 @@ object Minosoft {
} }
if (RunConfiguration.DISABLE_EROS && !RunConfiguration.DISABLE_RENDERING) { if (RunConfiguration.DISABLE_EROS && !RunConfiguration.DISABLE_RENDERING) {
// eros is disabled, but rendering not, force initialize the desktop, otherwise eros will do so // eros is disabled, but rendering not, force initialize the desktop, otherwise eros will do so
DefaultThreadPool += { DesktopUtil.initialize() } DefaultThreadPool += { SystemUtil.api = DesktopAPI() }
} }
taskWorker.work(MinosoftBoot.LATCH) taskWorker.work(MinosoftBoot.LATCH)

View File

@ -20,7 +20,7 @@ import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.confirmation.OpenFileConfirmationDialog import de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.confirmation.OpenFileConfirmationDialog
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons
import de.bixilon.minosoft.util.DesktopUtil import de.bixilon.minosoft.util.system.SystemUtil
import javafx.scene.text.Text import javafx.scene.text.Text
import java.io.File import java.io.File
import java.nio.file.Path import java.nio.file.Path
@ -41,7 +41,7 @@ class OpenFileClickEvent(
return return
} }
if (!guiRenderer.connection.profiles.gui.confirmation.openFile) { if (!guiRenderer.connection.profiles.gui.confirmation.openFile) {
DesktopUtil.openFile(file) SystemUtil.api?.openFile(file)
return return
} }
val dialog = OpenFileConfirmationDialog(guiRenderer, file) val dialog = OpenFileConfirmationDialog(guiRenderer, file)

View File

@ -22,7 +22,7 @@ import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.confirmation.URLConfirmationDialog import de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.confirmation.URLConfirmationDialog
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons
import de.bixilon.minosoft.util.DesktopUtil import de.bixilon.minosoft.util.system.SystemUtil
import javafx.scene.text.Text import javafx.scene.text.Text
import java.net.URL import java.net.URL
@ -39,7 +39,7 @@ class OpenURLClickEvent(
return return
} }
if (!guiRenderer.connection.profiles.gui.confirmation.openURL) { if (!guiRenderer.connection.profiles.gui.confirmation.openURL) {
DesktopUtil.openURL(url) SystemUtil.api?.openURL(url)
return return
} }
val dialog = URLConfirmationDialog(guiRenderer, url) val dialog = URLConfirmationDialog(guiRenderer, url)

View File

@ -138,7 +138,7 @@ class ProfilesListController : EmbeddedJavaFXController<Pane>() {
Button("Edit").apply { Button("Edit").apply {
// ToDo: proper profile editing // ToDo: proper profile editing
isDisable = true isDisable = true
// setOnAction { DefaultThreadPool += { DesktopUtil.openFile(profile.manager.getPath(profile.name)) } } // setOnAction { DefaultThreadPool += { SystemUtil.openFile(profile.manager.getPath(profile.name)) } }
ctext = EDIT ctext = EDIT
}, },
Button("Set primary").apply { Button("Set primary").apply {

View File

@ -21,10 +21,10 @@ import de.bixilon.kutil.latch.SimpleLatch
import de.bixilon.kutil.shutdown.ShutdownManager import de.bixilon.kutil.shutdown.ShutdownManager
import de.bixilon.minosoft.assets.IntegratedAssets import de.bixilon.minosoft.assets.IntegratedAssets
import de.bixilon.minosoft.gui.eros.crash.ErosCrashReport.Companion.crash import de.bixilon.minosoft.gui.eros.crash.ErosCrashReport.Companion.crash
import de.bixilon.minosoft.util.DesktopUtil
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
import de.bixilon.minosoft.util.system.SystemUtil
import javafx.application.Application import javafx.application.Application
import javafx.application.Platform import javafx.application.Platform
import javafx.scene.image.Image import javafx.scene.image.Image
@ -38,10 +38,10 @@ class JavaFXInitializer internal constructor() : Application() {
JavaFXUtil.JAVA_FX_THREAD = Thread.currentThread() JavaFXUtil.JAVA_FX_THREAD = Thread.currentThread()
JavaFXUtil.HOST_SERVICES = hostServices JavaFXUtil.HOST_SERVICES = hostServices
DesktopUtil.initialize() SystemUtil.api = JavaFXSystemAPI()
val worker = UnconditionalWorker(autoWork = true) val worker = UnconditionalWorker(autoWork = true)
worker += { JavaFXUtil.MINOSOFT_LOGO = Image(IntegratedAssets.DEFAULT[DesktopUtil.ICON]) } worker += { JavaFXUtil.MINOSOFT_LOGO = Image(IntegratedAssets.DEFAULT[SystemUtil.ICON]) }
worker.work(LATCH) worker.work(LATCH)
Log.log(LogMessageType.JAVAFX, LogLevels.VERBOSE) { "Initialized JavaFX Toolkit!" } Log.log(LogMessageType.JAVAFX, LogLevels.VERBOSE) { "Initialized JavaFX Toolkit!" }

View File

@ -11,31 +11,18 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.util package de.bixilon.minosoft.gui.eros.util
import de.bixilon.minosoft.assets.IntegratedAssets
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.gui.eros.util.JavaFXUtil
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.terminal.RunConfiguration
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
import java.awt.Taskbar import de.bixilon.minosoft.util.system.DesktopAPI
import java.awt.Toolkit
import java.io.File import java.io.File
import java.net.URL import java.net.URL
import java.nio.file.Path
class JavaFXSystemAPI : DesktopAPI() {
object DesktopUtil { override fun openURL(url: URL) {
val ICON = minosoft("icons/window_icon").texture()
fun openURL(url: URL) {
if (RunConfiguration.DISABLE_EROS) {
Log.log(LogMessageType.GENERAL, LogLevels.WARN) { "Can not open url: $url: Eros is disabled!" }
return
}
try { try {
JavaFXUtil.HOST_SERVICES.showDocument(url.toString()) JavaFXUtil.HOST_SERVICES.showDocument(url.toString())
} catch (exception: Throwable) { } catch (exception: Throwable) {
@ -43,42 +30,16 @@ object DesktopUtil {
} }
} }
fun openFile(path: Path) { override fun openFile(file: File) {
openFile(path.toFile())
}
fun openFile(file: File) {
if (!file.exists()) { if (!file.exists()) {
Log.log(LogMessageType.GENERAL, LogLevels.WARN) { "Can not open file $file: File does not exist!" } Log.log(LogMessageType.GENERAL, LogLevels.WARN) { "Can not open file $file: File does not exist!" }
return return
} }
if (RunConfiguration.DISABLE_EROS) {
Log.log(LogMessageType.GENERAL, LogLevels.INFO) { "Can not open file: $file: Eros is disabled!" }
return
}
try { try {
JavaFXUtil.HOST_SERVICES.showDocument(file.absolutePath) JavaFXUtil.HOST_SERVICES.showDocument(file.absolutePath)
} catch (exception: Throwable) { } catch (exception: Throwable) {
exception.printStackTrace() exception.printStackTrace()
} }
} }
private fun Taskbar.setDockIcon() {
iconImage = Toolkit.getDefaultToolkit().createImage(IntegratedAssets.DEFAULT[ICON].readAllBytes())
}
private fun Taskbar.initialize() {
if (isSupported(Taskbar.Feature.ICON_IMAGE)) {
setDockIcon()
}
}
fun initialize() {
System.setProperty("java.awt.headless", false.toString())
if (Taskbar.isTaskbarSupported()) {
Taskbar.getTaskbar().initialize()
}
}
} }

View File

@ -28,10 +28,10 @@ import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.eros.controller.EmbeddedJavaFXController import de.bixilon.minosoft.gui.eros.controller.EmbeddedJavaFXController
import de.bixilon.minosoft.gui.eros.controller.JavaFXController import de.bixilon.minosoft.gui.eros.controller.JavaFXController
import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController
import de.bixilon.minosoft.util.DesktopUtil
import de.bixilon.minosoft.util.KUtil.toResourceLocation import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.crash.freeze.FreezeDumpUtil import de.bixilon.minosoft.util.crash.freeze.FreezeDumpUtil
import de.bixilon.minosoft.util.delegate.JavaFXDelegate.observeFX import de.bixilon.minosoft.util.delegate.JavaFXDelegate.observeFX
import de.bixilon.minosoft.util.system.SystemUtil
import javafx.application.HostServices import javafx.application.HostServices
import javafx.application.Platform import javafx.application.Platform
import javafx.beans.property.BooleanPropertyBase import javafx.beans.property.BooleanPropertyBase
@ -177,14 +177,14 @@ object JavaFXUtil {
fun Text.hyperlink(link: String) { fun Text.hyperlink(link: String) {
val url = link.toURL() val url = link.toURL()
this.setOnMouseClicked { DefaultThreadPool += { DesktopUtil.openURL(url) } } this.setOnMouseClicked { DefaultThreadPool += { SystemUtil.api?.openURL(url) } }
this.accessibleRole = AccessibleRole.HYPERLINK this.accessibleRole = AccessibleRole.HYPERLINK
this.styleClass.setAll("hyperlink") this.styleClass.setAll("hyperlink")
this.clickable() this.clickable()
} }
fun Text.file(path: File) { fun Text.file(path: File) {
this.setOnMouseClicked { DefaultThreadPool += { DesktopUtil.openFile(path) } } this.setOnMouseClicked { DefaultThreadPool += { SystemUtil.api?.openFile(path) } }
this.accessibleRole = AccessibleRole.HYPERLINK this.accessibleRole = AccessibleRole.HYPERLINK
this.styleClass.setAll("hyperlink") this.styleClass.setAll("hyperlink")
this.clickable() this.clickable()

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 Moritz Zwerger
* *
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* *
@ -16,7 +16,7 @@ package de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.confirmation
import de.bixilon.minosoft.data.text.TextComponent import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.input.button.ButtonElement import de.bixilon.minosoft.gui.rendering.gui.elements.input.button.ButtonElement
import de.bixilon.minosoft.util.DesktopUtil import de.bixilon.minosoft.util.system.SystemUtil
import java.io.File import java.io.File
class OpenFileConfirmationDialog( class OpenFileConfirmationDialog(
@ -32,7 +32,7 @@ class OpenFileConfirmationDialog(
override fun createButtons(): Array<ButtonElement> { override fun createButtons(): Array<ButtonElement> {
return arrayOf( return arrayOf(
ButtonElement(guiRenderer, "Yes, open it!") { ButtonElement(guiRenderer, "Yes, open it!") {
DesktopUtil.openFile(file) SystemUtil.api?.openFile(file)
close() close()
}, },
createCopyToClipboardButton(file.path) createCopyToClipboardButton(file.path)

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 Moritz Zwerger
* *
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* *
@ -16,7 +16,7 @@ package de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.confirmation
import de.bixilon.minosoft.data.text.TextComponent import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.input.button.ButtonElement import de.bixilon.minosoft.gui.rendering.gui.elements.input.button.ButtonElement
import de.bixilon.minosoft.util.DesktopUtil import de.bixilon.minosoft.util.system.SystemUtil
import java.net.URL import java.net.URL
class URLConfirmationDialog( class URLConfirmationDialog(
@ -30,7 +30,7 @@ class URLConfirmationDialog(
override fun createButtons(): Array<ButtonElement> { override fun createButtons(): Array<ButtonElement> {
return arrayOf( return arrayOf(
ButtonElement(guiRenderer, "Yes, open it!") { ButtonElement(guiRenderer, "Yes, open it!") {
DesktopUtil.openURL(url) SystemUtil.api?.openURL(url)
close() close()
}, },
createCopyToClipboardButton(url.toString()) createCopyToClipboardButton(url.toString())

View File

@ -18,8 +18,8 @@ import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.assets.AssetsManager import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.config.profile.profiles.rendering.RenderingProfile import de.bixilon.minosoft.config.profile.profiles.rendering.RenderingProfile
import de.bixilon.minosoft.terminal.RunConfiguration import de.bixilon.minosoft.terminal.RunConfiguration
import de.bixilon.minosoft.util.DesktopUtil
import de.bixilon.minosoft.util.delegate.RenderingDelegate.observeRendering import de.bixilon.minosoft.util.delegate.RenderingDelegate.observeRendering
import de.bixilon.minosoft.util.system.SystemUtil
import de.matthiasmann.twl.utils.PNGDecoder import de.matthiasmann.twl.utils.PNGDecoder
import org.lwjgl.BufferUtils import org.lwjgl.BufferUtils
import java.nio.ByteBuffer import java.nio.ByteBuffer
@ -83,7 +83,7 @@ interface BaseWindow {
fun setDefaultIcon(assetsManager: AssetsManager) { fun setDefaultIcon(assetsManager: AssetsManager) {
val decoder = PNGDecoder(assetsManager[DesktopUtil.ICON]) val decoder = PNGDecoder(assetsManager[SystemUtil.ICON])
val data = BufferUtils.createByteBuffer(decoder.width * decoder.height * PNGDecoder.Format.RGBA.numComponents) val data = BufferUtils.createByteBuffer(decoder.width * decoder.height * PNGDecoder.Format.RGBA.numComponents)
decoder.decode(data, decoder.width * PNGDecoder.Format.RGBA.numComponents, PNGDecoder.Format.RGBA) decoder.decode(data, decoder.width * PNGDecoder.Format.RGBA.numComponents, PNGDecoder.Format.RGBA)
data.flip() data.flip()

View File

@ -415,7 +415,7 @@ class GLFWWindow(
override fun setIcon(size: Vec2i, buffer: ByteBuffer) { override fun setIcon(size: Vec2i, buffer: ByteBuffer) {
if (PlatformInfo.OS == OSTypes.MAC) { if (PlatformInfo.OS == OSTypes.MAC) {
return // the window icon can just be set with the TaskBar api. See DesktopUtil for more information return // the window icon can just be set with the TaskBar api. See SystemUtil for more information
} }
val images = GLFWImage.malloc(1) val images = GLFWImage.malloc(1)
val image = GLFWImage.malloc() val image = GLFWImage.malloc()

View File

@ -0,0 +1,47 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.system
import de.bixilon.minosoft.assets.IntegratedAssets
import java.awt.Taskbar
import java.awt.Toolkit
import java.io.File
import java.net.URL
open class DesktopAPI : SystemAPI {
init {
setTaskbarIcon()
}
override fun openFile(file: File) = Unit
override fun openURL(url: URL) = Unit
private fun Taskbar.setDockIcon() {
iconImage = Toolkit.getDefaultToolkit().createImage(IntegratedAssets.DEFAULT[SystemUtil.ICON].readAllBytes())
}
private fun Taskbar.initialize() {
if (isSupported(Taskbar.Feature.ICON_IMAGE)) {
setDockIcon()
}
}
private fun setTaskbarIcon() {
System.setProperty("java.awt.headless", false.toString())
if (Taskbar.isTaskbarSupported()) {
Taskbar.getTaskbar().initialize()
}
}
}

View File

@ -0,0 +1,23 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.system
import java.io.File
import java.net.URL
interface SystemAPI {
fun openURL(url: URL)
fun openFile(file: File)
}

View File

@ -0,0 +1,23 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.system
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
object SystemUtil {
val ICON = minosoft("icons/window_icon").texture()
var api: SystemAPI? = null
}