TestAwtPanels: lwjgl2 vs lwjgl3 + Swing/AWT

Hello everyone,
I have a question. Is it correct that class TestAwtPanels only works with LWJGL2, and with LWJGL3 it returns the following error message?

nov 14, 2024 7:36:47 PM com.jme3.system.JmeDesktopSystem initialize
INFO: Running on jMonkeyEngine 3.7.0-stable
 * Branch: HEAD
 * Git Hash: bc6cdf5
 * Build Date: 2024-10-21
nov 14, 2024 7:36:50 PM com.jme3.system.lwjgl.LwjglContext printContextInitInfo
INFO: LWJGL 3.3.2+13 context running on thread jME3 Main
 * Graphics Adapter: GLFW 3.4.0 Win32 WGL Null EGL OSMesa VisualC DLL
nov 14, 2024 7:36:50 PM com.jme3.system.lwjgl.LwjglCanvas printContextInitInfo
INFO: Initializing LWJGL3-AWT with jMonkeyEngine
 *  Double Buffer: true
 *  Stereo: false
 *  Red Size: 8
 *  Rreen Size: 8
 *  Blue Size: 8
 *  Alpha Size: 0
 *  Depth Size: 24
 *  Stencil Size: 0
 *  Accum Red Size: 0
 *  Accum Green Size: 0
 *  Accum Blue Size: 0
 *  Accum Alpha Size: 0
 *  Sample Buffers: 0
 *  Share Context: null
 *  Major Version: 3
 *  Minor Version: 2
 *  Forward Compatible: false
 *  Profile: COMPATIBILITY
 *  API: GL
 *  Debug: false
 *  Swap Interval: 1
 *  SRGB (Gamma Correction): true
 *  Pixel Format Float: false
 *  Context Release Behavior: null
 *  Color Samples NV: 0
 *  Swap Group NV: 0
 *  Swap Barrier NV: 0
 *  Robustness: false
 *  Lose Context On Reset: false
 *  Context Reset Isolation: false
nov 14, 2024 7:36:50 PM com.jme3.renderer.opengl.GLRenderer loadCapabilitiesCommon
INFO: OpenGL Renderer Information
 * Vendor: Intel
 * Renderer: Intel(R) Iris(R) Xe Graphics
 * OpenGL Version: 3.2.0 - Build 31.0.101.4032
 * GLSL Version: 1.50 - Build 31.0.101.4032
 * Profile: Core
nov 14, 2024 7:36:50 PM com.jme3.renderer.opengl.GLRenderer setMainFrameBufferSrgb
WARNING: Driver claims that default framebuffer is not sRGB capable. Enabling anyway.
nov 14, 2024 7:36:51 PM com.jme3.audio.openal.ALAudioRenderer initOpenAL
INFO: Audio Renderer Information
 * Device: OpenAL Soft
 * Vendor: OpenAL Community
 * Renderer: OpenAL Soft
 * Version: 1.1 ALSOFT 1.23.1
 * Supported channels: 64
 * ALC extensions: ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX ALC_EXT_thread_local_context ALC_SOFT_device_clock ALC_SOFT_HRTF ALC_SOFT_loopback ALC_SOFT_loopback_bformat ALC_SOFT_output_limiter ALC_SOFT_output_mode ALC_SOFT_pause_device ALC_SOFT_reopen_device
 * AL extensions: AL_EXT_ALAW AL_EXT_BFORMAT AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_MULAW AL_EXT_MULAW_BFORMAT AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model AL_EXT_SOURCE_RADIUS AL_EXT_STATIC_BUFFER AL_EXT_STEREO_ANGLES AL_LOKI_quadriphonic AL_SOFT_bformat_ex AL_SOFTX_bformat_hoa AL_SOFT_block_alignment AL_SOFT_buffer_length_query AL_SOFT_callback_buffer AL_SOFTX_convolution_reverb AL_SOFT_deferred_updates AL_SOFT_direct_channels AL_SOFT_direct_channels_remix AL_SOFT_effect_target AL_SOFT_events AL_SOFT_gain_clamp_ex AL_SOFTX_hold_on_disconnect AL_SOFT_loop_points AL_SOFTX_map_buffer AL_SOFT_MSADPCM AL_SOFT_source_latency AL_SOFT_source_length AL_SOFT_source_resampler AL_SOFT_source_spatialize AL_SOFT_source_start_delay AL_SOFT_UHJ AL_SOFT_UHJ_ex
nov 14, 2024 7:36:51 PM com.jme3.audio.openal.ALAudioRenderer initOpenAL
INFO: Audio effect extension version: 1.0
nov 14, 2024 7:36:51 PM com.jme3.audio.openal.ALAudioRenderer initOpenAL
INFO: Audio max auxiliary sends: 2
nov 14, 2024 7:36:53 PM com.jme3.app.LegacyApplication handleError
SEVERE: Exception while creating the OpenGL context
java.awt.AWTException: sRGB color space requested but WGL_EXT_framebuffer_sRGB is unavailable
	at org.lwjgl.opengl.awt.PlatformWin32GLCanvas.create(PlatformWin32GLCanvas.java:438)
	at org.lwjgl.opengl.awt.PlatformWin32GLCanvas.create(PlatformWin32GLCanvas.java:156)
	at com.jme3.system.lwjgl.LwjglCanvas$LwjglAWTGLCanvas.beforeRender(LwjglCanvas.java:248)
	at com.jme3.system.lwjgl.LwjglCanvas.runLoop(LwjglCanvas.java:623)
	at com.jme3.system.lwjgl.LwjglWindow.run(LwjglWindow.java:719)
	at java.base/java.lang.Thread.run(Thread.java:834)

nov 14, 2024 7:37:02 PM com.jme3.system.lwjgl.LwjglCanvas$LwjglAWTGLCanvas removeNotify
WARNING: Windows does not support this functionality: remove(__canvas__)

lwjgl2 supports Swing/AWT.

lwjgl3 does not support Swing/AWT… it’s a limitation of glfw or whatever.

…and one of the things that keeps me on lwjgl2.

2 Likes

In LWJGL 3 we are using LWJGL3-AWT to provide the support. It works kinda. You can just switch off the gamma to get over this. I don’t know if there is something more deeper to this.

 *  SRGB (Gamma Correction): true
WARNING: Driver claims that default framebuffer is not sRGB capable. Enabling anyway.
3 Likes

Thanks for your answers guys. I have a couple more doubts.


  1. The TestSafeCanvas class centers the JME Canvas within the JFrame, but it doesn’t expand to fill the entire window. What could be causing this behavior?

* Here is a code snippet from the TestSafeCanvas class (see the original file for more details):

        final TestSafeCanvas app = new TestSafeCanvas();
        AppSettings settings = new AppSettings(true);
        settings.setWidth(640);
        settings.setHeight(480);
   
        app.setSettings(settings);
        app.setPauseOnLostFocus(false);
        app.createCanvas();
        app.startCanvas(true);

        JmeCanvasContext context = (JmeCanvasContext) app.getContext();
        Canvas canvas = context.getCanvas();
        canvas.setSize(settings.getWidth(), settings.getHeight());

        ...
        // In this case the JME Canvas is not centered in the JFrame
        frame.getContentPane().add(canvas);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

  1. What are the advantages and disadvantages of using TestSafeCanvas and TestAwtPanels with AwtPanelsContext as a custom renderer? Which approach offers better performance and flexibility?

* Here is a code snippet from the TestAwtPanels class (see the original file for more details):

        TestAwtPanels app = new TestAwtPanels();
        
        AppSettings settings = new AppSettings(true);
        settings.setCustomRenderer(AwtPanelsContext.class);
        settings.setFrameRate(60);
        app.setSettings(settings);
        app.setShowSettings(false);
        app.start();

        final AwtPanelsContext ctx = (AwtPanelsContext) app.getContext();
        AwtPanel panel = ctx.createPanel(PaintMode.Accelerated);
        panel.setPreferredSize(new Dimension(400, 300));
        ctx.setInputSource(panel);

        ...
        // In this case the JME Canvas is centered in the JFrame
        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(panel, BorderLayout.CENTER);
        frame.pack();
        frame.setLocation(null);
        frame.setVisible(true);

Last year, I was working on a lightweight framework that integrates JME, Swing and Jfx all in one environment (The Hybrid Jme Project), I was facing similar issues, and I overcame it by adding the canvas to a JFxPanel (which is a JPanel with a support for JFx components), before adding it to the JFrame (so, you will attach the JPanel to the JFrame at the end)… I literally cannot remember what was the exact issue, but I think it’s something with the Swing Canvas dimensions that cannot be redrawn once created, only the canvas components could be updated so far, but I cannot remember the exact issue, I might be wrong, but this approach works so far…

EDIT:
In your case, if you aren’t using JavaFx, then JPanel should work just fine.

I’m using Java Swing, not JavaFX. Regarding the TestSafeCanvas class, I have already tried this solution and it does not work.

Hi @capdevon

The TestSafeCanvas class centers the JME Canvas within the JFrame, but it doesn’t expand to fill the entire window

It’s strange that the TestSafeCanvas class doesn’t work correctly (at least I don’t have problems with it), are you running this example directly from jme3? (from the screenshot I think not; correct me if I’m wrong)

Note

This particular class breaks when you remove and re-add the canvas in the component (Windows)

What could be causing this behavior?

Oddly enough, apparently when you embed a GL canvas in a component that does not have a BorderLayout layout (or another that does not trigger a listener that notifies such changes when resizing said component) it will not resize the drawing area . especially if we use GroupLayout.

You can create a component (JPanel or a custom one) with a BorderLayout layout and only containing the jme3 canvas (add the canvas in the center of the component).

AppSettings settings = new AppSettings(true);
settings.setWidth(640);
settings.setHeight(480);

MyAppCanvas app = new MyAppCanvas();
app.setPauseOnLostFocus(false);
app.setSettings(settings);
app.createCanvas();
app.startCanvas(true);

JmeCanvasContext context = (JmeCanvasContext) app.getContext();
Canvas canvas = context.getCanvas();
canvas.setPreferredSize(new Dimension(settings.getWidth(), settings.getHeight()));

JPanel myRootPane = new JPanel();
myRootPane.setLayout(new BorderLayout());
myRootPane.add(canvas, BorderLayout.CENTER);

JFrame frame = new JFrame("JME3 - AWT/Swing Canvas");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
	@Override
	public void windowClosing(WindowEvent e) {
		app.stop();
	}
});
frame.getContentPane().add(myRootPane);
frame.pack();

frame.setLocationRelativeTo(null);
frame.setVisible(true);

Hi @SwiftWolf ,

Have you had a chance to run your code? Does the Canvas center correctly in the Frame as expected?

I’ve tried your BorderLayout suggestion with lwjgl2, but the Canvas still isn’t centering. Any other ideas?

Here is the test class. Can you run it on your computer and tell me what result you get?

Thank you

package jme3test.awt;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TestSafeCanvas extends SimpleApplication {

    public static void main(String[] args) {
        AppSettings settings = new AppSettings(true);
        settings.setResolution(640, 480);

        final TestSafeCanvas app = new TestSafeCanvas();
        app.setPauseOnLostFocus(false);
        app.setSettings(settings);
        app.createCanvas();
        app.startCanvas(true);

        JmeCanvasContext context = (JmeCanvasContext) app.getContext();
        Canvas canvas = context.getCanvas();
        canvas.setPreferredSize(new Dimension(settings.getWidth(), settings.getHeight()));

        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }

        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                app.stop();
            }
        });
        
        JPanel container = new JPanel();
        container.setLayout(new BorderLayout());
        container.add(canvas, BorderLayout.CENTER);

        frame.getContentPane().add(container);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    @Override
    public void simpleInitApp() {
        flyCam.setDragToRotate(true);

        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
        geom.setMaterial(mat);
        rootNode.attachChild(geom);
    }
}

Is it normal to share code without testing it :thinking:?

On both Linux and Windows I have no problems centering it.

First I would like to ask you a few things.

  1. Are you using lwjgl2?
  2. Do you have the same problem with lwjgl2 or does it only happen with lwjgl3?

If you are with lwjgl3, can you try resizing the canvas manually and check that the java.awt.event.ComponentAdapter#componentResized(ComponentEvent e) listener fires correctly?

I have no problems on both systems, I can run it without problems (Copying the code you provided).

[ Windows 11 ]


[ Linux ]

Hi @SwiftWolf ,
I think I found the source of the confusion around the test class results. It seems the behavior depends on whether we’re using LWJGL 2 or 3:

  • LWJGL 2:
    • The Canvas isn’t centered in the Frame, regardless of using a BorderLayout.CENTER Panel or manual resizing.
    • Mouse behavior: Click/release with flyCam.setDragToRotate(true) doesn’t re-center the cursor during camera rotation.
  • LWJGL 3:
    • We need to set settings.setGammaCorrection(false) to avoid program crashes.
    • The Canvas is centered by default (no need for BorderLayout.CENTER Panel).
    • Mouse behavior: Click/release with flyCam.setDragToRotate(true) re-centers the cursor for camera rotation.

This difference in behavior explains why some configurations work in one and not the other.

Note: The mouse problem is caused by the different behavior of the libraries when FlyByCamera enables/disables cursor visibility. This behavior is currently being discussed in an open issue here.


1. Here is code and screenshots of the test with lwjgl2

(run with jme3.7.0-stable, Windows 11):

public class Test_SafeCanvas extends SimpleApplication {

    public static void main(String[] args) {
        AppSettings settings = new AppSettings(true);
        settings.setResolution(640, 480);

        final Test_SafeCanvas app = new Test_SafeCanvas();
        app.setPauseOnLostFocus(false);
        app.setSettings(settings);
        app.createCanvas();
        app.startCanvas(true);

        JmeCanvasContext context = (JmeCanvasContext) app.getContext();
        Canvas canvas = context.getCanvas();
        canvas.setPreferredSize(new Dimension(settings.getWidth(), settings.getHeight()));

        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }

        JFrame frame = new JFrame("Test - LWJGL2");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                app.stop();
            }
        });
        
        JPanel container = new JPanel();
        container.setLayout(new BorderLayout());
        container.add(canvas, BorderLayout.CENTER);

        frame.getContentPane().add(container);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    @Override
    public void simpleInitApp() {
        flyCam.setDragToRotate(true);

        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
        geom.setMaterial(mat);
        rootNode.attachChild(geom);
    }

}


2. Here is code and screenshots of the test with lwjgl3

(run with jme3.7.0-stable, Windows 11):

public class Test_SafeCanvas extends SimpleApplication {

    public static void main(String[] args) {
        AppSettings settings = new AppSettings(true);
        settings.setGammaCorrection(false); /* for lwjgl3 */
        settings.setResolution(640, 480);

        final Test_SafeCanvas app = new Test_SafeCanvas();
        app.setPauseOnLostFocus(false);
        app.setSettings(settings);
        app.createCanvas();
        app.startCanvas(true);

        JmeCanvasContext context = (JmeCanvasContext) app.getContext();
        Canvas canvas = context.getCanvas();
        canvas.setPreferredSize(new Dimension(settings.getWidth(), settings.getHeight()));

        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }

        JFrame frame = new JFrame("Test - LWJGL3");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                app.stop();
            }
        });
        
        // This panel is not necessary-to center the Canvas
//        JPanel container = new JPanel();
//        container.setLayout(new BorderLayout());
//        container.add(canvas, BorderLayout.CENTER);

        frame.getContentPane().add(canvas);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    @Override
    public void simpleInitApp() {
        flyCam.setDragToRotate(true);
        flyCam.setMoveSpeed(10f);
        viewPort.setBackgroundColor(ColorRGBA.DarkGray);

        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
        geom.setMaterial(mat);
        rootNode.attachChild(geom);
    }

}

Edit:

It remains to clarify my second doubt. Any suggestions?

2 Likes