forked from libgdx/gdx-liftoff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.kt
170 lines (160 loc) · 6.83 KB
/
main.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package gdx.liftoff
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Window
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3WindowListener
import com.badlogic.gdx.scenes.scene2d.utils.UIUtils
import com.badlogic.gdx.utils.GdxRuntimeException
import com.github.czyzby.autumn.context.ContextInitializer
import com.github.czyzby.autumn.fcs.scanner.DesktopClassScanner
import com.github.czyzby.autumn.mvc.application.AutumnApplication
import com.github.czyzby.autumn.nongwt.scanner.FallbackDesktopClassScanner
import com.kotcrab.vis.ui.util.OsUtils
import gdx.liftoff.config.Configuration
import gdx.liftoff.views.Extension
import gdx.liftoff.views.GdxPlatform
import gdx.liftoff.views.JvmLanguage
import gdx.liftoff.views.ProjectTemplate
import org.lwjgl.system.macosx.LibC
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.management.ManagementFactory
private const val JVM_RESTARTED_ARG = "jvmIsRestarted"
/**
* Calling this should be done in an if check; if this returns true, the old program should end because a new JVM will
* take over, but if it returns false, the program should continue normally. It is meant to allow MacOS to start with
* its required '-XstartOnFirstThread' argument, even if the jar wasn't originally started with it.
* Taken from https://github.com/crykn/guacamole/blob/master/gdx-desktop/src/main/java/de/damios/guacamole/gdx/StartOnFirstThreadHelper.java .
* Thanks crykn/damios!
*/
fun startNewJvmIfRequired(): Boolean {
if (!UIUtils.isMac) {
return false
}
val pid = LibC.getpid()
// check whether -XstartOnFirstThread is enabled
if ("1" == System.getenv("JAVA_STARTED_ON_FIRST_THREAD_$pid")) {
return false
}
// check whether the JVM was previously restarted
// avoids looping, but most certainly leads to a crash
if ("true" == System.getProperty(JVM_RESTARTED_ARG)) {
System.err.println(
"There was a problem evaluating whether the JVM was started with the -XstartOnFirstThread argument"
)
return false
}
// Restart the JVM with -XstartOnFirstThread
val jvmArgs = ArrayList<String>()
val separator = System.getProperty("file.separator")
jvmArgs.add(System.getProperty("java.home") + separator + "bin" + separator + "java")
jvmArgs.add("-XstartOnFirstThread")
jvmArgs.add("-D$JVM_RESTARTED_ARG=true")
jvmArgs.addAll(ManagementFactory.getRuntimeMXBean().inputArguments)
jvmArgs.add("-cp")
jvmArgs.add(System.getProperty("java.class.path"))
var mainClass = System.getenv("JAVA_MAIN_CLASS_$pid")
if (mainClass == null) {
val trace = Thread.currentThread().stackTrace
mainClass = if (trace.isNotEmpty()) {
trace[trace.size - 1].className
} else {
System.err.println("The main class could not be determined.")
return false
}
}
jvmArgs.add(mainClass!!)
try {
val process = ProcessBuilder(jvmArgs).redirectErrorStream(true).start()
val processOutput = BufferedReader(InputStreamReader(process.inputStream))
var line: String?
while (processOutput.readLine().also { line = it } != null) {
println(line)
}
process.waitFor()
} catch (e: Exception) {
System.err.println("There was a problem restarting the JVM")
e.printStackTrace()
}
return true
}
fun main() {
if (startNewJvmIfRequired()) return
val config = Lwjgl3ApplicationConfiguration()
config.setTitle("gdx-liftoff")
config.setWindowedMode(Configuration.WIDTH, Configuration.HEIGHT)
config.disableAudio(true)
// config.setDecorated(false)
config.setResizable(true)
config.setForegroundFPS(16)
config.setIdleFPS(8)
config.setAutoIconify(true)
config.setWindowIcon(*arrayOf(128, 64, 32, 16).map { "icons/libgdx$it.png" }.toTypedArray())
val windowListener: Lwjgl3WindowListener = object : Lwjgl3WindowListener {
override fun focusLost() { Gdx.graphics.isContinuousRendering = false }
override fun focusGained() { Gdx.graphics.isContinuousRendering = true }
override fun created(window: Lwjgl3Window) {}
override fun iconified(isIconified: Boolean) {}
override fun maximized(isMaximized: Boolean) {}
override fun closeRequested(): Boolean { return true }
override fun filesDropped(files: Array<String>) {}
override fun refreshRequested() {}
}
config.setWindowListener(windowListener)
try {
Lwjgl3Application(
object : AutumnApplication(DesktopClassScanner(), Root::class.java) {
override fun registerDefaultComponentAnnotations(initializer: ContextInitializer) {
super.registerDefaultComponentAnnotations(initializer)
// Classes with these annotations will be automatically scanned for and initiated as singletons:
initializer.scanFor(
Extension::class.java,
ProjectTemplate::class.java,
JvmLanguage::class.java,
GdxPlatform::class.java
)
}
},
config
)
} catch (error: ExceptionInInitializerError) {
if (OsUtils.isMac() && error.cause is IllegalStateException) {
if (error.stackTraceToString().contains("XstartOnFirstThread")) {
println(
"Application was not launched on first thread. " +
"Add VM argument -XstartOnFirstThread to avoid this."
)
}
}
throw error
} catch (error: GdxRuntimeException) {
Lwjgl3Application(
object : AutumnApplication(FallbackDesktopClassScanner(), Root::class.java) {
override fun registerDefaultComponentAnnotations(initializer: ContextInitializer) {
super.registerDefaultComponentAnnotations(initializer)
// Classes with these annotations will be automatically scanned for and initiated as singletons:
initializer.scanFor(
Extension::class.java,
ProjectTemplate::class.java,
JvmLanguage::class.java,
GdxPlatform::class.java
)
}
},
config
)
}
}
fun Throwable.stackTraceToString(): String {
val stringWriter = StringWriter()
val printWriter = PrintWriter(stringWriter, true)
printStackTrace(printWriter)
return stringWriter.buffer.toString()
}
/**
* Application's scanning root for Autumn dependency injection framework.
*/
class Root