1+ package uno.awt
2+
3+ import glm_.vec2.Vec2i
4+ import org.lwjgl.glfw.GLFWErrorCallback
5+ import org.lwjgl.opengl.GLUtil
6+ import org.lwjgl.system.Callback
7+ import org.lwjgl.system.jawt.JAWTDrawingSurface
8+ import org.lwjgl.system.jawt.JAWTFunctions
9+ import uno.glfw.GlfwWindow
10+ import uno.glfw.HWND
11+ import uno.glfw.VSync
12+ import uno.glfw.glfw
13+ import java.awt.Canvas
14+ import java.awt.Graphics
15+ import java.awt.event.ComponentAdapter
16+ import java.awt.event.ComponentEvent
17+ import javax.swing.SwingUtilities
18+
19+ /* *
20+ * A Canvas component that uses OpenGL for rendering.
21+ *
22+ * Spasi: GLFW saves the AWT window proc somewhere and replaces it with its own on `glfwAttachWin32Window`.
23+ * It restores it back when the GLFW window is destroyed.
24+ * The difference between key and mouse events is that for key events the GLFW window proc returns `DefWindowProcW(...)`,
25+ * but for mouse events it returns 0, so the events do not propagate to AWT
26+ *
27+ * This implementation supports Windows only.
28+ */
29+ abstract class LwjglCanvas (val glDebug : Boolean = false ) : Canvas() {
30+
31+ val awt = JAWT ()
32+
33+ lateinit var glfwWindow: GlfwWindow
34+
35+ var swapBuffers = true
36+ var fps = true
37+
38+ var debugProc: Callback ? = null
39+
40+ var awtDebug = false
41+
42+ lateinit var glfwErrorCallback: GLFWErrorCallback
43+
44+ private fun initInternal (hwnd : HWND ) {
45+ // println("LwjglCanvas.initInternal ${Date().toInstant()}")
46+
47+ initialized = true
48+
49+ glfwErrorCallback = GLFWErrorCallback .createPrint().set()
50+
51+ glfw {
52+ init ()
53+ windowHint { debug = glDebug }
54+ }
55+
56+ // glfwWindowHint can be used here to configure the GL context
57+ glfwWindow = GlfwWindow .fromWin32Window(hwnd).apply {
58+ makeContextCurrent()
59+ createCapabilities(forwardCompatible = false )
60+ }
61+
62+ glfwWindow.cursorPosCallback = { it.toString() }
63+
64+ if (glDebug)
65+ debugProc = GLUtil .setupDebugMessageCallback()
66+
67+ glfw.swapInterval = VSync .OFF
68+
69+ init ()
70+
71+ glfwWindow.unmakeContextCurrent()
72+
73+ // println("/LwjglCanvas.initInternal ${Date().toInstant()}")
74+ }
75+
76+ var initialized = false
77+ var resized = false
78+ var animated = true
79+
80+ // lateinit var caps: Caps
81+
82+ // According to jawt.h > This value may be cached
83+ lateinit var surface: JAWTDrawingSurface
84+
85+ init {
86+
87+ if (! awt.get())
88+ throw IllegalStateException (" GetAWT failed" )
89+
90+ // this avoids to calling the super method
91+ this .addComponentListener(object : ComponentAdapter () {
92+ override fun componentResized (e : ComponentEvent ? ) {
93+ // println("resized")
94+ resized = true
95+ }
96+ // Not working
97+ // override fun componentHidden(e: ComponentEvent?) {
98+ // println("hidden")
99+ // }
100+ //
101+ // override fun componentMoved(e: ComponentEvent?) {
102+ // println("moved")
103+ // }
104+ //
105+ // override fun componentShown(e: ComponentEvent?) {
106+ // println("shown")
107+ // }
108+ })
109+
110+ SwingUtilities .invokeAndWait {
111+ surface = awt.getDrawingSurface(this )!!
112+ }
113+ }
114+
115+ /* * critical, call paint without default g.clearRect, because it causes flickering */
116+ override fun update (g : Graphics ) = paint(g)// .also { println("update") }
117+
118+ var last = 0L
119+ var time = 0L
120+ var frames = 0
121+
122+ override fun paint (g : Graphics ) {
123+
124+ if (awtDebug)
125+ println (" paint start " + Thread .currentThread().name)
126+
127+ // Lock the drawing surface
128+ if (surface.lock() == JawtLock .ERROR )
129+ throw IllegalStateException (" ds->Lock() failed" )
130+
131+ wrapped { hwnd ->
132+
133+ if (! initialized)
134+ initInternal(hwnd)
135+
136+ glfwWindow.inContext {
137+
138+ if (resized) {
139+ // println("LwjglCanvas.reshape ${Date().toInstant()}")
140+ val newSize = Vec2i (width, height)
141+ glfwWindow.size = newSize
142+ reshape(newSize)
143+ resized = false
144+ // println("/LwjglCanvas.reshape ${Date().toInstant()}")
145+ }
146+
147+ // println("LwjglCanvas.render ${Date().toInstant()}")
148+ if (awtDebug)
149+ println (" paint before render " )
150+
151+ render()
152+
153+ // println("/LwjglCanvas.render ${Date().toInstant()}")
154+ if (awtDebug)
155+ println (" paint end" )
156+
157+ if (swapBuffers)
158+ glfwWindow.swapBuffers()
159+
160+ if (fps) {
161+ val now = System .currentTimeMillis()
162+ time + = now - last
163+ last = now
164+ frames++
165+ if (time > 1000 ) {
166+ time % = 1000
167+ println (" fps = $frames " )
168+ frames = 0
169+ }
170+ }
171+ }
172+ }
173+
174+ if (animated)
175+ repaint()
176+ }
177+
178+ /* override fun paint(g: Graphics) {
179+ if (awtDebug) {
180+ println("paint start " + Thread.currentThread().name)
181+ }
182+
183+ // Lock the drawing surface
184+ val lock = JAWT_DrawingSurface_Lock(surface, surface.Lock())
185+ if (lock has JAWT_LOCK_ERROR)
186+ throw IllegalStateException("ds->Lock() failed")
187+
188+ try {
189+ // Get the drawing surface info
190+ val dsi = JAWT_DrawingSurface_GetDrawingSurfaceInfo(surface, surface.GetDrawingSurfaceInfo())
191+ ?: throw IllegalStateException("ds->GetDrawingSurfaceInfo() failed")
192+
193+ try {
194+ // Get the window platform drawing info
195+ val surfaceInfo = JAWTWin32DrawingSurfaceInfo.create(dsi.platformInfo())
196+
197+ val hdc = surfaceInfo.hdc()
198+ assert(hdc != NULL)
199+
200+ if (!initialized)
201+ initInternal(HWND(surfaceInfo.hwnd()))
202+
203+ glfwWindow.makeContextCurrent()
204+
205+ if (resized) {
206+ // println("LwjglCanvas.reshape ${Date().toInstant()}")
207+ val newSize = Vec2i(width, height)
208+ glfwWindow.size = newSize
209+ reshape(newSize)
210+ resized = false
211+ // println("/LwjglCanvas.reshape ${Date().toInstant()}")
212+ }
213+
214+ // println("LwjglCanvas.render ${Date().toInstant()}")
215+ if (awtDebug) {
216+ println("paint before render ")
217+ }
218+ render()
219+ // println("/LwjglCanvas.render ${Date().toInstant()}")
220+
221+ if (swapBuffers)
222+ glfwWindow.swapBuffers()
223+
224+ if (fps) {
225+ val now = System.currentTimeMillis()
226+ time += now - last
227+ last = now
228+ frames++
229+ if (time > 1000) {
230+ time %= 1000
231+ println("fps = $frames")
232+ frames = 0
233+ }
234+ }
235+
236+ glfwWindow.unmakeContextCurrent()
237+
238+ } finally {
239+ // Free the drawing surface info
240+ JAWT_DrawingSurface_FreeDrawingSurfaceInfo(dsi, surface.FreeDrawingSurfaceInfo())
241+ }
242+ } finally {
243+ // Unlock the drawing surface
244+ JAWT_DrawingSurface_Unlock(surface, surface.Unlock())
245+ }
246+
247+ if (awtDebug) {
248+ println("paint end")
249+ }
250+
251+ if (animated)
252+ repaint()
253+ }*/
254+
255+ inline fun wrapped (block : (HWND ) -> Unit ) {
256+
257+ try {
258+ // Get the drawing surface info
259+ val dsi = surface.info ? : throw IllegalStateException (" ds->GetDrawingSurfaceInfo() failed" )
260+
261+ try {
262+ // Get the window platform drawing info
263+ val surfaceInfo = JAWTWin32DrawingSurfaceInfo (dsi)
264+
265+ val hdc = surfaceInfo.hdc
266+ assert (hdc.isValid)
267+
268+ block(surfaceInfo.hwnd)
269+
270+ } finally {
271+ // Free the drawing surface info
272+ surface free dsi
273+ }
274+ } finally {
275+ // Unlock the drawing surface
276+ surface.unlock()
277+ }
278+ }
279+
280+ fun destroyInternal () {
281+ println (" destroyInternal" )
282+
283+ glfwWindow.makeContextCurrent()
284+
285+ destroy()
286+
287+ debugProc?.free()
288+
289+ glfwWindow.unmakeContextCurrent()
290+
291+ JAWTFunctions .JAWT_FreeDrawingSurface (surface, awt.FreeDrawingSurface ())
292+ awt.free()
293+
294+ glfwWindow.destroy()
295+ glfw.terminate()
296+ }
297+
298+ fun toggleAnimation () {
299+ if (animated)
300+ animated = false
301+ else {
302+ animated = true
303+ repaint()
304+ }
305+ }
306+
307+ // public methods to overwrite in application
308+
309+ abstract fun init ()
310+ abstract fun reshape (size : Vec2i )
311+ abstract fun render ()
312+ abstract fun destroy ()
313+ }
0 commit comments